不同版本实现ToDoList

一、静态网页原型

1、index.html

<!--
 * @Description: 
 * @Version: 1.0
 * @Autor: Yu
 * @Date: 2022-01-02 15:18:35
 * @LastEditors: Yu
 * @LastEditTime: 2022-01-02 16:34:19
-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ToDoList</title>
    <link rel="stylesheet" href="./css/index.css">
  </head>
  <body>
    <div class="contonter">
      <!-- 头部开始 -->
      <header>
          <input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
      </header>
      <!-- 头部结束 -->
​
      <!-- 内容部分开始 -->
      <section>
          <ul>
              <li><input type="checkbox"/>吃饭<button>删除</button></li>
              <li><input type="checkbox"/>睡觉<button>删除</button></li>
              <li><input type="checkbox"/>逛街<button>删除</button></li>
              <li><input type="checkbox"/>敲代码<button>删除</button></li>
          </ul>
      </section>
      <!-- 内容部分结束 -->
​
      <!-- 底部开始 -->
      <footer>
          <div class="footer_left">
              <input type="checkbox"/>已完成<span class="done">0</span>/全部<span class="all">4</span>
          </div>
          <div class="footer_right">
              <button>清除已完成任务</button>
          </div>
      </footer>
      <!-- 底部结束 -->
    </div>
  </body>
</html>

2、index.css

*{
    padding: 0;
    margin: 0;
}
​
ul{
    list-style: none;
}
​
input[type='checkbox']{
    width: 15px;
    height: 15px;   
    vertical-align: middle;
}
​
body{
    font-size: 18px;
    color: #434544;
}
​
.contonter{
    overflow: hidden;
    width: 1200px;       
    margin: 10px auto; 
    border: 2px solid #E2E2E2;  
    box-sizing: border-box;  
    border-radius: 10px;
    padding: 20px;
}
​
header{
    width: 100%;
    height: 50px;
}
 
header input{
    width: 100%;
    height: 100%;
    padding-left: 10px;
    box-sizing: border-box;        
    outline: none;
    border: 1px solid #DFDFDF;
    border-radius: 5px;
}
​
section ul{
    margin: 30px 0;
    border: 1px solid #E9E9E9;
    border-radius: 5px;    
}
​
section ul li{
    position: relative;
    height: 45px;
    line-height: 45px;
    padding: 0 10px;
    box-sizing: border-box;
    border-bottom: 1px solid #E9E9E9;    
}
​
section ul li:last-of-type{
    border-bottom: none;
}
​
section ul li:hover{
    background-color: rgb(235, 234, 234);        
    cursor: pointer;
}
​
section ul li input{
    margin-right: 10px;
}
​
section ul li button{
    position: absolute;
    top: 10px;
    right: 10px;
    height: 30px;
    padding: 0 10px;
    line-height: 30px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
}
​
footer{
    width: 100%;
    height: 50px;
    float: left;
    line-height: 50px;
}
​
.footer_left{    
    float: left;
    padding-left: 10px;
    box-sizing: border-box; 
}
​
.footer_left input{    
    margin-right: 20px;    
    cursor: pointer;
}
​
.footer_right{
    float: right;
}
​
.footer_right button{
    padding: 0 20px;
    height: 40px;
    line-height: 40px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
    cursor: pointer;
}

3、效果图

二、使用原生js实现ToDoList

1、index.html

<!--
 * @Description: 原生js实现ToDoList
 * @Version: 1.0
 * @Autor: Yu
 * @Date: 2022-01-02 15:18:35
 * @LastEditors: Yu
 * @LastEditTime: 2022-02-06 19:24:40
-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ToDoList</title>
    <!-- 引入css文件 -->
    <link rel="stylesheet" href="./css/index.css">
    <!-- 引入js文件 -->
    <script src="./js/index.js"></script>
  </head>
  <body>
    <div class="contonter">
      <!-- 头部开始 -->
      <header>
          <input type="text" id="ipt" placeholder="请输入你的任务名称,按回车键确认"/>
      </header>
      <!-- 头部结束 -->
​
      <!-- 内容部分开始 -->
      <section>
          <ul id="lis">
              <li class="empty">暂无数据</li>
          </ul>          
      </section>
      <!-- 内容部分结束 -->
​
      <!-- 底部开始 -->
      <footer>
          <div class="footer_left">
              <input type="checkbox" id="done"/>已完成<span class="done"></span>/全部<span class="all"></span>
          </div>
          <div class="footer_right">
              <button id="del_done">清除已完成任务</button>
          </div>
      </footer>
      <!-- 底部结束 -->
    </div>
    <!-- js文件 可以放到此处,好处就是不用监听window的onload,直接写js代码 -->
    <!-- <script src="./js/index.js"></script>     -->
  </body>
</html>

2、index.js

/*
 * @Description: js文件
 * @Version: 1.0
 * @Autor: Yu
 * @Date: 2022-01-02 16:48:34
 * @LastEditors: Yu
 * @LastEditTime: 2022-02-06 21:13:48
 */
