localStorage完成当地贮存树形菜单
最近在写一个Todo-list的页面,页面规划和操纵都写完后,想要用localStorage完成当地贮存。然而对贮存数据的要领一窍不通,就先去了解了web的数据贮存。
数据贮存
经常运用的web的数据贮存有cookie和Web Storage贮存机制。
cookie
cookie是“小型文本文件”,主要用途是分辨用户身份、保留用户登录信息。cooki是由服务器端天生、贮存在客户端上的数据(平常经由加密)。
Cookie会被附加在每一个HTTP要求中,所以无形中增添了流量。
HTTP要求中的Cookie是明文通报的,安全性成问题。(除非用HTTPS)
Cookie的大小限定在4KB摆布。于庞杂的存储需求来说是不够用的。
Web Storage
Web Storage定义了两种贮存数据的对象,localStorage和sessionStorage,它们贮存大小平常为5MB,机制以下:
sessionStorage由浏览器存储某个会话(browsing session)的数据,数据在当前会话下有用,革新页面数据照旧存在,但在封闭页面或浏览器后被消灭。
localStorage由浏览器存储数据,数据没有逾期时候(expiration time),也就是浏览器封闭再从新翻开后数据依然存在。
localStorage对象的操纵
在存储中设置值:Storage.setItem()
从存储中获取值:Storage.getItem()
相应存储的变化:window.addEventListener(‘storage’, function(e){});
删除数据纪录:Storage.removeItem() 接收一个参数——你想要移除的数据项的键,然后会将对应的数据项从域名对应的存储对象中移除。Storage.clear() 不接收参数,只是简朴地清空域名对应的全部存储对象。
树形菜单的数据贮存
在我的todo-list页面中,用户对树的操纵有:
在树形菜单上增添、删除使命分类
在使命分类中增添、删除该分类中的使命。
也是是说用户的每次操纵后都要更新存储的数据,以便在革新页面后显示出用户操纵后更悛改的页面。
由于当地贮存只能存字符串数据,所以存储属性菜单能够用到JSON。用JSON.parse将一个JavaScript对象字符串化为JSON,然后全部存入localStorage来完成保留。用JSON.stringify将JSON字符串剖析成为一个JavaScript对象来从存储中获取值。
那末怎样用对象存储树形菜单呢,我想到了两个体式格局。
体式格局一
一种是用一个对象treeframe存储树的构造(一切的使命分类,不包括分类中的使命)以及树结点中的使命,存储情势以下。一旦对树形菜单实行任何一项操纵,就更新treeframe对象。
var treeframe = {
"使命列表":{
"运用说明":[
{
"title":"一般使命列表",
"content":"能够设定使命的称号、使命形貌、使命停止时候",
"endTime":"2016-05-20",
"type":"normal",
},
{
"title":"增添新分类",
"content":"经由过程左边自定义分类的文件夹图标增添新分类",
"endTime":"2017-07-20",
"type":"normal",
}
],
"本日使命":{},
"短时间使命":{
"7月":{},
"8月":{},
"9月":{}
},
"历久使命":{}
}
}
体式格局二
另一种体式格局是用一个对象treeframe存储树的构造,另有一个对象allTasks存储一切结点中的使命。这类体式格局只须要在增添、删除使命分类时更新树的构造treeframe;增添、删除使命时变动allTasks。由于使命须要增添到树的构造上,所以要纪录使命是增添到哪一个结点上的,须要为每一个树结点增添一个id作为标识以便于在结点上增添使命,树状构造中每一个结点的id依据树的先序遍历将结点的id顺次贮存于数组treeClassify.list中。为了每一个新增的结点的id都不与已存在的id的结点雷同,用一个计数器fatherIdjsq来纪录可增添的新id。
存储情势以下:
var treeframe = {
"使命列表":{
"运用说明":{},
"本日使命":{},
"短时间使命":{
"7月":{},
"8月":{},
"9月":{}
},
"历久使命":{}
}
}
var allTasks = {
'list' : [ {
"fatherId":"fatherId1"
"title":"一般使命列表",
"cnode":{"value":"一般使命列表"},
"pnode":{"value":"运用说明"},
"content":"能够设定使命的称号、使命形貌、使命停止时候",
"endTime":"2016-05-20",
"type":"normal",
},
{
"fatherId":"fatherId5"
"title":"增添新分类",
"cnode":{"value":"增添新分类"},
"pnode":{"value":"运用说明"},
"content":"经由过程左边自定义分类的文件夹图标增添新分类",
"endTime":"2017-07-20",
"type":"fast",
}]
};
var treeClassfiy = {"list":["fatherId0","fatherId1","fatherId2","fatherId3","fatherId4","fatherId5","fatherId6","fatherId8","fatherId7"]}
var fatherIdjsq = 9
我以为假如最最先写全部todo-list时对全部页面的功用和操纵以及逻辑都理的迥殊清晰的话,第一种存储要领肯定用起来越发轻易。然则我选了第二种,由于如许便于我分别对树的构造和使命列表举行操纵,和我之前写好的代码比较轻易融会在一起。
所以终究要存储的数据以下:
- 树状构造的存储:对象字面量情势与json的互相转化
- 树状构造中每一个结点的id:依据树的先序遍历将结点的id顺次贮存于数组中
- 一切使命:每一个使命存储于一个对象中,一切使命的对象构成一个数组
- 能够增添的新id
树形菜单的操纵与贮存
1. 删除使命分类
变动树状构造,在界面上删除这个分类的后,将这个分类的id从存储id的数组中删去。
var jsq = 0; //计数器jsq用来纪录按先序遍历树,并将结点存于数组时,该结点在数组中的位置
preOrder(treedata); //先序遍历treeframe(treeframe初始贮存于treedata)
function preOrder(treeframe) {
for(var key in treeframe) {
if(jsq===fatherIdArr.indexOf(choseDiv.id)) { //查找到当前要删除的结点(经由过程检察当前要删除结点的id在贮存结点id的数组(treeClassfiy.list)中的位置)
delete treeframe[key]; //经由过程detele删除对象的属性来删除结点
var pos = treeClassifyIdArr.list.indexOf(choseDiv.id);//查找当前要删除的结点id在贮存结点id的数组中(treeClassfiy.list)的位置
treeClassifyIdArr.list.splice(pos,1); //把id从数组中删除
fatherIdArr.splice(jsq, 1);
break;
}
jsq++;
if(typeof treeframe[key] === "object") {
preOrder(treeframe[key]);
}
}
}
localStorage.setItem('treeframe', JSON.stringify(treedata)); //将更新的treedata存入设置的值treeframe中
2. 增添使命分类
每次手动增添使命分类时,树状构造会转变。也就是说树状构造的转变,意味着树的结点增添/删除了,存储结点id的数组也要转变。
所以要变动树状构造,为新添的分类设置一个新的id,在界面上建立这个分类的后,将这个分类的id插进去到存储id的数组中。
var jsq = 0;
preOrder(treedata);
function preOrder(treeframe) {
for(var key in treeframe) {
if(jsq===fatherIdArr.indexOf(choseDiv.id)) { //找到被选中的结点
fatherIdArr.push("fatherId"+fatherIdjsq); //先在数组中增添新的id,使新的结点衬着到页面上
treeframe[key][value] = {}; //更新树状构造,增添结点(为纪录树的构造的对象增添属性)
preOrder2(treeframe[key]);
function preOrder2(treeframe) { //jsq纪录被选中结点的的末了一个子节点
for(var key in treeframe) {
jsq++;
if(typeof treeframe[key] === "object") {
preOrder2(treeframe[key]);
}
}
}
break;
}
jsq++;
if(typeof treeframe[key] === "object") {
preOrder(treeframe[key]);
}
}
}
localStorage.setItem('treeframe', JSON.stringify(treedata)); //将更新的treedata存入设置的值treeframe中
/*此处衬着页面*/
fatherIdArr.pop(); //新的结点衬着到页面上后删除该结点的id
fatherIdArr.splice(jsq, 0, "fatherId"+fatherIdjsq++); //更新该结点id在id数组中的位置
treeClassifyIdArr.list = []; //将id数组中的元素增添到对象中
for(var j=0;j<fatherIdArr.length;j++) {
treeClassifyIdArr.list.push(fatherIdArr[j]);
}
localStorage.setItem('treeClassify', JSON.stringify(treeClassifyIdArr)); //将更新的treeClassifyIdArr存入设置的值treeClassify中
localStorage.setItem('fatherIdjsq', fatherIdjsq); //将更新的fatherIdjsq存入设置的值fatherIdjsq中
3. 删除使命
tasklistArr.splice(i,1);
allTasks.list = [];
for(var j=0;j<tasklistArr.length;j++) {
allTasks.list.push(tasklistArr[j]);
}
localStorage.setItem('allTasks', JSON.stringify(allTasks)); //更新贮存
4. 增添使命
allTasks.list = [];
for(var i=0;i<tasklistArr.length;i++) {
allTasks.list.push(tasklistArr[i]);
}
localStorage.setItem('allTasks', JSON.stringify(allTasks));//更新贮存
5. 测试当地存储是不是已被添补
if(!localStorage.getItem('treeClassify')) {
//当地没有存储,加载初始设置的页面
} else {
//当地有存储,不加载初始设置的页面,而是依据贮存来衬着页面
}
一切存储操纵的封装
var Storage = (function(mod) {
// 删除、增添、编辑疾速使命和一般使命的存储
mod.TaskChange = function() {
allTasks.list = [];
for(var i=0;i<tasklistArr.length;i++) {
allTasks.list.push(tasklistArr[i]);
}
localStorage.setItem('allTasks', JSON.stringify(allTasks));
};
// 存储纪录使命分类(树结点)的id
mod.treeNodeIdChange = function(jsq) {
// 将id数组中的元素增添到对象中
treeClassifyIdArr.list = [];
for(var j=0;j<d.fatherIdArr.length;j++) {
treeClassifyIdArr.list.push(d.fatherIdArr[j]);
}
// 将更新的treeClassifyIdArr存入设置的值treeClassify中
localStorage.setItem('treeClassify', JSON.stringify(treeClassifyIdArr));
// 将更新的d.fatherIdjsq存入设置的值d.fatherIdjsq中
localStorage.setItem('d.fatherIdjsq', d.fatherIdjsq);
};
// 增添使命分类后的存储
mod.addClassifySto = function(choseDiv,value) {
console.log("addClassifySto");
var jsq = 0;
preOrderAdd(d.treedata);
function preOrderAdd(treeframe) {
for(var key in treeframe) {
// 找到被选中的结点
if (jsq===d.fatherIdArr.indexOf(choseDiv.id)) {
// 先在数组中增添新的id,使新的结点衬着到页面上
d.fatherIdArr.push("fatherId"+d.fatherIdjsq);
// 更新树状构造,增添结点(为纪录树的构造的对象增添属性)
treeframe[key][value] = {};
preOrder2(treeframe[key]);
break;
}
jsq++;
if (typeof treeframe[key] === "object") {
preOrderAdd(treeframe[key]);
}
}
}
// jsq纪录被选中结点的的末了一个子节点
function preOrder2(treeframe) {
for(var key in treeframe) {
jsq++;
if (typeof treeframe[key] === "object") {
preOrder2(treeframe[key]);
}
}
}
// 将更新的treedata存入设置的值treeframe中
localStorage.setItem('treeframe', JSON.stringify(d.treedata));
return jsq;
};
// 删除使命分类后的存储
mod.delClassifySto = function(choseDiv) {
// tree
console.log("delClassifySto");
var jsq = 0;
preOrderDel(d.treedata);
function preOrderDel(treeframe) {
for(var key in treeframe) {
if (jsq===d.fatherIdArr.indexOf(choseDiv.id)) {
delete treeframe[key];
var pos = treeClassifyIdArr.list.indexOf(choseDiv.id);
treeClassifyIdArr.list.splice(pos,1);
d.fatherIdArr.splice(jsq, 1);
break;
}
jsq++;
if (typeof treeframe[key] === "object") {
preOrderDel(treeframe[key]);
}
}
}
localStorage.setItem('treeframe', JSON.stringify(d.treedata));
return jsq;
};
return mod;
})(Storage||{});
须要注重的处所
加载初始设置的页面的过程当中,不须要存储任何数据,也就是不须要操纵localStorage对象。如许的话假如用户不对页面举行操纵,就没有任何数据须要存储。只要在用户对页面举行操纵后,才会贮存并再页面再次革新时依据贮存的内容举行页面的衬着。
在线检察demo
在线demo可供参考,迎接review代码,求指摘、建媾和交换:p
localStorage完成当地贮存树形菜单_demo
参考资料
详说 Cookie, LocalStorage 与 SessionStorage
JSON.stringify
链运用 Web Storage API