第十五天挑战(本地存储菜谱)
地址:https://javascript30.com/
所有内容均上传至gitee,答案不唯一,仅代表本人思路
中文详解:https://github.com/soyaine/JavaScript30
该详解是Soyaine及其团队整理编撰的,是对源代码的详解,强烈推荐大家观看学习!!!
本人gitee:https://gitee.com/thats-all-right-ha-ha/30-days—js-challenge
效果
-
样式分析
- 组件整体居中,组件内部由头部,菜单主体,菜单添加模块组成
-
逻辑分析
-
在输入框输入菜名,点击添加后,菜名在菜单主体内显示
-
输入框非空限定
-
点击菜单左侧的多选框,出现选中样式
-
菜单保存在本地,当页面刷新或关闭后,菜单内容和选中状态不会被重置
-
本人代码及思路分析
仅提供布局及逻辑代码
结构:
<div class="wrapper"><h2>LOCAL TAPAS</h2><p></p><ul class="plates"><li>Loading Tapas...</li></ul><form class="add-items"><input type="text" name="item" placeholder="Item Name" required><input type="submit" value="+ Add Item"></form>
</div>
逻辑:
//获取元素
const addItems = document.querySelector('.add-items');
const itemsList = document.querySelector('.plates');
const submit = document.querySelector('input[type="submit"]')
const items = JSON.parse(localStorage.getItem('Tapas')) || [];
//页面添加元素 + 本地存储
function addItem(e) {e.preventDefault()const text = document.querySelector('input[type="text"]').valueconsole.log(text)let content = {text,isDone: false}template(content, items.length)items.push(content)localStorage.setItem('Tapas', JSON.stringify(items))this.reset()
}
//模板函数
function template(dom, index) {let check = ''if (dom.isDone) check = 'checked'itemsList.innerHTML += `<li><input type='checkbox' data-index=${index} id='item${index}' ${check}><label for='item${index}'>${dom.text} </label></li>`
}
//页面初始化
function getItem() {items.forEach((item, index) => {template(item, index)})
}
getItem()
//事件标记,动态存储
function check(e) {const checkbox = document.querySelectorAll('input[type="checkbox"]')const index = e.target.dataset.index console.log(index) if(index || index === 0){items[index].isDone = !items[index].isDonelocalStorage.setItem('Tapas',JSON.stringify(items))}
}
//事件监听
addItems.addEventListener('submit', addItem)
itemsList.addEventListener('click', check)
分析:
-
**整体思路:**当提交事件执行的时候,使用localStorage对输入的内容和内容状态进行本地存储,在页面初始化的时候获取本地存储的数据并进行渲染,
-
具体实现:
- 首先选定需要监听和修改的元素
- addItem:
- 进行提交的时候会对默认对页面进行刷新,先使用e.preventDefault阻止默认事件
- 获取输入框内的内容,并初始化需要添加的数据,并将数据添加到模板中,通过模板添加到页面中
- 将数据添加到数组中,再将该数组添加到本地,最后重置表单内容
- template:构建字符串模板,并初始化元素属性(data-index & item)和选中状态
- getItem:页面刷新或打开时,获取本地数据,并将他们通过template函数渲染到页面中
- check:
- 获取页面中所有的复选框
- 这里监听的是itemsList父级元素,target会返回父级元素中所包含的所有元素,这里会返回两个元素,分别是label和input
- 事先定义了data-index作为页面中添加元素的下标,并对其进行了唯一标识
- 首先判断其是否携带data-index属性来判断其是否是input标签,如果存在那么便从checkbox集合中通过index寻找出对应的被点击的input元素,并对其状态进行动态修改,修改后保存到本地
-
弊端分析(与官方方法对比):
- DOM操作频繁: 在模板函数中,每次添加一个待办事项都会重新设置itemsList的innerHTML,这样会引起DOM操作频繁,效率较低。
- 事件委托不足: 目前勾选完成待办事项的事件监听是直接添加在每个checkbox上的,当待办事项较多时,会导致事件监听器过多,影响性能。应该考虑使用事件委托,将事件监听器添加到父元素上,然后通过事件冒泡来处理。
- **页面初始化渲染性能开销大:**页面初始化渲染的时候使用的是forEach方法,该方法频繁对页面元素进行增加,消耗性能
官方代码
官方代码仅代表该案例原作者思路,不唯一
结构
<div class="wrapper"><h2>LOCAL TAPAS</h2><p></p><ul class="plates"><li>Loading Tapas...</li></ul><form class="add-items"><input type="text" name="item" placeholder="Item Name" required><input type="submit" value="+ Add Item"></form>
</div>
逻辑
const addItems = document.querySelector('.add-items');
const itemsList = document.querySelector('.plates');
const items = JSON.parse(localStorage.getItem('items')) || [];function addItem(e) {e.preventDefault();const text = (this.querySelector('[name=item]')).value;const item = {text,done: false};items.push(item);populateList(items, itemsList);localStorage.setItem('items', JSON.stringify(items));this.reset();
}function populateList(plates = [], platesList) {platesList.innerHTML = plates.map((plate, i) => {return `<li><input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''} /><label for="item${i}">${plate.text}</label></li>`;}).join('');
}function toggleDone(e) {if (!e.target.matches('input')) return; // skip this unless it's an inputconst el = e.target;const index = el.dataset.index;items[index].done = !items[index].done;localStorage.setItem('items', JSON.stringify(items));populateList(items, itemsList);
}addItems.addEventListener('submit', addItem);
itemsList.addEventListener('click', toggleDone);populateList(items, itemsList);
分析
仅代表本人对该代码的分析
建议直接去看Soyaine的中文详解
-
**整体思路:**整体写法与上述保持一致
-
具体实现:
- addItem:与上述相比,这里是通过name属性来选择元素
- toggleDone:这里使用正则的方法对父级元素内的子元素进行过滤,保留input元素
- populateList:这里使用map方法对添加元素进行整合,最后整体添加到页面中
-
优点:
- 通过
map
方法一次性生成所有待办事项的HTML字符串,然后一次性插入到DOM中,减少了浏览器的重绘和重排次数,但在处理大量数据时仍有优化空间。 - 第二段代码通过在列表的父元素上监听点击事件,使用了事件委托的策略,这样做可以减少事件监听器的数量,提高性能,尤其是在待办事项数量较多时。
- 通过