//1、原始写法
// window.οnlοad=function(){
//     alert(111)
// }
//2、原始写法的es6写法
// window.οnlοad=()=>{
//     alert(111)
// }
//3、监听事件写法
// window.addEventListener('load',function(){
//     alert(111)
// })
//4、立即执行函数
// ;(function () {
//     alert(111);
// })();
​
//5、监听事件的es6写法
window.addEventListener('load', () => {
  // 获取元素
  const ipt = document.getElementById('ipt')
  let lis = document.getElementById('lis')
  const empty = document.getElementsByClassName('empty')[0]
  let countTask = 0
​
  countAll()
  isCheckedAll()
  countDoneTask()
  delAllDoneTask()
  /**
   * @description: 监听输入框的回车按键
   * @param {*}
   * @return {*}
   * @author: Yu
   */
  ipt.onkeydown = (event) => {
    if (event.keyCode == 13) {
      //判断输入的值是否为空
      if (ipt.value.trim() == '')
        return alert('您输入的任务不能为空,请重新输入!')
      const doneChecked = document.getElementById('done')
      doneChecked.checked = false
      empty.style.display = 'none'
      //创建一个任务
      let taskObj = {
        id: getUuid(),
        content: ipt.value,
        done: false,
      }
      //创建节点
      let li = document.createElement('li')
      let input = document.createElement('input')
      input.setAttribute('type', 'checkbox')
      let txt = document.createTextNode(taskObj.content)
      let button = createElementWithTxt('button', '删除')
      li.appendChild(input)
      li.appendChild(txt)
      li.appendChild(button)
      //向节点的后面添加一个任务
      //lis.appendChild(li);
      //向节点的前面添加一个任务
      lis.insertBefore(li, lis.children[0])
      ipt.value = ''
      delToDoList()
      countAll()
      isCheckedAll()
      delAllDoneTask()
    }
  }
​
  /**
   * @description: 删除一个任务
   * @param {*}
   * @return {*}
   * @author: Yu
   */
  function delToDoList() {
    btns = lis.getElementsByTagName('button')
    for (let i = 0; i < btns.length; i++) {
      btns[i].onclick = function (e) {
        if (confirm('确认要删除该任务吗?')) {
          lis.removeChild(this.parentNode)
          btns = lis.getElementsByTagName('button')
          stopPropagation(e)
          if (btns.length === 0) empty.style.display = 'block'
          const checkboxs = lis.getElementsByTagName('input')
          countTask = 0
          for (let i = 0; i < checkboxs.length; i++) {
            if (checkboxs[i].checked) {
              countTask++
            }
          }
          countDoneTask()
          countAll()
          const doneChecked = document.getElementById('done')
          if (btns.length === countTask && btns.length !== 0) {
            doneChecked.checked = true
          } else {
            doneChecked.checked = false
          }
        }
      }
    }
  }
​
  /**
   * @description: 统计所有的任务的数量
   * @param {*}
   * @return {*}
   * @author: Yu
   */
  function countAll() {
    const all = document.getElementsByClassName('all')[0]
    const btns = lis.getElementsByTagName('button')
    all.innerText = btns.length
  }
​
  /**
   * @description: 统计已完成的任务
   * @param {*}
   * @return {*}
   * @author: Yu
   */
  function countDoneTask() {
    const done = document.getElementsByClassName('done')[0]
    done.innerText = countTask
  }
​
  /**
   * @description: 全选和取消全选
   * @param {*}
   * @return {*}
   * @author: Yu
   */
  function isCheckedAll() {
    const doneChecked = document.getElementById('done')
    const checkboxs = lis.getElementsByTagName('input')
    doneChecked.onclick = function () {
      for (let i = 0; i < checkboxs.length; i++) {
        checkboxs[i].checked = this.checked
      }
      if (this.checked) {
        countTask = checkboxs.length
        countDoneTask()
      } else {
        countTask = 0
        countDoneTask()
      }
    }
    for (let i = 0; i < checkboxs.length; i++) {
      checkboxs[i].onclick = function () {
        let flag = true
        countTask = 0
        for (let i = 0; i < checkboxs.length; i++) {
          if (!checkboxs[i].checked) {
            flag = false
            break
          }
        }
        for (let i = 0; i < checkboxs.length; i++) {
          if (checkboxs[i].checked) {
            countTask++
          }
        }
        countDoneTask()
        // 设置全选按钮的状态
        doneChecked.checked = flag
      }
    }
  }
​
  /**
   * @description: 清除所有已完成的任务
   * @param {*}
   * @return {*}
   * @author: Yu
   */
  function delAllDoneTask() {
    let checkboxs = lis.getElementsByTagName('input')
    const delDone = document.getElementById('del_done')
    delDone.onclick = function () {
      if (checkboxs.length === 0) return alert('请添加任务!')
      if (confirm('确认要删除所有已完成的任务吗?')) {
        countTask = 0
        for (let i = 0; i < checkboxs.length; i++) {
          if (checkboxs[i].checked) {
            lis.removeChild(checkboxs[i].parentNode)
            i--
          }
        }
        checkboxs = lis.getElementsByTagName('input')
        for (let j = 0; j < checkboxs.length; j++) {
          if (checkboxs[j].checked) {
            countTask++
          }
        }
        countDoneTask()
        btns = lis.getElementsByTagName('button')
        if (btns.length === 0) {
          const doneChecked = document.getElementById('done')
          doneChecked.checked = false
          empty.style.display = 'block'
        }
        countAll()
      }
    }
  }
​
  /**
   * @description: 阻止事件冒泡
   * @param {*}
   * @return {*}
   * @author: Yu
   */
  function stopPropagation(e) {
    e = e || window.event
    if (e.stopPropagation) {
      //W3C阻止冒泡方法
      e.stopPropagation()
    } else {
      e.cancelBubble = true //IE阻止冒泡方法
    }
  }
​
  /**
   * @description: 生成唯一id
   * @param {*}
   * @return {*}
   * @author: Yu
   */
  function getUuid() {
    var s = []
    var hexDigits = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    for (var i = 0; i < 36; i++) {
      s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
    }
    s[14] = '4'
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
    s[8] = s[13] = s[18] = s[23] = '-'
    let uuid = s.join('')
    return uuid
  }
​
  /**
   * @description: 生成一个带文本的标签
   * @param {*} tagName
   * @param {*} Txt
   * @return {*}
   * @author: Yu
   */
  function createElementWithTxt(tagName, Txt) {
    // 创建元素节点(标签)
    var node = document.createElement(tagName)
    // 创建文本节点
    var oTxt = document.createTextNode(Txt)
    // 将文本节点放到元素节点中
    node.appendChild(oTxt)
    // 返回元素节点
    return node
  }
​
  /**
   * @description: 去空
   * @param {*} str
   * @return {*}
   * @author: Yu
   */
  function trim(str) {
    return str.replace(/(^\s*)|(\s*$)/g, '')
  }
})

