JavaScript 课程 |任务跟踪器项目
上一篇: JavaScript 课程 | JavaScript 中的对象
项目介绍
在本课程的最后一篇文章中,我们将学习如何制作一个简单的 JavaScript 应用程序,我们可以在其中添加任务、删除任务和编辑它们。 Pure(Vanilla) JavaScript 已被使用,我们还将大量使用 DOM 操作,因此主要先决条件之一是 HTML | DOM。
项目结构
index.html
styles.css
list.js
- 索引.html
HTML
Task Tracker
Todo
Completed Tasks
CSS
/* Basic Style */
body {
background: #ecf0f1;
color: #333;
font-family: Lato, sans-serif;
}
.container {
display: block;
width: 500px;
margin: 50px auto 0;
}
ul {
margin: 0;
padding: 0;
}
li * {
float: left;
}
li,
h3 {
clear: both;
list-style: none;
}
input,
button {
outline: none;
}
button {
background: none;
border: 0px;
color: #888;
font-size: 15px;
width: 100px;
margin: 10px 0 0;
font-family: Lato, sans-serif;
cursor: pointer;
}
button:hover {
color: #333;
}
/* Heading */
h3,
label[for='new-task'] {
color: #333;
font-weight: 700;
font-size: 15px;
border-bottom: 2px solid #333;
padding: 30px 0 10px;
margin: 0;
text-transform: uppercase;
}
input[type="text"] {
margin: 0;
font-size: 18px;
line-height: 18px;
height: 18px;
padding: 10px;
border: 1px solid #ddd;
background: #fff;
border-radius: 6px;
font-family: Lato, sans-serif;
color: #888;
}
input[type="text"]:focus {
color: #333;
}
.middle {
text-align: center;
}
/* New Task */
label[for='new-task'] {
display: block;
margin: 0 0 20px;
}
input#new-task {
float: right;
width: 318px;
}
p>button:hover {
color: #0FC57C;
}
/* Task list */
li {
overflow: hidden;
padding: 20px 0;
border-bottom: 1px solid #eee;
}
li>input[type="checkbox"] {
margin: 0 10px;
position: relative;
top: 15px;
}
li>label {
font-size: 18px;
line-height: 40px;
width: 237px;
padding: 0 0 0 11px;
}
li>input[type="text"] {
width: 226px;
}
li>.delete:hover {
color: #CF2323;
}
/* Completed */
#completed-tasks label {
text-decoration: line-through;
color: #888;
}
/* Edit Task */
ul li input[type=text] {
display: none;
}
ul li.editMode input[type=text] {
display: block;
}
ul li.editMode label {
display: none;
}
Javascript
// Add a new task.
let taskInput = document.getElementById("new-task");
// first button
let addButton = document.getElementsByTagName("button")[0];
// ul of #incomplete-tasks
let incompleteTaskHolder = document.getElementById("incomplete-tasks");
// completed-tasks
let completedTasksHolder = document.getElementById("completed-tasks");
/*---- Part 1 ----*/
// function to create new task item
let createNewTaskElement = function (taskString) {
let listItem = document.createElement("li");
// input (checkbox)
let checkBox = document.createElement("input"); // checkbox
// label
let label = document.createElement("label"); // label
// input (text)
let editInput = document.createElement("input"); // text
// button.edit
let editButton = document.createElement("button"); // edit button
// button.delete
let deleteButton = document.createElement("button"); // delete button
label.innerText = taskString;
// Each elements, needs appending
checkBox.type = "checkbox";
editInput.type = "text";
// innerText encodes special characters, HTML does not.
editButton.innerText = "Edit";
editButton.className = "edit";
deleteButton.innerText = "Delete";
deleteButton.className = "delete";
// and appending.
listItem.appendChild(checkBox);
listItem.appendChild(label);
listItem.appendChild(editInput);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);
return listItem;
}
/*---- Part 2 ----*/
let addTask = function () {
console.log("Add Task...");
let listItem = createNewTaskElement(taskInput.value);
if (taskInput.value == "") {
return;
}
// Append listItem to incompleteTaskHolder
incompleteTaskHolder.appendChild(listItem);
bindTaskEvents(listItem, taskCompleted);
taskInput.value = "";
}
/*---- Part 3 ----*/
let editTask = function () {
console.log("Edit Task...");
console.log("Change 'edit' to 'save'");
let listItem = this.parentNode;
let editInput = listItem.querySelector('input[type=text]');
let label = listItem.querySelector("label");
let containsClass = listItem.classList.contains("editMode");
// If class of the parent is .editmode
if (containsClass) {
label.innerText = editInput.value;
} else {
editInput.value = label.innerText;
}
listItem.classList.toggle("editMode");
}
/*---- Part 4 ----*/
let deleteTask = function () {
console.log("Delete Task...");
let listItem = this.parentNode;
let ul = listItem.parentNode;
// Remove the parent list item from the ul.
ul.removeChild(listItem);
}
/*---- Part 5 ----*/
let taskCompleted = function () {
console.log("Complete Task...");
// Append the task list item to the #completed-tasks
let listItem = this.parentNode;
completedTasksHolder.appendChild(listItem);
bindTaskEvents(listItem, taskIncomplete);
}
/*---- Part 6 ----*/
let taskIncomplete = function () {
console.log("Incomplete Task...");
// Mark task as incomplete.
let listItem = this.parentNode;
incompleteTaskHolder.appendChild(listItem);
bindTaskEvents(listItem, taskCompleted);
}
/*---- Part 7 ----*/
addButton.onclick = addTask;
addButton.addEventListener("click", addTask);
let bindTaskEvents = function (taskListItem, checkBoxEventHandler) {
console.log("bind list item events");
// select ListItems children
let checkBox = taskListItem.querySelector("input[type=checkbox]");
let editButton = taskListItem.querySelector("button.edit");
let deleteButton = taskListItem.querySelector("button.delete");
// Bind editTask to edit button.
editButton.onclick = editTask;
// Bind deleteTask to delete button.
deleteButton.onclick = deleteTask;
// Bind taskCompleted to checkBoxEventHandler.
checkBox.onchange = checkBoxEventHandler;
}
/*---- Part 8 ----*/
// cycle over incompleteTaskHolder ul list items
// for each list item
for (let i = 0; i < incompleteTaskHolder.children.length; i++) {
// bind events to list items children(tasksCompleted)
bindTaskEvents(incompleteTaskHolder.children[i], taskCompleted);
}
// cycle over completedTasksHolder ul list items
for (let i = 0; i < completedTasksHolder.children.length; i++) {
// bind events to list items children(tasksIncompleted)
bindTaskEvents(completedTasksHolder.children[i], taskIncomplete);
}
解释:
上面的 HTML 代码包含简单的列表标签和一个文本字段,我们将在添加、删除任务时填充文本。分配了某些类,我们在通过 DOM 获取特定元素或通过在 styles.css 文件中对其进行样式设置时使用这些类。以上所有内容都在一个具有“容器”类的 div 中,该类具有自己的样式和属性。
- 样式.css
CSS
/* Basic Style */
body {
background: #ecf0f1;
color: #333;
font-family: Lato, sans-serif;
}
.container {
display: block;
width: 500px;
margin: 50px auto 0;
}
ul {
margin: 0;
padding: 0;
}
li * {
float: left;
}
li,
h3 {
clear: both;
list-style: none;
}
input,
button {
outline: none;
}
button {
background: none;
border: 0px;
color: #888;
font-size: 15px;
width: 100px;
margin: 10px 0 0;
font-family: Lato, sans-serif;
cursor: pointer;
}
button:hover {
color: #333;
}
/* Heading */
h3,
label[for='new-task'] {
color: #333;
font-weight: 700;
font-size: 15px;
border-bottom: 2px solid #333;
padding: 30px 0 10px;
margin: 0;
text-transform: uppercase;
}
input[type="text"] {
margin: 0;
font-size: 18px;
line-height: 18px;
height: 18px;
padding: 10px;
border: 1px solid #ddd;
background: #fff;
border-radius: 6px;
font-family: Lato, sans-serif;
color: #888;
}
input[type="text"]:focus {
color: #333;
}
.middle {
text-align: center;
}
/* New Task */
label[for='new-task'] {
display: block;
margin: 0 0 20px;
}
input#new-task {
float: right;
width: 318px;
}
p>button:hover {
color: #0FC57C;
}
/* Task list */
li {
overflow: hidden;
padding: 20px 0;
border-bottom: 1px solid #eee;
}
li>input[type="checkbox"] {
margin: 0 10px;
position: relative;
top: 15px;
}
li>label {
font-size: 18px;
line-height: 40px;
width: 237px;
padding: 0 0 0 11px;
}
li>input[type="text"] {
width: 226px;
}
li>.delete:hover {
color: #CF2323;
}
/* Completed */
#completed-tasks label {
text-decoration: line-through;
color: #888;
}
/* Edit Task */
ul li input[type=text] {
display: none;
}
ul li.editMode input[type=text] {
display: block;
}
ul li.editMode label {
display: none;
}
注意:以上 HTML 和 CSS 文件主要用于演示部分。
- 列表.js
Javascript
// Add a new task.
let taskInput = document.getElementById("new-task");
// first button
let addButton = document.getElementsByTagName("button")[0];
// ul of #incomplete-tasks
let incompleteTaskHolder = document.getElementById("incomplete-tasks");
// completed-tasks
let completedTasksHolder = document.getElementById("completed-tasks");
/*---- Part 1 ----*/
// function to create new task item
let createNewTaskElement = function (taskString) {
let listItem = document.createElement("li");
// input (checkbox)
let checkBox = document.createElement("input"); // checkbox
// label
let label = document.createElement("label"); // label
// input (text)
let editInput = document.createElement("input"); // text
// button.edit
let editButton = document.createElement("button"); // edit button
// button.delete
let deleteButton = document.createElement("button"); // delete button
label.innerText = taskString;
// Each elements, needs appending
checkBox.type = "checkbox";
editInput.type = "text";
// innerText encodes special characters, HTML does not.
editButton.innerText = "Edit";
editButton.className = "edit";
deleteButton.innerText = "Delete";
deleteButton.className = "delete";
// and appending.
listItem.appendChild(checkBox);
listItem.appendChild(label);
listItem.appendChild(editInput);
listItem.appendChild(editButton);
listItem.appendChild(deleteButton);
return listItem;
}
/*---- Part 2 ----*/
let addTask = function () {
console.log("Add Task...");
let listItem = createNewTaskElement(taskInput.value);
if (taskInput.value == "") {
return;
}
// Append listItem to incompleteTaskHolder
incompleteTaskHolder.appendChild(listItem);
bindTaskEvents(listItem, taskCompleted);
taskInput.value = "";
}
/*---- Part 3 ----*/
let editTask = function () {
console.log("Edit Task...");
console.log("Change 'edit' to 'save'");
let listItem = this.parentNode;
let editInput = listItem.querySelector('input[type=text]');
let label = listItem.querySelector("label");
let containsClass = listItem.classList.contains("editMode");
// If class of the parent is .editmode
if (containsClass) {
label.innerText = editInput.value;
} else {
editInput.value = label.innerText;
}
listItem.classList.toggle("editMode");
}
/*---- Part 4 ----*/
let deleteTask = function () {
console.log("Delete Task...");
let listItem = this.parentNode;
let ul = listItem.parentNode;
// Remove the parent list item from the ul.
ul.removeChild(listItem);
}
/*---- Part 5 ----*/
let taskCompleted = function () {
console.log("Complete Task...");
// Append the task list item to the #completed-tasks
let listItem = this.parentNode;
completedTasksHolder.appendChild(listItem);
bindTaskEvents(listItem, taskIncomplete);
}
/*---- Part 6 ----*/
let taskIncomplete = function () {
console.log("Incomplete Task...");
// Mark task as incomplete.
let listItem = this.parentNode;
incompleteTaskHolder.appendChild(listItem);
bindTaskEvents(listItem, taskCompleted);
}
/*---- Part 7 ----*/
addButton.onclick = addTask;
addButton.addEventListener("click", addTask);
let bindTaskEvents = function (taskListItem, checkBoxEventHandler) {
console.log("bind list item events");
// select ListItems children
let checkBox = taskListItem.querySelector("input[type=checkbox]");
let editButton = taskListItem.querySelector("button.edit");
let deleteButton = taskListItem.querySelector("button.delete");
// Bind editTask to edit button.
editButton.onclick = editTask;
// Bind deleteTask to delete button.
deleteButton.onclick = deleteTask;
// Bind taskCompleted to checkBoxEventHandler.
checkBox.onchange = checkBoxEventHandler;
}
/*---- Part 8 ----*/
// cycle over incompleteTaskHolder ul list items
// for each list item
for (let i = 0; i < incompleteTaskHolder.children.length; i++) {
// bind events to list items children(tasksCompleted)
bindTaskEvents(incompleteTaskHolder.children[i], taskCompleted);
}
// cycle over completedTasksHolder ul list items
for (let i = 0; i < completedTasksHolder.children.length; i++) {
// bind events to list items children(tasksIncompleted)
bindTaskEvents(completedTasksHolder.children[i], taskIncomplete);
}
解释
- 第1部分
- 这个函数的工作方式是它接受'inputString' ,即我们在HTML 文本字段中作为任务传递的文本,然后它使用DOM 属性创建几个元素并将它们附加到特定的类中。追加后,我们将列表中的所有元素作为 listItems 插入。
- 第2部分
- 当我们单击按钮“addButton”(第 115 行)时调用此 addTask函数,然后在其中我们使用用户输入的值创建一个 listItem,然后检查该值,因为它不能是空字符串,然后我们只需添加在“inputTaskHolder”中的上述值,最后在调用“bindFunction”之前将其中的值设置为空字符串。
- 当我们单击按钮“addButton”(第 115 行)时调用此 addTask函数,然后在其中我们使用用户输入的值创建一个 listItem,然后检查该值,因为它不能是空字符串,然后我们只需添加在“inputTaskHolder”中的上述值,最后在调用“bindFunction”之前将其中的值设置为空字符串。
- 第 3 部分
- 此代码函数用于编辑现有任务,我们这样做会跟踪父节点,然后进行简单的 if-else 检查是否单击了“editMode”按钮,如果单击则只需分配标签的值innerText 到 editInput 内部的值,如果不是则反之亦然。然后在编辑之后,我们切换我们编辑的“editMode”的值。
- 此代码函数用于编辑现有任务,我们这样做会跟踪父节点,然后进行简单的 if-else 检查是否单击了“editMode”按钮,如果单击则只需分配标签的值innerText 到 editInput 内部的值,如果不是则反之亦然。然后在编辑之后,我们切换我们编辑的“editMode”的值。
- 第 4 部分
- 在这一部分中,我们通过使用当前节点的父节点,然后存储父节点的父节点,然后简单地删除该节点的子节点来删除任务以及我们执行此操作的方式。
- 在这一部分中,我们通过使用当前节点的父节点,然后存储父节点的父节点,然后简单地删除该节点的子节点来删除任务以及我们执行此操作的方式。
- 第 5 部分
- 在此函数中,我们只需将父节点的子节点附加到 completeTaskHolder 元素中,然后调用 bindFunction 即可将任务标记为完成。
- 在此函数中,我们只需将父节点的子节点附加到 completeTaskHolder 元素中,然后调用 bindFunction 即可将任务标记为完成。
- 第 6 部分
- 在这个函数中,我们通过简单地将父节点的子节点附加到 inCompleteTaskHolder 元素中,然后调用 bindFunction 将任务标记为未完成。
- 在这个函数中,我们通过简单地将父节点的子节点附加到 inCompleteTaskHolder 元素中,然后调用 bindFunction 将任务标记为未完成。
- 第 7 部分
- 在这一部分中,我们调用 BindFunction 来响应几个用户交互活动并使几个按钮工作。
- 在这一部分中,我们调用 BindFunction 来响应几个用户交互活动并使几个按钮工作。
- 第 8 部分
- 在最后一节中,我们在不完整和完整的 TaskHolder 元素中使用 for 循环遍历绑定子级的几个部分。
输出:(添加任何任务之前)
输出:(添加任务后)
输出:(完成任务后)
下一篇: JavaScript 课程 |练习测验-3