This example is to imitate the official website sample style and function with my own way to write, basically did not see the official website source code, only reference custom instructions. Let’s go through them step by step. Website demo

The functionality to be implemented

  1. Add todo individually
  2. Single delete todo
  3. Double-click to edit Todo
  4. A single todo has completed the corresponding style state change
  5. All toDO is completed with the corresponding style state change
  6. Clear all completed todos
  7. Todos number display
  8. All TOdos, TODOS completed, toDOS filter not completed

Add todo individually

<input type="text" class="todos_add" placeholder="What needs to be done?" 
@keyup.enter="addTodo($event.target)"/ / bindingenterThe eventref="currentInput">// Empty the input field by manipulating the input element to enterCopy the code
data() {// Some initialization data
	return {
		todolists: [].dataStatus: ["All"."Active"."Completed"].dataStatusIndex: 0.whichShow: true.defaultShow: true}},Copy the code
addTodo(e) { / / add a todo
	var val = e.value
	if (val === "") {return} // Return immediately if the input is empty
	this.todoLists = this.todoLists.concat({// Add todo using concat API
		value: val, // Enter the content
		isEditing: false.// Whether to edit
		isActive: false.// Delete whether the X icon is activated
		isChecked: false // Whether it is completed
	})
	this.$refs.currentInput.value = "" // Press Enter to add todo and clear the value field
	window.localStorage.setItem("content".JSON.stringify(this.todoLists))// Use localStorage to store data in JSON format
},
Copy the code

The corresponding state of each TODO is stored in the same object, and the operation to change the TODO state will not be unified processing, so that each TODO has a separate state.

Single delete todo

<li class="content_todoList" v-for="(list,index) in todoLists" @mouseover="list.isActive = true" // Mouseleave ="list.isActive=false" // Mouseleave ="list.isActive=false" // Mouseleave ="list.isActive=false" <span class="el-icon-close"  content_todoList_delete" :class="{show: @click="deleteTodo(index)"> </span> </li>Copy the code
deleteTodo(index) { // Delete a single todo
		this.todoLists.splice(index, 1)// Use splice's API
		window.localStorage.setItem("content".JSON.stringify(todoLists)) // Stores data in JSON format //localStorage stores data
},
Copy the code

Bind mouse-in and mouse-out events to each LI element to dynamically change the isActive of each toDO, and then use isActive to dynamically display the class.

Double click to edit todo&& single todo is complete

<input type="checkbox" class="checkBox" 
v-model="list.isChecked">// Bidirectionally bind isChecked<div class="content_todoList_main" 
@dblclick="toEdit(list)"// Double-click the eventv-show=! "" list.isEditing"// Switch elements:class="{deleted:list.isChecked}">Todo {{list.value}}</div>

<input type="text" class="content_todoList_main main_input" 
v-model="list.value"// Two-way binding can be enteredvalue
v-show="list.isEditing"// Switch elementsv-todo-focus="list.value"// Custom command, double-click automaticallyfocusFocus @blur="unEdit(list)">// bind blur to lose focus eventsCopy the code
methods: {
	toEdit(obj) { // Make the added todothing editable
		obj.isEditing = true
	},
	
	unEdit(obj) { // Make the added todothing uneditable
		obj.isEditing = false
	},
}

directives: { // Define the focus directive with a binding parameter
	"todo-focus": function (el, binding) {
		if (binding.value) {
			el.focus()
		}
	}
}
Copy the code

The show and editable states are controlled by the isEditing attribute in each todo. Double-click on div to change the isEditing state to true, and the todo isEditing state is displayed as input. After the input loses focus, the blur event changes it to false. Todo’s idChecked controls whether it’s done, changing the style dynamically.

All TODOS are complete

<span 
class="icon-down el-icon-arrow-down"/ / useelementLibrary makes down arrowicon
v-show="todoLists.length>0"/ / bytodoListsControls whether the down arrow is displayedicon
@click="selectAllTodos"></span>// All completed eventsCopy the code
selectAllTodos() { // Set all toDos to completed, using map's API via todo's isChecked property
	this.todoLists.map(todo= > todo.isChecked = todo.isChecked ? false : true)}Copy the code

Todos number display

<div class="data_times" v-show="times === 0">// if times is 0, display item; if times is greater than 0, display items<span>{{times}}</span>&nbsp item left
</div>
<div class="data_times" v-show="times > 0">
<span>{{times}}</span>&nbsp items left</div>
Copy the code
computed: {
	times() { // Count todos todos with the count property
		let todoArr = this.todoLists
		let times = 0
		for (let i = 0; i < todoArr.length; i++) {
			if (todoArr[i].isChecked === false) {
				times++
			}
		}
		return times
	}
},
Copy the code