3、index.css

*{
    padding: 0;
    margin: 0;
}
​
ul{
    list-style: none;
}
​
input[type='checkbox']{
    width: 15px;
    height: 15px;   
    vertical-align: middle;
}
​
body{
    font-size: 18px;
    color: #434544;
}
​
.contonter{
    overflow: hidden;
    width: 1200px;       
    margin: 10px auto; 
    border: 2px solid #E2E2E2;  
    box-sizing: border-box;  
    border-radius: 10px;
    padding: 20px;
}
​
header{
    width: 100%;
    height: 50px;
}
 
header input{
    width: 100%;
    height: 100%;
    padding-left: 10px;
    box-sizing: border-box;        
    outline: none;
    border: 1px solid #DFDFDF;
    border-radius: 5px;
}
​
section ul{
    margin: 30px 0;
    border: 1px solid #E9E9E9;
    border-radius: 5px;    
}
​
section ul li{
    position: relative;
    height: 45px;
    line-height: 45px;
    padding: 0 10px;
    box-sizing: border-box;
    border-bottom: 1px solid #E9E9E9;  
    border-radius:5px;  
}
​
section ul:nth-last-child(1){
    border-bottom: none;
}
​
section ul li:hover{
    background-color: rgb(235, 234, 234);        
    cursor: pointer;
}
​
section ul li input{
    margin-right: 10px;
}
​
section ul li button{
    position: absolute;
    top: 10px;
    right: 10px;
    height: 30px;
    padding: 0 10px;
    line-height: 30px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
    cursor: pointer;
}
​
footer{
    width: 100%;
    height: 50px;
    float: left;
    line-height: 50px;
}
​
.footer_left{    
    float: left;
    padding-left: 10px;
    box-sizing: border-box; 
}
​
.footer_left input{    
    margin-right: 20px;    
}
​
.footer_right{
    float: right;
}
​
.footer_right button{
    padding: 0 20px;
    height: 40px;
    line-height: 40px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
    cursor: pointer;
}

5、原生js升级版

