-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c3fbabe
commit 63166cc
Showing
6 changed files
with
454 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
|
||
{ | ||
"type": "chrome", | ||
"request": "launch", | ||
"name": "Launch index.html", | ||
// "url": "http://localhost:3000", | ||
"file": "${workspaceFolder}/index.html", | ||
"webRoot": "${workspaceFolder}/index.html" | ||
}, | ||
{ | ||
"type": "chrome", | ||
"request": "launch", | ||
"name": "Launch test.html", | ||
// "url": "http://localhost:3000", | ||
"file": "${workspaceFolder}/test.html", | ||
"webRoot": "${workspaceFolder}" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
|
||
<head> | ||
<meta charset="utf-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> | ||
<title>Todo App</title> | ||
|
||
<link rel="stylesheet" href="style.css"/> | ||
</head> | ||
|
||
<body> | ||
<div id="root"></div> | ||
<script src="script.js"></script> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
body { | ||
margin: 10px auto; | ||
width: 470px; | ||
} | ||
|
||
h3 { | ||
margin: 0; | ||
padding-bottom: .3em; | ||
padding-right: 20px; | ||
font-size: 1.1em; | ||
} | ||
|
||
p { | ||
margin: 0; | ||
padding: 0 0 .5em; | ||
} | ||
|
||
.pane { | ||
background: #edf5e1; | ||
padding: 10px 20px 10px; | ||
border-top: solid 2px #c4df9b; | ||
position: relative; | ||
} | ||
|
||
.remove-button { | ||
position: absolute; | ||
font-size: 110%; | ||
top: 0; | ||
color: darkred; | ||
right: 10px; | ||
display: block; | ||
width: 24px; | ||
height: 24px; | ||
border: none; | ||
background: transparent; | ||
cursor: pointer; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
/** | ||
* 疑问: | ||
* 1, 为什么点击 form 区域提交的是 submit 事件 | ||
* 2. 为 todolist 的整个区域添加 eventListener 是给他所有的子节点一起添加了吗? 如果不是,event.target 是怎么确定的? | ||
*/ | ||
|
||
class Model { | ||
constructor() { | ||
this.todos = JSON.parse(localStorage.getItem('todos')) || [] | ||
} | ||
|
||
update(){ | ||
localStorage.setItem('todos', JSON.stringify(this.todos)) | ||
this.onTodoListChanged(this.todos) | ||
|
||
} | ||
|
||
|
||
addTodo(todo) { | ||
this.todos = [...this.todos, todo] | ||
this.update() | ||
} | ||
|
||
editTodo(id, updatedText) { | ||
this.todos = this.todos.map(todo => { | ||
return todo.id === id ? { | ||
id: todo.id, | ||
text: updatedText, | ||
complete: todo.complete | ||
} : todo | ||
}) | ||
this.update() | ||
} | ||
|
||
deleteTodo(id) { | ||
this.todos = this.todos.filter(todo => todo.id !== id) | ||
this.update() | ||
} | ||
|
||
toggleTodo(id) { | ||
this.todos = this.todos.map(todo => | ||
todo.id === id ? { | ||
id: todo.id, | ||
text: todo.text, | ||
complete: !todo.complete | ||
} : todo) | ||
this.update() | ||
} | ||
|
||
bindEvents(controller){ | ||
this.onTodoListChanged = controller.onTodoListChanged | ||
} | ||
} | ||
|
||
class View { | ||
constructor() { | ||
this.app = this.getElemet('#root') | ||
|
||
this.title = this.createElement('h1') | ||
this.title.textContent = 'Todos' | ||
|
||
this.form = this.createElement('form') | ||
|
||
this.input = this.createElement('input') | ||
this.input.type = "text" | ||
this.input.placeholder = "Add Todo" | ||
this.input.name = "todo" | ||
|
||
this.submitButton = this.createElement('button') | ||
this.submitButton.textContent = 'Submit' | ||
|
||
this.todoList = this.createElement('ul', 'todo-list') | ||
|
||
this.form.append(this.input, this.submitButton) | ||
this.app.append(this.title, this.form, this.todoList) | ||
} | ||
|
||
getTodoText() { | ||
return this.input.value | ||
} | ||
|
||
resetInput() { | ||
this.input.value = '' | ||
} | ||
|
||
displayTodos(todos) { | ||
while (this.todoList.firstChild) { | ||
this.todoList.removeChild(this.todoList.firstChild) | ||
} | ||
if (todos.length === 0) { | ||
const p = this.createElement('p') | ||
p.textContent = 'Nothing to do! Add a task?' | ||
this.todoList.append(p) | ||
} else { | ||
todos.forEach(todo => { | ||
const li = this.createElement('li') | ||
li.id = todo.id | ||
const checkbox = this.createElement('input') | ||
checkbox.type = 'checkbox' | ||
checkbox.checked = todo.complete | ||
|
||
const span = this.createElement('span') | ||
span.contentEditable = true | ||
span.classList.add('editable') | ||
|
||
if (todo.complete) { | ||
const strike = this.createElement('s') | ||
strike.textContent = todo.text | ||
span.append(strike) | ||
} else { | ||
span.textContent = todo.text | ||
} | ||
|
||
const deleteButton = this.createElement('button', 'delete') | ||
deleteButton.textContent = 'Delete' | ||
li.append(checkbox, span, deleteButton) | ||
this.todoList.append(li) | ||
}) | ||
} | ||
} | ||
|
||
createElement(tag, className) { | ||
const element = document.createElement(tag) | ||
if (className) element.classList.add(className); | ||
return element | ||
} | ||
|
||
getElemet(selector) { | ||
const element = document.querySelector(selector); | ||
|
||
return element; | ||
} | ||
|
||
bindEvents(controller){ | ||
this.form.addEventListener('submit', controller.handleAddTodo) // 如果换成 click 会怎么样 | ||
this.todoList.addEventListener('click', controller.handleDeleteTodo) | ||
this.todoList.addEventListener('change', controller.handleToggle) | ||
this.todoList.addEventListener('focusout', controller.handleEditTodoComplete) | ||
this.todoList.addEventListener('input', controller.handleEditTodo) | ||
} | ||
} | ||
|
||
class Controller { | ||
constructor(model, view) { | ||
this.model = model; | ||
this.view = view; | ||
|
||
this.onTodoListChanged(this.model.todos) | ||
this.view.bindEvents(this) | ||
this.model.bindEvents(this) | ||
this.temporaryEditValue | ||
} | ||
|
||
onTodoListChanged = todos => { | ||
this.view.displayTodos(todos) | ||
} | ||
|
||
handleEditTodo = event => { | ||
if(event.target.className === 'editable'){ | ||
this.temporaryEditValue = event.target.innerText | ||
} | ||
} | ||
|
||
handleEditTodoComplete = event => { | ||
if(this.temporaryEditValue){ // 为什么这里不用验证 event.target的类型 | ||
const id = parseInt(event.target.parentElement.id) | ||
this.model.editTodo(id, this.temporaryEditValue) | ||
this.temporaryEditValue = '' | ||
} | ||
} | ||
|
||
handleAddTodo = event => { | ||
event.preventDefault() | ||
|
||
if(this.view.getTodoText()){ // 为什么这里不用验证 event.target 的类型 | ||
const todo = { | ||
id:this.model.todos.length > 0 ? this.model.todos[this.model.todos.length - 1].id + 1:1, | ||
text: this.view.getTodoText(), | ||
complete: false, | ||
} | ||
this.model.addTodo(todo) | ||
this.view.resetInput() | ||
} | ||
|
||
} | ||
|
||
handleDeleteTodo = event => { | ||
if(event.target.className === 'delete'){ | ||
const id = parseInt(event.target.parentElement.id) | ||
this.model.deleteTodo(id) | ||
} | ||
} | ||
|
||
handleToggle = event => { | ||
if(event.target.type === 'checkbox'){ | ||
const id = parseInt(event.target.parentElement.id) | ||
this.model.toggleTodo(id) | ||
} | ||
} | ||
} | ||
|
||
const model = new Model(); | ||
|
||
const view = new View(); | ||
const controller = new Controller(model, view); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
*, | ||
*::before, | ||
*::after { | ||
box-sizing: border-box | ||
} | ||
|
||
html { | ||
font-family: sans-serif; | ||
font-size: 1rem; | ||
color: #444; | ||
} | ||
|
||
#root { | ||
max-width: 450px; | ||
margin: 2rem auto; | ||
padding: 0 1rem; | ||
} | ||
|
||
form { | ||
display: flex; | ||
margin-bottom: 2rem; | ||
} | ||
|
||
[type="text"], | ||
button { | ||
display: inline-block; | ||
-webkit-appearance: none; | ||
padding: .5rem 1rem; | ||
font-size: 1rem; | ||
border: 2px solid #ccc; | ||
border-radius: 4px; | ||
} | ||
|
||
button { | ||
cursor: pointer; | ||
background: #007bff; | ||
color: white; | ||
border: 2px solid #007bff; | ||
margin: 0 .5rem; | ||
} | ||
|
||
[type="text"] { | ||
width: 100%; | ||
} | ||
|
||
[type="text"]:active, | ||
[type="text"]:focus { | ||
outline: 0; | ||
border: 2px solid #007bff; | ||
} | ||
|
||
[type="checkbox"] { | ||
margin-right: 1rem; | ||
font-size: 2rem; | ||
} | ||
|
||
h1 { | ||
color: #222; | ||
} | ||
|
||
ul { | ||
padding: 0; | ||
} | ||
|
||
li { | ||
display: flex; | ||
align-items: center; | ||
padding: 1rem; | ||
margin-bottom: 1rem; | ||
background: #f4f4f4; | ||
border-radius: 4px; | ||
} | ||
|
||
li span { | ||
display: inline-block; | ||
padding: .5rem; | ||
width: 250px; | ||
border-radius: 4px; | ||
border: 2px solid transparent; | ||
} | ||
|
||
li span:hover { | ||
background: rgba(179, 215, 255, 0.52); | ||
} | ||
|
||
li span:focus { | ||
outline: 0; | ||
border: 2px solid #007bff; | ||
background: rgba(179, 207, 255, 0.52) | ||
} |
Oops, something went wrong.