The todoLists are computed using the calculated attribute, and the for loop is swiped to select idChecked as true and finally return times.

Clear all completed todos

<div class="data_clearTodos" 
@click="clearTodos" 
v-show="times < todoLists.length">// Display "Clear Completed" if the todoLists number is less than the total todoLists length<a href="#">clear completed</a>
</div>

<div class="data_clearTodos" 
@click="clearTodos" 
v-show="times === todoLists.length">// Display "Clear Completed" if the number of todoLists events equals the total todoLists length<a href="#"></a>
</div>
Copy the code
clearTodos() { // Empty the completed todoLists, using filter's API for filtering
	this.todoLists = this.todoLists.filter(todo= > todo.isChecked === false)
	window.localStorage.setItem("content".JSON.stringify(this.todoLists)) // Store data in JSON format
},
Copy the code

If The Times of todos to be done is less than the todoLists length, proving that a toDO has been completed, you can display “Clear Completed”, and if equal, indicating that no toDo has been completed.

Three state filters

All TOdos, TODOS completed, toDOS filter not completed

<li class="content_todoList" 
v-show="defaultShow || (whichShow? list.isChecked:! list.isChecked)">

<div class="data_status">
	<a href="#" 
	:class="{active: index === dataStatusIndex}"/ / dynamicclassimplementationtabswitchv-for="(item ,index) in dataStatus" v-forLoop @click="switchStatus(index)"// Switch differenttabAccording to the content:key="index">
		{{item}}
	</a>
</div>
Copy the code
switchStatus(index) { // Determine the operation by the if condition
	this.dataStatusIndex = index
	if (this.dataStatus[index] === "Active") {
		this.defaultShow = false
		this.whichShow = false
	} else if (this.dataStatus[index] === "Completed") {
		this.defaultShow = false
		this.whichShow = true
	} else if (this.dataStatus[index] === "All") {
		this.defaultShow = true}},Copy the code

Here I am at the same time if conditional sentence judgment operation, a little trouble, so far I have not come up with other schemes. Todos in different states are displayed using the ternary and or operators on the li element.

The complete code

<style>
	* {
		padding: 0;
		margin: 0;
		box-sizing: border-box;
	}

	input {
		outline: none;
	}

	ul,
	li,
	ol {
		list-style: none;
	}

	#app {
		width: 800px;
		height: 900px;
		margin: 0 auto;
		text-align: center;
		background-color: rgb(245, 245, 245);
		padding: 24px 0;
	}

	#app .header {
		font-size: 48px;
	}

	.content {
		width: 72%;
		margin: 0 auto;
		box-shadow: 0 3px 3px 2px rgba(0, 0, 0, 0.25);
		position: relative;
	}

	.icon-down {
		position: absolute;
		font-size: 24px;
		top: 16px;
		left: 16px;
		cursor: pointer;
	}

	#app .content .todos_add {
		width: 100%;
		height: 56px;
		padding: 16px 56px;
		font-size: 24px;
		border: 1px solid transparent;
	}

	.content_todoLists {
		position: relative;
		z-index: 3;
	}

	.content_todoList {
		display: flex;
		flex-direction: row;
		border-top: 1px solid #ccc;
		font-size: 24px;
		padding: 8px;
		background-color: white;
		align-items: center;
	}

	.checkBox {
		width: 20px;
		height: 20px;
		margin-left: 10px;
	}

	.content_todoList_main {
		flex: 1;
		text-align: left;
		margin-left: 16px;
		font-size: 20px;
		padding: 6px 0;
	}

	.main_input {
		position: relative;
		z-index: 1;
	}

	.content_todoList_delete {
		position: absolute;
		right: 16px;
		color: rgb(252, 55, 55);
		font-weight: 500;
		display: none;
		cursor: pointer;
	}

	.show {
		display: block;
	}

	.deleted {
		text-decoration-line: line-through;
		color: #bbb;
	}

	.show:hover {
		color: rgb(255, 0, 0);
		font-weight: 700;
	}

	::-moz-placeholder {
		color: rgb(221, 218, 218);
	}

	::-webkit-input-placeholder {
		color: rgb(221, 218, 218);
	}

	:-ms-input-placeholder {
		color: rgb(221, 218, 218);
	}

	.data {
		display: flex;
		justify-content: space-between;
		padding: 8px;
		font-size: 14px;
		font-weight: 300;
		color: rgb(145, 145, 145);
	}

	a {
		text-decoration: none;
		color: rgb(145, 145, 145);
	}

	.data_times {
		width: 73px;
	}

	.data_clearTodos {
		width: 142px;
	}

	.data_status a {
		display: inline-block;
		border: 1px solid transparent;
		border-radius: 2px;
		padding: 1px 4px;
		margin: 0 2px;
	}

	.data_status a:hover {
		border-color: #bbb;
	}

	.data_clearTodos a:hover {
		text-decoration-line: underline;
	}

	.active {
		box-shadow: 0 0 1px black;
	}