;(function () {
    // 获取元素
    const todoData = document.querySelector('#ipt');
    const todoList = document.querySelector('#lis');
    const emptyData = document.querySelector('.empty'); 
    const delTodoBtn = document.querySelector('#del_done');
    const allNumber = document.querySelector('.all');
    const doneNumber= document.querySelector('.done');
    let todoItems = document.querySelectorAll('.todo-item');
    const todoDone = document.querySelector('#done')
​
    // 已完成的任务
    let done=0;
​
    // 执行方法
    delTodoAllItem();  
    checkTodoItem(); 
    doneTaskNumber();
    allTaskNumber();
​
    /* 添加任务 */
    todoData.onkeypress = (event) => {
      if (event.which === 13) {
        if (todoData.value.trim() == '')  return alert('请输入任务名称')
        addTodoItem();
        delTodoItem();
        checkTodoItem();
        doneTaskNumber();
        allTaskNumber();
      }      
    }
    
    /* 添加任务项 */
    function addTodoItem(){
        let li = document.createElement('li');
        let input = document.createElement('input');
        let button = document.createElement('button');
        const todoContent = document.createTextNode(todoData.value.trim());
        const btnDelete = document.createTextNode('删除');
        button.appendChild(btnDelete);
        button.classList.add('del-item');
        input.setAttribute('type', 'checkbox')
        li.appendChild(input);
        li.appendChild(todoContent);
        li.appendChild(button);
        li.classList.add('todo-item');
        todoList.insertBefore(li, todoList.children[0]);
        todoData.value = '';
        emptyData.style.display = 'none';
        todoItems = document.querySelectorAll('.todo-item');
        todoDone.checked=false;
    }
​
    /* 删除任务项 */
    function delTodoItem(){
        const delTodo = document.querySelector('.del-item');
        delTodo.addEventListener('click',()=>{
            if (!confirm('确认要删除该任务吗?')) return
            delTodo.parentNode.remove();
            doneTaskNumber();
            allTaskNumber();
            if (todoItems.length === done) {
              todoDone.checked = true
            }
            if (todoList.children.length!= 1) return
            emptyData.style.display = 'block';
            todoDone.checked = false;
        })
    }
​
    /* 删除任务列表 */
    function delTodoAllItem(){       
        delTodoBtn.addEventListener('click',()=>{
            if (done <= 0) return alert('请先添加任务')
            if (!confirm('确认要删除已完成任务吗?')) return
            todoItems.forEach(v=>{
                if (v.querySelector('input').checked) {
                    v.remove()
                }
            })            
            doneTaskNumber();
            allTaskNumber();            
            if (todoList.children.length != 1) return
            emptyData.style.display = 'block';
            todoDone.checked = false;
        })
    }
​
    /* 全选反选 */
    function checkTodoItem(){
        let todoItemsInput = document.querySelectorAll('.todo-item input');
        const todoCheck = document.querySelector('.todo-item input');        
        todoDone.addEventListener('click',()=>{
            if (todoItemsInput.lenth === 0) return
            todoItemsInput.forEach((v) => (v.checked = todoDone.checked))
            doneTaskNumber();  
        })
        if (!todoCheck) return
        todoCheck.addEventListener('click', () => {
            todoItemsInput = document.querySelectorAll('.todo-item input');
            for (let i = 0; i < todoItemsInput.length; i++) {
              todoDone.checked = true
              if (!todoItemsInput[i].checked) {
                todoDone.checked = false;
                break;
              }
            }             
            doneTaskNumber();                     
        })        
    }
​
    /* 已完成数量 */
    function doneTaskNumber(){
        done = 0;        
        const todoItemsInput = document.querySelectorAll('.todo-item input');
        todoItemsInput.forEach(v=>{
            if(v.checked) done++
        })
        doneNumber.innerText=done
    }
​
    /* 全部任务数量 */
    function allTaskNumber(){
        todoItems = document.querySelectorAll('.todo-item');
        allNumber.innerText = todoItems.length;
    }
})();

三、使用jQuery实现ToDoList

1、index.html

<!--
 * @Description: jQuery实现
 * @Version: 1.0
 * @Autor: Yu
 * @Date: 2022-01-02 15:18:35
 * @LastEditors: Yu
 * @LastEditTime: 2022-07-30 16:19:28
-->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ToDoList</title>
    <!-- 引入css文件 -->
    <link rel="stylesheet" href="./css/index.css">   
  </head>
  <body>
    <div class="contonter">
      <!-- 头部开始 -->
      <header>
          <input type="text" id="ipt" placeholder="请输入你的任务名称,按回车键确认"/>
      </header>
      <!-- 头部结束 -->
​
      <!-- 内容部分开始 -->
      <section>
          <ul id="lis">
              <li class="empty">暂无数据</li>
          </ul>          
      </section>
      <!-- 内容部分结束 -->
​
      <!-- 底部开始 -->
      <footer>
          <div class="footer_left">
              <input type="checkbox" id="done"/>已完成<span class="done">0</span>/全部<span class="all">0</span>
          </div>
          <div class="footer_right">
              <button id="del_done">清除已完成任务</button>
          </div>
      </footer>
      <!-- 底部结束 -->
    </div>
     <!-- 引入js文件 -->
    <script src="./lib/jquery-3.2.0.min.js"></script>
    <script src="./js/index.js"></script>
  </body>
</html>

2、index.css

*{
    padding: 0;
    margin: 0;
}
​
ul{
    list-style: none;
}
​
input[type='checkbox']{
    width: 15px;
    height: 15px;   
    vertical-align: middle;
}
​
body{
    font-size: 18px;
    color: #434544;
}
​
.contonter{
    overflow: hidden;
    width: 1200px;       
    margin: 10px auto; 
    border: 2px solid #E2E2E2;  
    box-sizing: border-box;  
    border-radius: 10px;
    padding: 20px;
}
​
header{
    width: 100%;
    height: 50px;
}
 
header input{
    width: 100%;
    height: 100%;
    padding-left: 10px;
    box-sizing: border-box;        
    outline: none;
    border: 1px solid #DFDFDF;
    border-radius: 5px;
}
​
section ul{
    margin: 30px 0;
    border: 1px solid #E9E9E9;
    border-radius: 5px;    
}
​
section ul li{
    position: relative;
    height: 45px;
    line-height: 45px;
    padding: 0 10px;
    box-sizing: border-box;
    border-bottom: 1px solid #E9E9E9;  
    border-radius:5px;  
}
​
section ul:nth-last-child(1){
    border-bottom: none;
}
​
section ul li:hover{
    background-color: rgb(235, 234, 234);        
    cursor: pointer;
}
​
section ul li input{
    margin-right: 10px;
}
​
section ul li button{
    position: absolute;
    top: 10px;
    right: 10px;
    height: 30px;
    padding: 0 10px;
    line-height: 30px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
    cursor: pointer;
}
​
footer{
    width: 100%;
    height: 50px;
    float: left;
    line-height: 50px;
}
​
.footer_left{    
    float: left;
    padding-left: 10px;
    box-sizing: border-box; 
}
​
.footer_left input{    
    margin-right: 20px;    
}
​
.footer_right{
    float: right;
}
    
