localStorage实现本地储存树形菜单

localStorage实现本地储存树形菜单

最近在写一个Todo-list的页面,页面布局和操作都写完后,想要用localStorage实现本地储存。然而对储存数据的方法一无所知,就先去了解了web的数据储存。

数据储存

常用的web的数据储存有cookie和Web Storage储存机制。

cookie

cookie是“小型文本文件”,主要用途是辨别用户身份、保存用户登录信息。cooki是由服务器端生成、储存在客户端上的数据(通常经过加密)。

  1. Cookie会被附加在每个HTTP请求中,所以无形中增加了流量。

  2. HTTP请求中的Cookie是明文传递的,安全性成问题。(除非用HTTPS)

  3. Cookie的大小限制在4KB左右。于复杂的存储需求来说是不够用的。

Web Storage

Web Storage定义了两种储存数据的对象,localStorage和sessionStorage,它们储存大小一般为5MB,机制如下:

  1. sessionStorage由浏览器存储某个会话(browsing session)的数据,数据在当前会话下有效,刷新页面数据依旧存在,但在关闭页面或浏览器后被清除。

  2. localStorage由浏览器存储数据,数据没有过期时间(expiration time),也就是浏览器关闭再重新打开后数据仍然存在。

localStorage对象的操作

在存储中设置值:Storage.setItem()
从存储中获取值:Storage.getItem()
响应存储的变化:window.addEventListener('storage', function(e){});
删除数据记录:Storage.removeItem() 接受一个参数——你想要移除的数据项的键,然后会将对应的数据项从域名对应的存储对象中移除。Storage.clear() 不接受参数,只是简单地清空域名对应的整个存储对象。

树形菜单的数据储存

在我的todo-list页面中,用户对树的操作有:

  1. 在树形菜单上添加、删除任务分类

  2. 在任务分类中添加、删除该分类中的任务。
    也是是说用户的每次操作后都要更新存储的数据,以便在刷新页面后显示出用户操作后更改过的页面。

由于本地储存只能存字符串数据,所以存储属性菜单可以用到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

3. 删除任务

tasklistArr.splice(i,1);
allTasks.list = [];
for(var j=0;j

4. 添加任务

allTasks.list = [];
for(var i=0;i

5. 测试本地存储是否已被填充

if(!localStorage.getItem('treeClassify')) {
//本地没有存储,加载初始设置的页面
} else {
//本地有存储,不加载初始设置的页面,而是根据储存来渲染页面
}

需要注意的地方

加载初始设置的页面的过程中,不需要存储任何数据,也就是不需要操作localStorage对象。这样的话如果用户不对页面进行操作,就没有任何数据需要存储。只有在用户对页面进行操作后,才会储存并再页面再次刷新时按照储存的内容进行页面的渲染。

参考资料

详说 Cookie, LocalStorage 与 SessionStorage
JSON.stringify
链使用 Web Storage API

关键字:JavaScript

版权声明

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处。如若内容有涉嫌抄袭侵权/违法违规/事实不符,请点击 举报 进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部