</style>
Copy the code
<body>
	<div id="app">
		<header class="header">todos</header>
		<div class="content">
			<span class="icon-down el-icon-arrow-down" 
			v-show="todoLists.length>0" 
			@click="selectAllTodos">
			</span>
			<input type="text" class="todos_add" placeholder="What needs to be done?" 
			@keyup.enter="addTodo($event.target)" 
			ref="currentInput">
			<ul class="content_todoLists">
				<li v-for="(list,index) in todoLists" class="content_todoList" 
				@mouseover="list.isActive = true" 
				@mouseleave="list.isActive=false"
				v-show="defaultShow || (whichShow? list.isChecked:! list.isChecked)">
					<input type="checkbox" class="checkBox" v-model="list.isChecked">
					<div class="content_todoList_main" @dblclick="toEdit(list)" v-show=! "" list.isEditing" :class="{deleted:list.isChecked}">
						{{list.value}}
					</div>
					<input type="text" class="content_todoList_main main_input" 
					v-model="list.value" 
					v-show="list.isEditing" 
					v-todo-focus="list.value"
					@blur="unEdit(list)">
					<span class="el-icon-close content_todoList_delete" :class="{show: list.isActive}" @click="deleteTodo(index)"></span>
				</li>
			</ul>
			<div class="data" v-show="todoLists.length>0">
				<div class="data_times" v-show="times === 0">
					<span>{{times}}</span>&nbspitem left
				</div>
				<div class="data_times" v-show="times > 0">
					<span>{{times}}</span>&nbspitems left
				</div>
				<div class="data_status">
					<a href="#" :class="{active:index === dataStatusIndex}" v-for="(item,index) in dataStatus" @click="switchStatus(index)" :key="index">
						{{item}}
					</a>
				</div>
				<div class="data_clearTodos" @click="clearTodos" v-show="times < todoLists.length">
					<a href="#">clear completed</a>
				</div>
				<div class="data_clearTodos" @click="clearTodos" v-show="times === todoLists.length">
					<a href="#"></a>
				</div>
			</div>
		</div>
	</div>
</body>
Copy the code
<script> let vm = new Vue({ el: "#app", data() { return { todoLists: [], dataStatus: ["All", "Active", "Completed"], dataStatusIndex: 0, whichShow: true, defaultShow: true } }, computed: TodoArr = this.todoLists let times = 0 for (let I = 0; i < todoArr.length; i++) { if (todoArr[i].isChecked === false) { times++ } } return times } }, methods: {toEdit(obj) {// make the added todo editable obj.isEditing = true}, unEdit(obj) {// Make the added todo uneditable obj.isEditing = false}, AddTodo (e) {// addTodo var = e.value if (val === "") {return} // immediately return this.todolists = if the input is empty This.todolists. concat({// add todo value: val with concat API, // input content isEditing: false, // if isActive: False, // Delete the X icon to enable isChecked: Whether false / /}) has been completed this. $refs. CurrentInput. Value = "" / / press enter to add todo the input box value after reset window. The localStorage. SetItem (" content", Json.stringify (this.todolists)) // Use localStorage to store data in JSON format}, DeleteTodo (index) {/ / delete todo this. TodoLists. Splice (index of 1) the window. The localStorage. SetItem (" content ", Json.stringify (this.todolists)) // Store data in JSON format}, switchStatus(index) {// Try the following three state switches, This. dataStatusIndex = index if (this.dataStatus[index] === "Active") {this.defaultShow = false this.whichShow = false } else if (this.dataStatus[index] === "Completed") { this.defaultShow = false this.whichShow = true } else if (this.dataStatus[index] === "All") { this.defaultShow = true } }, ClearTodos () {// Clear completed todoLists this.todolists = this.todolists.filter (todo => todo.ischecked === false) Window. The localStorage. SetItem (" content ", JSON. Stringify (enclosing todoLists)) / / store data in JSON format}, SelectAllTodos () {// Set all todos as completed this.todolists.map (todo => todo.ischecked = todo.ischecked? -- Cache: {// Self-define focus directive "todo-focus": function (el, binding) { if (binding.value) { el.focus() } } }, created() { let myStorage = window.localStorage.getItem('content') this.todoLists = JSON.parse(myStorage) || [] // Since todoLists initial value is null, use the or operator, if null set to an empty array}}) </script>Copy the code