.footer_right button{
    padding: 0 20px;
    height: 40px;
    line-height: 40px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
    cursor: pointer;
}

3、index.js

$(function () {
  /* 输入框填写任务名称按回车添加任务 */
  $('#ipt').keyup(function (event) {
    if (event.keyCode == 13) {
      if ($('#ipt').val().trim() === '') return alert('请输入任务名称')
      addTodoItem()  
      delTodoItem() 
      isCheckedToDo() 
      doneTaskNumber()
      allTaskNumber()
      delAllTask()
    }
  })  
})

/* 添加任务项 */
function addTodoItem() {  
  const li = `
  <li class="todo-item">
    <input type="checkbox">
    ${$('#ipt').val().trim()}
    <button class="del-item">删除</button>
  </li>`
  $('#lis').prepend(li)
  $('#ipt').val('')
  $('.empty').css('display', 'none')
  $('#done').prop('checked', false)
}

/* 删除任务项 */
function delTodoItem() {
  $('.del-item').off('click')
  $('.del-item').on('click', function () {
    if (!confirm('确认要删除该任务吗?')) return
    $(this).parent().remove()
    doneTaskNumber()
    allTaskNumber()  
    if ($('.todo-item input').length === $('.todo-item input:checked').length)
    $('#done').prop('checked', true)
    if ($('#lis').children().length != 1) return
    $('.empty').css('display', 'block')
    $('#done').prop('checked', false)
  })  
}

/* 全选和反选 */
function isCheckedToDo(){    
    $('#done').click(function () {
      doneTaskNumber()
      $('.todo-item input').each(function () {
        $(this).prop('checked', $('#done').prop('checked'))
      })
    })

    iptArr = $('.todo-item input')
    iptArr.click(function () {
      doneTaskNumber()
      for (let i = 0; i < iptArr.length; i++) {
        $('#done').prop('checked', true)
        if (!iptArr[i].checked) {
          $('#done').prop('checked', false)
          break
        }
      }
    })             
}

/* 已完成任务 */
function doneTaskNumber(){
    let count = 0;
    count = $('.todo-item input:checked').length;
    $('.done').text(count);
}

/* 全部任务 */
function allTaskNumber(){     
    $('.all').text($('.todo-item input').length) 
}

/* 清除所有选中任务 */
function delAllTask(){
    $('#del_done').off('click')    
    $('#del_done').click(function(){
        if ($('.todo-item input').length <= 0) return alert('请先添加任务')
        if ($('.todo-item input:checked').length <= 0 )
          return alert('还没有已完成任务')
        if (!confirm('确认要删除该任务吗?')) return
        $('.todo-item input:checked').parent().remove()
        doneTaskNumber()
        allTaskNumber()
        if ($('#lis').children().length != 1) return
        $('.empty').css('display', 'block')
        $('#done').prop('checked', false)
    })    
}

四、使用vue实现ToDoList

1、使用vue-cli创建项目

注意使用vue3

2、App

<template>
  <TodoList/>
</template>

<script>
import TodoList from './views'

