学完了ES6,就可以拿一个小项目练练手,todoMVC是一个比较出名的用来练习各种编程语言的项目,可以直接去它官网看。
这里写一个ES6版本的。
点击图片看效果,如果想看Vue2版本的,也可以点这里。
增删改查功能都有,自己看代码。
核心代码:
// 找对象
const newTodoInput = $('.new-todo');
const oTodoList = $('.todo-list');
const oMain = $('.main');
const oFooter = $('.footer');
const oTodoCount = $('.todo-count');
const oClearCompleted = $('.clear-completed');
const oToggleAll = $('.toggle-all');
const oTodoType = $$('.filters a');
// 数据的本地存储
const TODOS_KEY = 'dotos_ES6';
let todosStorage = {
get() {
return JSON.parse(localStorage.getItem(TODOS_KEY)) || [];
},
set(todos) {
localStorage.setItem(TODOS_KEY,JSON.stringify(todos));
}
}
// 全局变量
// 存储数据的todos
// let todos = [
// { id: 1, title: '学习', completed: false },
// { id: 2, title: '做饭', completed: true },
// { id: 3, title: '接娃放学', completed: true },
// ]
let todos = todosStorage.get();
// 用于区分单击和双击事件
let timer = null;
// 初始化渲染
render(todos);
// 当enter的时候添加事项
newTodoInput.addEventListener('keyup', (e) => {
if (e.key === 'Enter') {
// 添加事项
addTodo();
todosStorage.set(todos);
// 重新渲染
render(todos);
}
})
// 因为有很多事项,有很多删除按钮,所以用事件委托性能更高
// ul同时委托了单击事件和双击事件,通过300ms的延迟来判断是单击还是双击
// 如果300ms以后没有单击,则是单击事件,执行对应的代码。
oTodoList.addEventListener('click', function (e) {
clearTimeout(timer);
timer = setTimeout(function () {
const target = e.target;
console.log(target);
const tempLi = target.closest('li');
// 根据li的id属性找到todos里面该id的数据,数字和字符串的相等用==
const todo = todos.find(item => item.id == tempLi.id);
// 如果点击的是删除按钮,则删除该事项
if (target.className === 'destroy') {
delTodo(todo);
}
// 如果点击的是复选框按钮,则切换完成和未完成的状态
if (target.className === 'toggle') {
todo.completed = target.checked;
}
// 修复双击下出现输入框编辑的时候,单击输入框的文本,输入框又消失的问题
if (target.className === 'edit') {
const oEditing = tempLi.querySelector('.edit');
tempLi.classList.add('editing');
oEditing.focus();
return;
}
todosStorage.set(todos);
render(todos);
}, 300);
})
// 编辑事项
oTodoList.addEventListener('dblclick', function (e) {
// 如果300ms以内又有点击事件,则是双击事件,清掉前面定时器要执行的代码,执行双击对应的代码。
clearTimeout(timer);
const target = e.target;
console.log(target);
const tempLi = target.closest('li');
const todo = todos.find(item => item.id == tempLi.id);
if (target.tagName === 'LABEL') {
const oEditing = tempLi.querySelector('.edit');
tempLi.classList.add('editing');
oEditing.focus();
// 把输入框的光标放在最后面。
// element.setSelectionRange(selectionStart, selectionEnd [, selectionDirection]);
// 如果希望全选输入元素中的文本,你可以使用 HTMLInputElement.select() 方法。
oEditing.setSelectionRange(-1, -1);
// 存储输入框以前的数据
const titleBeforeEdit = oEditing.value;
oEditing.addEventListener('keyup', function (e) {
console.log(e.key)
const val = this.value.trim();
// 编辑完成
if (e.key === 'Enter') {
if (val === '') {
delTodo(todo);
} else {
todo.title = val;
}
todosStorage.set(todos);
render(todos);
}
// 取消编辑
if (e.key === 'Escape') {
this.value = titleBeforeEdit;
render(todos);
}
})
// 失去焦点的时候,编辑完成
oEditing.addEventListener('blur', function () {
const val = this.value.trim();
if (val === '') {
delTodo(todo);
} else {
todo.title = val;
}
todosStorage.set(todos);
render(todos);
})
}
})
// 删除完成的事项
oClearCompleted.addEventListener('click', function () {
todos = todos.filter(todo => !todo.completed);
todosStorage.set(todos);
render(todos);
})
// 点击全选复选框,对所有事项的状态切换
oToggleAll.addEventListener('change', function () {
const val = this.checked;
for (const todo of todos) {
todo.completed = val;
}
todosStorage.set(todos);
render(todos);
})
// 根据hash切换a的状态
// 不能点击a的时候获取hash,否则获取的是上一个hash值。
window.onhashchange = function () {
// console.log(location.hash);
// a被选中的状态
selectedType();
render(todos);
}
// 找对象的函数
function $(selector) {
return document.querySelector(selector);
}
function $$(selector) {
return document.querySelectorAll(selector);
}
// 渲染事项
function render(todos) {
// 对面板的切换
if (todos.length) {
oMain.style.display = 'block';
oFooter.style.display = 'block';
} else {
oMain.style.display = 'none';
oFooter.style.display = 'none';
}
if (!todos.length) {
return;
};
let renderTodos = [];
// 筛选数据
// console.log(location.hash);
switch (location.hash) {
case '#/active':
renderTodos = todos.filter(todo => !todo.completed);
break;
case '#/completed':
renderTodos = todos.filter(todo => todo.completed);
break;
default:
renderTodos = todos;
break;
}
// a的被选中状态
selectedType();
let str = '';
for (const obj of renderTodos) {
str += `<li id='${obj.id}' class=${obj.completed ? 'completed' : ''}>
<div class="view">
<input class="toggle" type="checkbox" ${obj.completed ? 'checked' : ''}>
<label>${obj.title}</label>
<button class="destroy"></button>
</div>
<input class="edit" value="${obj.title}">
</li>`
}
oTodoList.innerHTML = str;
// 得到未完成的事项的个数。
const remainingCount = getRemainingCount(todos);
// 状态栏 未完成数量的显示
oTodoCount.innerHTML = `<strong>${remainingCount}</strong> ${remainingCount === 1 ? 'item' : 'items'} left`
// 删除完成事项的按钮
// 如果事项的总个数大于未完成事项的个数,则按钮显示
oClearCompleted.style.display = todos.length > remainingCount ? 'block' : 'none';
//对全选复选框的状态设置
if (remainingCount === 0 && todos.length !== 0) {
oToggleAll.checked = true;
} else {
oToggleAll.checked = false;
}
}
// 未完成的事项的个数
function getRemainingCount(todos) {
return todos.filter(item => !item.completed).length;
}
// 添加事项
function addTodo() {
let value = newTodoInput.value.trim();
if (!value) return;
todos.push({ id: Date.now(), title: value, completed: false });
newTodoInput.value = '';
}
// 删除单个事项
function delTodo(todo) {
const index = todos.indexOf(todo);
if (index !== -1) {
todos.splice(index, 1);
}
}
// 事项类别状态设置
function selectedType() {
oTodoType.forEach((item, index) => {
if (item.hash === location.hash) {
oTodoType.forEach(item => item.classList.remove('selected'));
item.classList.add('selected');
// console.log(item.hash);
}
})
}
发表评论:
◎请发表你卖萌撒娇或一针见血的评论,严禁小广告。