不知不觉也来到最後一篇啦!
在 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);
}
以上就是所有制作过程!谢谢收看。
<<: 番外篇(2)一起来做 To Do List!- 实作篇(2)
今天要来介绍一下如何 set up TypeScript! 请先到 TypeScript 的官网然後...
前言 JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用...
Object content Array 是一种 Object,所以我们也能够用 key valu...
闲话家常 前面几天都是枯燥乏味的设定,也是很重要的。要有一个稳定的环境,才能够专心在Coding。 ...
现在不管是学校课程规划或是同学主动想要了解职场,对於实习其实是一个可以看清自己的能力跟业界之间的差距...