export default {
  name: 'App',
  components: {
    TodoList
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}

*{
    padding: 0;
    margin: 0;
}

ul{
    list-style: none;
}

input[type='checkbox']{
    width: 15px;
    height: 15px;   
    vertical-align: middle;
}

body{
    font-size: 18px;
    color: #434544;
}
</style>

3、header

<template>
    <header>
        <input type="text" id="ipt" v-model.trim="userTask" @keyup.enter="addToDoList" placeholder="请输入你的任务名称,按回车键确认"/>       
    </header>
</template>

<script>
import { ref } from 'vue'

export default {
    name:'Header',
    setup(props,{emit}) {
       let userTask=ref('')

       /* 添加任务 */
       function addToDoList(){
         if(userTask.value==='') return alert('请输入任务名称')
         emit('userTask', userTask)
         userTask.value = ''                  
       }     

       return{
        userTask,
        addToDoList
       }
    }
}
</script>

<style scoped>
header{
    width: 100%;
    height: 50px;
}
 
header input{
    width: 100%;
    height: 100%;
    padding-left: 10px;
    box-sizing: border-box;        
    outline: none;
    border: 1px solid #DFDFDF;
    border-radius: 5px;
}
</style>

4、list

<template>
    <section>
        <ul id="lis">
            <Item @delToDoListItemById='delToDoListItemById' @changeCheckedToDoItem='changeCheckedToDoItem'/>            
        </ul>          
    </section>
</template>

<script>
import Item from '@/components/item'

export default {
    name:'List',
    components:{
        Item
    },
    setup(props,{emit}) {

      /* 删除当前任务 */
      function delToDoListItemById(delToDoListItemById){
        emit('delToDoListItemById', delToDoListItemById)
      } 

      /* 改变当前任务选中状态 */
      function changeCheckedToDoItem(changeCheckedToDoItem){
         emit('changeCheckedToDoItem', changeCheckedToDoItem)
      }     

      return{        
        delToDoListItemById,
        changeCheckedToDoItem
      }
    }
}
</script>

<style scoped>
section ul{
    margin: 30px 0;
    border: 1px solid #E9E9E9;
    border-radius: 5px;    
}
</style>

5、item

<template>
  <div>
    <li v-if="userTaskItem.length===0" class="empty">暂无数据</li>
    <li v-else class="todo-item"  v-for="item in userTaskItem" :key="item.id">
        <input type="checkbox" @change="changeCheckedToDoItem(item.id)"  :checked='item.done'> {{item.value}} <button class="del-item" @click="delToDoListItemById(item.id)">删除</button>
    </li>   
  </div>
</template>

<script>
import { inject } from 'vue'

export default {
    name:'Item',
    setup(props,{emit}) {
      let userTaskItem = inject('userTaskItem')

      /* 删除当前任务 */
      function delToDoListItemById(id){
        if (!confirm('确认要删除该任务吗?')) return
        emit('delToDoListItemById', id)        
      }  

      /* 改变当前任务选中状态 */    
      function changeCheckedToDoItem(id){
        emit('changeCheckedToDoItem', id)
      }

      return{
        userTaskItem,
        delToDoListItemById,
        changeCheckedToDoItem
      }
    }
}
</script>

<style scoped>
section ul li{
    position: relative;
    height: 45px;
    line-height: 45px;
    padding: 0 10px;
    box-sizing: border-box;
    border-bottom: 1px solid #E9E9E9;  
    border-radius:5px;  
}

section ul li:last-of-type{
    border-bottom: none;
}

section ul:nth-last-child(1){
    border-bottom: none;
}

section ul li:hover{
    background-color: rgb(235, 234, 234);        
    cursor: pointer;
}

section ul li input{
    margin-right: 10px;
}

section ul li button{
    position: absolute;
    top: 10px;
    right: 10px;
    height: 30px;
    padding: 0 10px;
    line-height: 30px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
    cursor: pointer;
}
</style>>

6、footer

<template>
    <footer>
        <div class="footer_left">
            <input ref="myRef" type="checkbox" id="done" @click="checkedAllToDoList"/>已完成<span class="done">{{ doneTaskNumber }}</span>/全部<span class="all">{{userTaskItem.length}}</span>
        </div>
        <div class="footer_right">
            <button id="del_done" @click="delAllDoneTask">清除已完成任务</button>
        </div>
    </footer>
</template>

<script>
import { inject,ref,computed,watch } from 'vue'

export default {
    name:'Footer',
    setup(props,{emit}) {
      let userTaskItem = inject('userTaskItem')
      const myRef = ref(null);      

      /* 全选 */ 
      function checkedAllToDoList(){
        emit('checkedAllToDoList', myRef.value.checked)        
      }

      /* 已完成任务数量 */  
      const doneTaskNumber = computed(() => {
       return userTaskItem.value.filter(v=>v.done===true).length 
      })

      /* 判断是否全选 */  
      watch(doneTaskNumber, (newValue) => {
        if(newValue!==0 && newValue===userTaskItem.value.length){
            myRef.value.checked=true
        }else{
            myRef.value.checked=false
        }
      })

      /* 添加任务时重置全选为false */
      watch(userTaskItem, (newValue) => {
        if(newValue.length>doneTaskNumber.value){
            myRef.value.checked=false
        }else if(newValue.length!==0 && newValue.length===doneTaskNumber.value){
            myRef.value.checked=true
        }else{
            myRef.value.checked=false 
        }
      },{deep:true})


      /* 清除所有已完成的任务 */
      function delAllDoneTask(){
        if(userTaskItem.value.length===0) return
        if (!confirm('确认要删除该任务吗?')) return
        emit('delAllDoneTask', delAllDoneTask)        
      }

      return{
        userTaskItem,
        doneTaskNumber,
        checkedAllToDoList,
        myRef,
        delAllDoneTask        
      }
    }
}
</script>

<style scoped>
footer{
    width: 100%;
    height: 50px;
    float: left;
    line-height: 50px;
}

.footer_left{    
    float: left;
    padding-left: 10px;
    box-sizing: border-box; 
}

.footer_left input{    
    margin-right: 20px;    
}

.footer_right{
    float: right;
}
    
.footer_right button{
    padding: 0 20px;
    height: 40px;
    line-height: 40px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
    cursor: pointer;
}
</style>

7、index

<template>
  <div class="contonter">
    <Header @userTask="userTask"/>
    <List @delToDoListItemById='delToDoListItemById' @changeCheckedToDoItem='changeCheckedToDoItem'/>
    <Footer @delAllDoneTask='delAllDoneTask' @checkedAllToDoList='checkedAllToDoList'/>
  </div>
</template>

<script>
import { provide, ref } from 'vue'
import Header from '@/components/header'
import List from '@/components/list'
import Footer from '@/components/footer'

export default {
    components:{
      Header,
      List,
      Footer
    },
    setup() {
       let userTaskItem=ref([])

       /* 添加任务 */
       function userTask(userTask){
            userTaskItem.value.unshift({
                id:userTaskItem.value.length+1,
                value:userTask.value,
                done:false
            })   
        }     

        /* 删除当前任务 */
        function delToDoListItemById(id){
            userTaskItem.value=userTaskItem.value.filter(v=>v.id!==id)
        }    

        /* 改变当前任务选中状态 */
        function changeCheckedToDoItem(id){ 
            userTaskItem.value.forEach(v=>{
                if (v.id===id) {
                    v.done=!v.done
                }
            }) 
        } 

        /* 清除所有已完成的任务 */
        function delAllDoneTask(){
            userTaskItem.value=userTaskItem.value.filter(v=>v.done===false)
        }

        /* 全选 */ 
        function checkedAllToDoList(done){
            userTaskItem.value.filter(v=>v.done = done)
        }


        provide('userTaskItem',userTaskItem)

        return{
            userTask,
            delToDoListItemById,
            changeCheckedToDoItem,
            delAllDoneTask,
            checkedAllToDoList            
        }
    }
}
</script>

<style  scoped>
.contonter{
    overflow: hidden;
    width: 1200px;       
    margin: 10px auto; 
    border: 2px solid #E2E2E2;  
    box-sizing: border-box;  
    border-radius: 10px;
    padding: 20px;
}
</style>>

五、使用react实现ToDoList

1、创建react脚手架

  • 检查npm和node是否安装

  • npm install -g create-react-app

  • create-react-app hello-react

  • cd hello-react

  • npm start或者yarn start

2、如下载慢请将npm或yarn镜像切换成taobao镜像

NPM CONFIG SET REGISTRY HTTPS://REGISTRY.NPM.TAOBAO.ORG
// 配置后可通过下面方式来验证是否成功
npm config get registry
//安装yarn,将yarn镜像切换为淘宝镜像
npm i -g -yarn
yarn --vresion
// 查看下载源
yarn config get registry
// 更换为淘宝源
yarn config set registry  https://registry.npm.taobao.org

3、App

import React, { Component } from 'react'
import Header from './components/header'
import List from './components/list'
import Footer from './components/footer'
import './App.css'

export default class App extends Component {
  state = {
    todos: [
      { id: 1, name: '吃饭', done: true },
      { id: 2, name: '睡觉', done: false },
      { id: 3, name: '打代码111', done: true },
    ],
  }

  /**
   * @description: 添加item
   * @param {*}
   * @return {*}
   * @author: Yu
   */
  addListItem = (value) => {
    let { todos } = this.state
    let newTodos = { id: todos.length + 1, name: value, done: false }
    this.setState({todos: [newTodos, ...todos] })
  }

    /**
     * @description: 修改状态
     * @param {*}
     * @return {*}
     * @author: Yu
     */
    updataDone=(id,done)=>{
        const newTodos= this.state.todos.map(v=>{
            if(v.id===id){
                return {...v,done}
            }
            return v
        })
        this.setState({ todos: newTodos })
    }

    /**
     * @description: 删除item
     * @param {*}
     * @return {*}
     * @author: Yu
     */    
    removeListItem=(id)=>{
        const newTodos= this.state.todos.filter(v=>{
            return v.id!==id
        })
        this.setState({
            todos:newTodos
        })
    }

    /**
     * @description: 全选
     * @param {*}
     * @return {*}
     * @author: Yu
     */    
    checkedAll=(done)=>{
        const newTodos=this.state.todos.map(v=>{
            return {...v,done}
        })
        this.setState({
          todos: newTodos
        })
    }

    /**
     * @description: 删除所有选中的内容
     * @param {*}
     * @return {*}
     * @author: Yu
     */    
    removeAllChecked=()=>{
        const newTodos=this.state.todos.filter(v=>!v.done)
        this.setState({
            todos:newTodos
        })
    }

  render() {
    return (
      <div className="contonter">
        <Header addListItem={this.addListItem}></Header>
        <List
          todos={this.state.todos}
          updataDone={this.updataDone}
          removeListItem={this.removeListItem}
        ></List>
        <Footer
          todos={this.state.todos}
          checkedAll={this.checkedAll}
          removeAllChecked={this.removeAllChecked}
        ></Footer>
      </div>
    )
  }
}
*{
    padding: 0;
    margin: 0;
}

ul{
    list-style: none;
}

input[type='checkbox']{
    width: 15px;
    height: 15px;   
    vertical-align: middle;
}

body{
    font-size: 18px;
    color: #434544;
}

.contonter{
    overflow: hidden;
    width: 1200px;       
    margin: 10px auto; 
    border: 2px solid #E2E2E2;  
    box-sizing: border-box;  
    border-radius: 10px;
    padding: 20px;
}

4、header

import React, { Component } from 'react'
import PropType from 'prop-types'
import './index.css'

export default class index extends Component {
    
    static propTypes={
        addListItem:PropType.func.isRequired
    }

    /**
     * @description: 监听键盘按下事件
     * @param {*}
     * @return {*}
     * @author: Yu
     */    
    handleKeyUp=(event)=>{
        const {target,keyCode}=event
        //判断是否按得是回车键
        if (keyCode!==13) return
        //判断用户有没有输入值
        if (target.value.trim()==='') {
            alert("您输入的值不能为空,请重新输入!")
            return
        }
        //将用户输入的值传递过去
        this.props.addListItem(target.value)
        //将用户输入的值清空
        target.value=""
    }

    render() {
        return (           
            <header>
                <input type="text" onKeyUp={this.handleKeyUp} placeholder="请输入你的任务名称,按回车键确认"/>
            </header>     
        )
    }
}
header{
    width: 100%;
    height: 50px;
}
 
header input{
    width: 100%;
    height: 100%;
    padding-left: 10px;
    box-sizing: border-box;        
    outline: none;
    border: 1px solid #DFDFDF;
    border-radius: 5px;
}

5、list

import React, { Component } from 'react'
import PropType from 'prop-types'
import Item from '../item'
import './index.css'

export default class index extends Component {   

    static propTypes={
        todos:PropType.array.isRequired,
        updataDone:PropType.func.isRequired,
        removeListItem:PropType.func.isRequired
    }

    render() { 
        const {todos,updataDone,removeListItem}=this.props    

        return (
            <section>
                <ul className={todos.length>0?'empty':''}>
                    {
                        todos.map(v=>{
                            return <Item key={v.id} {...v} updataDone={updataDone} removeListItem={removeListItem} ></Item>
                        })
                    }
                </ul>
            </section>
        )
    }
}
section ul{
    margin: 30px 0;     
}

.empty{
    border: 1px solid #E9E9E9;
    border-radius: 5px;   
}

6、item

import React, { Component } from 'react'
import './index.css'

export default class index extends Component {
    state={
        mouse:false
    }

    /**
     * @description: 鼠标移入移出
     * @param {*}
     * @return {*}
     * @author: Yu
     */    
    mouseInOut=(flag)=>{
        return ()=>{
            this.setState({
                mouse:flag
            })
        }
    }

    /**
     * @description: updataDone
     * @param {*}
     * @return {*}
     * @author: Yu
     */  
    updataDone=(id)=>{
        return (event)=>{
            this.props.updataDone(id,event.target.checked)
        }
    }  

    /**
     * @description: removeListItem
     * @param {*}
     * @return {*}
     * @author: Yu
     */  
    removeListItem=(id)=>{
        return ()=>{
            if (window.confirm("确认删除这个任务吗?")) {
                this.props.removeListItem(id)
            }
        }
    }  

    render() {
        const {id,name,done} = this.props
        const {mouse} = this.state

        return (
             <li onMouseLeave={this.mouseInOut(false)} onMouseEnter={this.mouseInOut(true)}>
                 <input type="checkbox" checked={done} onChange={this.updataDone(id)}/>{name}<button onClick={this.removeListItem(id)} style={{display:mouse?'block':'none'}}>删除</button>
             </li>
        )
    }
}
section ul li{
    position: relative;
    height: 45px;
    line-height: 45px;
    padding: 0 10px;
    box-sizing: border-box;
    border-bottom: 1px solid #E9E9E9;    
}

section ul li:last-of-type{
    border-bottom: none;
}

section ul li:hover{
    background-color: rgb(235, 234, 234);        
    cursor: pointer;
}

section ul li input{
    margin-right: 10px;
}

section ul li button{
    position: absolute;
    top: 10px;
    right: 10px;
    height: 30px;
    padding: 0 10px;
    line-height: 30px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
}

7、footer

import React, { Component } from 'react'
import './index.css'

export default class index extends Component {
    /**
     * @description: 全选
     * @param {*}
     * @return {*}
     * @author: Yu
     */    
    checkedAll=(event)=>{
         this.props.checkedAll(event.target.checked)
    }

    /**
     * @description: 清除所有
     * @param {*}
     * @return {*}
     * @author: Yu
     */    
    removeAllChecked=()=>{
        this.props.removeAllChecked()
    }

    render() {    
        const {todos}=this.props
        const sum=todos.reduce((pre,item)=>{
            if(item.done){
                pre++
            }
            return pre            
        },0)       

        return (
            <footer>
                <div className="footer_left">
                    <input onChange={this.checkedAll} type="checkbox" checked = {sum === todos.length && todos.length!==0?true:false}/>已完成<span className="done">{sum}</span>/全部<span className="all">{todos.length}</span>
                </div>
                <div className="footer_right">
                    <button onClick={this.removeAllChecked}>清除已完成任务</button>
                </div>
            </footer>
        )
    }
}
footer{
    width: 100%;
    height: 50px;
    float: left;
    line-height: 50px;
}

.footer_left{    
    float: left;
    padding-left: 10px;
    box-sizing: border-box; 
}

.footer_left input{    
    margin-right: 20px;   
    cursor: pointer; 
}

.footer_right{
    float: right;
}

.footer_right button{
    padding: 0 20px;
    height: 40px;
    line-height: 40px;
    color: #fff;
    background-color: #D5514C;
    border-radius: 5px;
    border: none;
    cursor: pointer;
}

六、效果

帅哥美女点个赞吧,源码:ToDoList: 使用不同版本实现方式来实现ToDoList

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值