不知不覺也來到最後一篇啦!
在 codepen 上可以看到一些酷炫的漢堡選單 code ,但這裡我做的是最陽春的版本:點擊漢堡選單 icon 秀出,點擊選單上的 x 關閉。
const hamburgerMenu = document.querySelector('#hamburger-menu-icon');
const hamburgerExit = document.querySelector('#hamburger-menu-exit');
hamburgerMenu.addEventListener('click',openMenu);
hamburgerExit.addEventListener('click',closeMenu);
function openMenu(){
let menu = document.getElementById('mobile-filter');
menu.classList.add('hamburgerMenu-active');
}
function closeMenu(){
hamburgerExit.classList.toggle('hamburgerMenuExit-active');
hamburgerExit.addEventListener('animationend',function(){
let menu = document.getElementById('mobile-filter');
menu.classList.remove('hamburgerMenu-active');
});
}
做完選單,來處理選單上放的東西。篩選器的下拉式選單可以用 <select> <option>
處理,並記得在 <option>
中放置 value 值,這樣等會處理 js 才有辦法讓電腦判別不同值。
我的手機版和電腦版分兩塊去做,在宣告時我使用電腦版和手機版的 class 名稱,搭配 querySelectorAll ,因此會抓到不只一個值,把資料以陣列方式儲存。為此在監聽時必須用 forEach ,不然會跳錯誤訊息。
此外在事件名稱的部分,如果使用 click ,會變成一點擊 <select>
,電腦就紀錄一開始在 <select>
的項目。導致後來不管你選哪個 <option>
,都沒有被記錄到。把事件換成 change 的話,則能避面上述情形。
filterStatus.forEach(function(i){
i.addEventListener('change',showFilterStatus);
});
filterDate.forEach(function(i){
i.addEventListener('change',showFilterDate);
});
filterSort.forEach(function(i){
i.addEventListener('change',showFilterSort);
});
完成監聽後,拆解任務如下:
<option>
做判斷善用 console.log 能發現 todoList.childNodes 正是我們需要的 todo 項目集合,用 todos 來稱呼它。 e.target 是點擊的 <option>
項目,所以要取它的值自然是用 .value 囉!這邊使用 switch 判斷式,裡面再用 if 判斷式做二次判斷。可以這樣理解:
用 switch 判斷 <option>
選了什麼,用 if 判斷接下來該顯示什麼?
如果不確定我下面寫的 todo 是什麼? sort 是什麼?建議使用 console.log 查看一下喔!
function showFilterStatus(e){
const todos = todoList.childNodes;
todos.forEach(function(todo){
switch(e.target.value){
case "all":
todo.style.display = 'flex';
break;
case "completed": //
if(todo.classList.contains('completed')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "uncompleted":
if(!todo.classList.contains('completed')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
}
});
}
function showFilterDate(e){
const todos = todoList.childNodes;
todos.forEach(function(todo){
//console.log(todo.childNodes[0].childNodes[0]); 從這句找到月份所在 dom 位置
const month = todo.childNodes[0].childNodes[0].innerHTML;
switch(e.target.value){
case "allMonth":
todo.style.display = 'flex'; //全都秀
break;
case "jan":
if (month.includes('01/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "feb":
if (month.includes('02/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "mar":
if (month.includes('03/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "apr":
if (month.includes('04/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "may":
if (month.includes('05/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "jun":
if (month.includes('06/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "jul":
if (month.includes('07/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "aug":
if (month.includes('08/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "sep":
if (month.includes('09/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "oct":
if (month.includes('10/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "nov":
if (month.includes('11/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "dec":
if (month.includes('12/')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
}
});
}
function showFilterSort(e){
const todos = todoList.childNodes;
todos.forEach(function(todo){
const sort = todo.childNodes[0].childNodes[2].childNodes[0];
switch(e.target.value){
case "allSort":
todo.style.display = 'flex';
break;
case "jobSort":
if (sort.classList.contains('fa-briefcase')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "houseworkSort":
if (sort.classList.contains('fa-home')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "sportSort":
if (sort.classList.contains('fa-futbol')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "routineSort":
if(sort.classList.contains('fa-hourglass')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
case "othersSort":
if(sort.classList.contains('fa-palette')){
todo.style.display = 'flex';
} else {
todo.style.display = 'none';
}
break;
}
});
}
最後的最後要來做個分析按鈕,按下去會跳出視窗,顯示種類的圓餅圖。 bootstrap 就有 modal 可以使用,但既然都要練習了...
因為是要點擊按鈕就彈視窗並跳出分析,所以我直接放在同一個函式中處理。在 modal 下層插入一個背景,讓下面的畫面不會太花,也避免使用者在手機版時,誤會關閉漢堡選單的 x 是關閉 modal 的 x 。
const container = document.querySelector('.container');
const analyzeBtn = document.querySelectorAll('.analyze-btn');
const modal = document.querySelector('.modal');
const modalExit = document.querySelector('.modal-exit');
analyzeBtn.forEach(function(i){
i.addEventListener('click',analyzeSort);
});
modalExit.addEventListener('click',closeModal);
function analyzeSort(){
modal.classList.toggle("modal-active");
const modalBackground = document.createElement('div');
modalBackground.classList.add("modal-background");
container.appendChild(modalBackground);
然後要將 JSON 抓到的資料轉成 d3.js 圖表、放進 modal 中。從本地端抓 todos 這個 key ,若是空的,顯示暫無待辦事項。否則,把抓到的種類放到 todoSortArray 陣列中。
let todos;
todos = JSON.parse(localStorage.getItem('todos'));
if(todos.length == 0){
const modalContent = document.querySelector('.modal-content');
modalContent.innerHTML = '暫無待辦事項';
}else{
let todoSortArray = [];
todos.forEach(function(todo){
todoSortArray.push(todo[2]);
})
問題是,抓到了全部的代辦事項種類後,我要怎樣讓電腦計算每種種類有幾個呢?估狗後我在 stackoverflow 上找到解答:用 for 迴圈搭配物件比較去做。
宣告 i=0 ,當 i 小於 todoSortArray.length 就跑下面的函式,跑完 i+1 ,直到它等於 todoSortArray.length 為止。然後宣告 num 就是 todoSortArray[i] ,也就是說 num 會等於 todoSortArray[0]、todoSortArray[1]...而 todoSortArray[0] 、 todoSortArray[1] 是什麼呢?正是我們剛剛抓的 job 、 choose 等等種類名稱。看要比較哪個字,就在 num 的位置輸入,會比較 counts 物件,如相同則該物件存值 +1 ,如不同則存值設為 1 。
let counts = {};
for(let i=0;i<todoSortArray.length;i++){
let num = todoSortArray[i];
counts[num] = counts[num]?counts[num]+1:1;
}
最後就很簡單了,把要比較的值代入,等它算好請它同步轉成數字,再畫成圓餅圖即可。
let jobNum = parseInt(counts["job"]);
let houseworkNum = parseInt(counts["housework"]);
let sportNum = parseInt(counts["sport"]);
let routineNum = parseInt(counts["routine"]);
let othersNum = parseInt(counts["others"]);
let chart = c3.generate({
bindto: '.modal-body',
data:{
columns:[
['工作',jobNum],
['家事',houseworkNum],
['運動',sportNum],
['例行公事',routineNum],
['其他',othersNum],
],
type:'pie', //圖的種類是圓餅圖
onclick:function(d,i){ //點擊圖時的效果
console.log("onclick",d,i);
},
onmouseover:function(d,i){ //滑鼠滑進圖的效果
console.log("onmouseover",d,i);
},
onmouseout:function(d,i){ //滑鼠滑出圖的效果
console.log("onmouseout",d,i);
}
}
});
}
}
別忘了在 modal 加離開鍵函式。
function closeModal(){
modal.classList.remove('modal-active');
const modalBackground = document.querySelector('.modal-background');
container.removeChild(modalBackground);
}
以上就是所有製作過程!謝謝收看。