百度前端学院春季任务三笔记
----Ljm 2015-11-20
任务要求:https://github.com/baidu-ife/tree/master/2015_spring/task/task0003
代码:https://github.com/newql/baidu-ife/tree/master/task3_spring
个人管理系统功能:
1.左侧为分类列表,中间为当前分类下的任务列表,右侧为任务详情。
2.左侧可以添加、删除分类;中间可以添加、删除任务;右侧可以编辑任务和标记任务完成状态。
3.不允许给默认主分类添加子分类;不允许给默认子分类添加任务;括号里的数字代表其中包含的未完成任务的总数。
分析思路:
一、根据功能分析搭建整个html和css的静态页面结构
其中需要注意一下几点:左侧部分主分类和子分类整体放在一个ul中,并且用h3标记主分类,用h4标记子分类便于后面写js;中间部分任务时间和任务标题整体放在一个ul中,并且用h5标记任务时间,用h6标记任务标题便于后面写js;右侧部分包含两个div即展示任务的div和编辑任务的div;还有一个添加主子分类的隐藏块cover。
二、开始写js部分
这一部分我一开始是瞎写的,各个部分糅合在一起,想到什么就写什么,最后发现好多bug而且根本找不到bug在哪里,所以在写之前最好构思好先写什么,写好了用各种情况去测试,如果没有问题了,再接着写下一部分。这样容易检查错误并且思路清晰。
接下来是思路:
2.1、构思左侧页面js
左侧主要包含主分类、子分类另外还有一个所有任务的点击项。这里面主要包含点击、增加主子分类、删除主子分类以及改变未完成任务数这几部分。
1、初始化数据库initDatebase()
数据库的设计即主分类对象中包含哪些属性(id、name、child);子分类对象中包含哪些属性(id、pid、child、name)以及任务对象中包含哪些属性(id、 pid、child、name、content、finish)。数据库中使用了JSON数据和字符串之间的相互转换并且还用到了localStorage本地存储,方便存储数据。
2、初始化函数initCates()
在写初始化函数时根据要用到什么功能的函数去声明什么函数,这样一步步把初始化函数写完。
这一部分包含的函数详见index.js代码中。
介绍几个特别的函数:
getObjByKey(obj,key,value)根据对象obj进行循环查找其属性key等于value的那个对象
变量EventUtil,兼容所有浏览器的添加事件监听
getIndexByKey(obj,key,value)根据对象obj进行循环查找其属性key等于value的下标index
在进行初始化cateList部分innerHTML时注意几点:每个主分类h3上添加了属性cateid;每个子分类h4上添加了属性childcateid;每个主子分类name被 放在一个span标签中。这样做是为了方便后面获取到对应的h元素是可以直接得到对象的id值从而获得相应的数据库中的对象。
初始化函数里加入了oldChoose这个变量,作用是为了让这个函数每次被调用的时候都记得之前选中的是哪个主子分类
3、左边的功能函数
主要分为点击函数(所有任务点击监听、主分类点击监听、子分类点击监听);增加主子分类监听;主分类的删除监听、子分类的删除监听。
每一部分写完测试再写下一部分再测试。
其中需要注意的是在增加主子分类时下拉菜单的每一项即option上设置了value属性其值如果是-1代表的是增加主分类;如果其值为其他数字代表的是增加 子分类再对应主分类的id上。
对于功能函数举个实例:例如删除子分类其中包含要删除对应的子分类以及下面的所有task(因为还没有写task所以先把这部分空着),还要改变这个子 分类所在主分类的属性child数组的值。这时候需要特别注意的是改变了child以后要把这个主分类对象赋值给cate中对应的对象再进行保存数据,否则主分类对 象根本没进行改变会导致很多错误。
2.2、构思中间页面的js
中间主要包括时间排序显示任务、任务点击、任务添加、任务删除以及status改变对应改变taskList的innerHTML。
初始化函数initTasks(status)注意其中有传入参数status,其作用是判断status到底是选中所有、未完成还是已完成,选择不同taskList的显示不同。
在初始化时任务时间包裹在h5标签中,任务标题包含在h6标签中,并且h6上面增加了属性taskid以便后面获取到对应的任务对象。
初始化中也包含了对oldChoose即记住原来选中的哪个task以便于后面显示。
2.3、左侧与中间连接
左边和右边的各自功能都实现以后将两边的功能连接起来。比如点击左边显示对应的任务;增加或者删除任务是改变左边的关于未完成任务的数字;以及前面的删除 主分类和子分类中的删除任务部分的补充。测试这两个联合在一起的功能。
2.4、右侧展示和编辑界面的js
初始化展示task的标题、时间和内容,其中注意未完成任务和已完成任务的区别。展示中间对应选中的task;编辑右侧未完成task;标记右侧未完成task为已完成。
编辑右侧任务时需要注意和前面添加任务时的确定按钮这两种情况的函数调用时不同的。方法是更改了这个button的id然后对button所在的div添加监听事件。
三、结束
到这里基本就结束。
下面介绍我在写的过程中遇到的各种小问题:
问题以及解决方法
1、出现如下图所示的问题
原因是跟box-sizing有关。
解决办法:使用以下语句
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
-ms-box-sizing:border-box;
-o-box-sizing:border-box;
box-sizing:border-box;
2、在点击主分类和子分类时出现如下图所示的情况
原因:比如默认主分类的li中含有h3、i标签、span标签,点击的不一定是h3元素,所以会出现上述情况,所以我们应该使用如下语句保证在h3上添加上className为choose
解决办法:
if(target.tagName.toLowerCase()=='h2')
{
target.className+='choose';
}
elseif(target.parentNode.tagName.toLowerCase()=='h2')
{
target.parentNode.className+='choose';
}
3.使用如下语句时出现如下图情况
.fa-minus-circle{
display: none;
/* float: right; */
position: absolute;
right: 20px;
color: #f44336;
font-size: 18px;
line-height: 1.5;
}
原因:使用绝对定位是当点击元素不同时可能会导致一些问题
解决办法:将绝对定位改为使用浮动即右浮动
.fa-minus-circle{
display: none;
float: right;
/* position: absolute;
right: 20px; */
color: #f44336;
font-size: 18px;
line-height: 1.5;
}
4、每次添加或者删除后,点击函数不起作用,使用代码如下:
for(vari=0,l=document.querySelectorAll("#cateList h3").length;i<l;i++)
{
EventUtil.addHandler(document.querySelectorAll("#cateListh3")[i],"click",cateClick);//点击主分类即h3的事件
}
for(var j=0,c=document.querySelectorAll("#cateListh4").length;j<c;j++)
{
EventUtil.addHandler(document.querySelectorAll("#cateListh4")[j],"click",childCateClick);//点击子分类即h4的事件
}
问题:通过上述给所有的子分类和主分类添加监听事件是不可行的。因为主分类和子分类的元素在添加和删除的过程中已经改变,所以要更改添加事件监听的方法。
解决办法:可以查看红宝书的第十三章关于事件的讲述。可以给整个大块即div添加事件这样就不需要每个元素都绑定事件。使用如下代码:
//给左边界面中的任务列表中的h3 h4 以及删除操作添加监听函数
EventUtil.addHandler(document.querySelector('#cateList'),'click',function(event)
{
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
taskEditHide();
if(target.className=='fa fa-minus-circle'&&target.parentNode.tagName.toLowerCase()=='h3')//点击的是主分类的删除
{
deleteCate(event);
}
else if(target.className=='fafa-minus-circle'&&target.parentNode.tagName.toLowerCase()=='h4')
//点击的是子分类的删除
{
deleteChildCate(event);
}
elseif(target.tagName.toLowerCase()=='h3' ||target.parentNode.tagName.toLowerCase()=='h3')//为h3添加点击事件
{
cateClick(target);
}
elseif(target.tagName.toLowerCase()=='h4' || target.parentNode.tagName.toLowerCase()=='h4')//为h4添加点击事件
{
childCateClick(target);
}
});
5、下面出现竖直的scroll;因为catelist高度设置为100%
解决办法:更改将height:100%去掉
6、经常使用cate[cateid]、childcate[childcateid]这样去获取对应的主子分类对象是完全错误的。因为当我们删除了之后那么每个对象的id与其在数组中下标index就不是相等的关系,需要特别注意。
7、在增加或者删除操作时需要改变父对象的child数组应该如何更新对应的父对象所在的所有对象,需要特别注意。
举例:
//代表是需要增加子分类
{
varcurCate=queryCateById(selectVal);
var newChildCate={
'id':childCate[childCate.length-1].id+1,
'name':cateName,
'child':[],
'pid':curCate.id
}
curCate.child.push(newChildCate.id);//更改父节点的child数组中的值
childCate.push(newChildCate);//更改子分类数组
//改变cate中那个刚才被更改child的对象
for(vari=0,l=cate.length;i<l;i++)
{
if(cate[i].id===curCate.id)
{
cate[i]=curCate;
break;
}
}
localStorage.cate=JSON.stringify(cate);
localStorage.childCate=JSON.stringify(childCate);
}
8、类型问题
比如:vartaskid=ele.getAttribute('taskid');
var taskobj=queryTaskById(parseInt(taskid));
这两句中第一句获取到taskid,这个taskid是string类型,但是第二句需要传入的参数是int型,所以不能直接传入taskid。
9、新建主子分类时主子分类名称如果已经存在需要提示并且不可以新建
10、新建了任务以后,如何让新建的任务被选中并且右边界面展示刚刚添加的任务信息。
解决办法:
//将任务列表中的刚才新建的任务更改为选中即choose状态
varh6Elements=document.getElementsByTagName('h6');
for(vari=0,l=h6Elements.length;i<l;i++)
{
var span=h6Elements[i].getElementsByTagName('span')[0].innerHTML;//获取h6元素的名称即任务名称
clearAllTaskHighLight();
if(span==inTitle)
{
h6Elements[i].className='choose';//为刚才新建的任务添加上choose
break;
}
}
document.querySelector("#inputTitle").value="";
document.querySelector("#inputDate").value="";
document.querySelector("#inputContent").value="";
taskDisplay();//右侧对应展示任务内容
11、新建task或者新建主子分类以后都要将cover界面和edit界面的相应input内容清空。
12、删除操作有问题
举例:deletechildcate函数有问题,不管点击删除哪个子分类,最后都会删除该子分类所在主分类下的最后一个子分类。
原因:使用了childcate[childcateid]或者cate[cateid]导致的错误,因为childcateid的对象在childcate中的下标index与childcateid不相符。
解决办法:用queryChildCateById(childcateid)方法去获得对应的子分类对象
13、在很多需要获得下标index的时候不要使用indexOf函数,这个函数有很多问题。(有可能也是类型有问题,比如【1,2,4】而传入的是’2’,那么自然返回-1,找不到正确的index。
14、初始化任务函数时,对于oldchoose的记忆需要注意判断条件要用id是否相等去判断,不要用name。
原因:在编辑任务时有可能会更改任务的标题,但是不会改变任务的id。
15、在任务增加或者删除以前,有选中的相应的status,但是在增加或者删除任务以后一定要将status恢复到所有上去,可以调用函数statusAll().否则会出现中间任务列表部分显示错误。
16、在标记任务为已完成、点击主子分类或者所有任务之前,不管status是什么,标记完以后都要将status恢复到所有上。否则会显示错误。
17、新建任务后右侧显示新建的任务也就是说中间任务列表中选中刚才新建的任务。
18、编辑任务和新建任务的界面都有确定button,为什么用EventUtil.addHandler为save和edit添加两个监听函数时,结果是两个函数都被执行了。
原因:添加事件的本质是给id为save或者id为edit的这个button元素添加的事件,两个事件都添加到了这个button上,所以两个事件都会执行。
解决办法:为button所在的div块添加监听事件,去判断button的id到底是save还是edit再去执行对应的函数。
//右侧编辑界面中的点击事件
EventUtil.addHandler(document.querySelector("#saveOrCancel"),'click',function(event)
{
event=EventUtil.getEvent(event);
var target=EventUtil.getTarget(event);
if(target.id=='edit')
{
//console.log('编辑任务');
taskEdit();
}
else if(target.id=='save')
{
..............//新建任务
}
})
总结
开始写代码之前一定要构思好一步一步该写什么,最好的方法是分模块写,这样思路清晰也方便查找bug;写的过程中应该首先把架构搭起来,这样可以事半功倍;另外在写代码的过程中一定要细心,我的方法是一步步调试往下写,这样就很容易知道问题出在哪里;解决bug的过程中一定要细心,学会调试方法,不要急躁。
问题
对Dom结构不熟悉;对添加事件监听完全不熟悉需要加强基础部分的学习。