美文网首页JavaScript
纯JS实现TodoList

纯JS实现TodoList

作者: h2coder | 来源:发表于2023-07-13 19:13 被阅读0次

    效果图

    代办事项.png

    列表条目样式

    <li>
      <div>
        <input class="chk" type="checkbox">
        <span class="title">事项1</span>
      </div>
      <button>X</button>
    </li>
    

    根据数据,渲染列表页面

    // 渲染函数,根据数据渲染页面
    function render() {
        // 数据转标签字符串数组
        let newArr = todos.map(function (item, index) {
            return `
              <li>
                <div>
                  <input data-index="${index}" class="chk" type="checkbox" ${item.finished ? "checked" : ""}>
                  <span class="title">${item.title}</span>
                </div>
                <button data-index="${index}">X</button>
              </li>
            `;
        });
        // 拼接每个标签
        let html = newArr.join("");
        // 渲染列表
        ul.innerHTML = html;
    
        // 统计未完成数量
        let unfinishCountValue = 0;
        // 统计完成数量
        let finishCountValue = 0;
        todos.forEach(function (item, index) {
            if (item.finished) {
                finishCountValue++;
            } else {
                unfinishCountValue++;
            }
        });
    
        console.log(`${unfinishCountValue}个未完成`);
        console.log(`${finishCountValue}个已完成`);
    
        unfinishCount.innerText = unfinishCountValue;
        finishCount.innerText = finishCountValue;
    }
    
    // 首次渲染
    render();
    

    处理键盘事件

        // 监听输入框的键盘事件
        wordInput.addEventListener('keyup', function (e) {
        // 限定只处理回车
        if (e.key === 'Enter') {
        // 添加代办事项到数组中
        let word = wordInput.value;
    
        // 没有输入内容,不处理
        if (word.trim() === '') {
        return;
        }
    
        // 插入到数组的最前面
        todos.unshift({
        title: word,
        finished: false
        });
    
        // 清空输入框
        wordInput.value = '';
    
        // 同步数据到本地
        syncData2Local();
        // 重新渲染
        render();
        }
    });
    

    处理条目点击

    // 事件委托,处理条目内的点击
    ul.addEventListener('click', function (e) {
      let targetEl = e.target;
      // 通过自定义属性,取出这一行数据的索引
      let index = targetEl.dataset.index;
      // 勾选框
      if (targetEl.classList.contains('chk')) {
        let todo = todos[index];
        // 切换选中和未选中
        todo.finished = !todo.finished;
        // 同步数据到本地
        syncData2Local();
        // 重新渲染
        render();
      } else if (targetEl.tagName === 'BUTTON') {
        // 删除数组中的数据
        todos.splice(index, 1);
        // 同步数据到本地
        syncData2Local();
        // 重新渲染
        render();
      }
    });
    

    清理已完成的事项

    // 清理已完成事项
    cleanFinishLink.addEventListener('click', function (e) {
      // 阻止默认行为
      e.preventDefault();
      // 是否有一个已完成的代办事项
      let hasFinished = false;
      // 删除所有已勾选的事项
      for (let i = todos.length - 1; i >= 0; i--) {
        const todo = todos[i];
        if (todo.finished) {
          hasFinished = true;
          todos.splice(i, 1);
        }
      }
      // 如果没有可清理的已完成代办事项,直接弹窗提示
      if (!hasFinished) {
        alert('没有一个已完成的代办事项可清理');
        return;
      }
      // 同步数据到本地
      syncData2Local();
      // 重新渲染
      render();
    });
    

    完整代码

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8" />
      <meta name="viewport"
        content="width=device-width, initial-scale=1.0,maximum-scale=1,minimum-scale=1,user-scalable=no" />
      <title>04-待办列表.html</title>
      <style>
        * {
          margin: 0;
          padding: 0;
          box-sizing: border-box;
        }
    
        body {
          background-color: #ccc;
        }
    
        ul {
          list-style: none;
        }
    
        li {
          padding: 20px;
          text-align: left;
          font-size: 30px;
          border-bottom: 1px dashed #ccc;
          display: flex;
          justify-content: space-between;
          align-items: center;
        }
    
        li input {
          margin-right: 10px;
        }
    
        li button {
          display: none;
          padding: 5px;
        }
    
        li:hover button {
          display: inline-block;
          cursor: pointer;
        }
    
        /* 兄弟选择器,chk是给checkbox加的类名,当checkbox勾选时,让兄弟元素span加上中划线 */
        .chk:checked+span {
          text-decoration: line-through;
        }
    
        h1 {
          margin-bottom: 10px;
        }
    
        /* .chk+span {} */
    
        .box {
          background-color: #fff;
          width: 60vw;
          padding: 20px 20px 0;
          margin: 50px auto;
        }
    
        .box .tool input {
          width: 100%;
          height: 50px;
          text-indent: 20px;
          font-size: 20px;
          font-style: italic;
          color: #666;
          font-weight: 700;
        }
    
        section {
          height: 50px;
          display: flex;
          justify-content: space-between;
          align-items: center;
        }
    
        a {
          text-decoration-color: #666;
          color: inherit;
        }
      </style>
    </head>
    
    <body>
      <div id="app" data-v-app="">
        <div class="box">
          <h1>待办列表</h1>
          <div class="tool">
            <input id="word" autofocus="" type="text" placeholder="请输入代办事项" />
          </div>
          <ul>
            <!-- 
              <li>
                <div>
                  <input class="chk" type="checkbox">
                  <span class="title">事项1</span>
                </div>
                <button>X</button>
              </li>
           -->
          </ul>
          <section>
            <span><i>0</i> 未完成</span><a href="#">清理 <b>0</b> 已完成</a>
          </section>
        </div>
      </div>
      <script>
        /*
          整个页面分为4个功能,它整体的操作流程如下
            1. 第一是输入框,用于给用户填写具体的待办事项,比如吃饭、睡觉等
            2. 第二是切换待办状态,可以实现**已完成**和**未完成**的状态切换
            3. 第三是删除功能,用户可以点击对应的一个待办选项,实现删除功能
            4. 第四是统计功能,可以显示 **已完成**的待办数量,和 **未完成**的待办数量
            5. 第五是清理已完成功能,可以实现 用户移除 **已完成** 的待办事项
        */
        // 代办事项列表
        const ul = document.querySelector('ul');
        // 代办事项输入框
        const wordInput = document.querySelector('#word');
        // 未完成数量
        const unfinishCount = document.querySelector('section span i');
        // 已完成数量
        const finishCount = document.querySelector('section a b');
        // 清理已完成的a标签
        const cleanFinishLink = document.querySelector('section a');
    
        // 代办事项数据数组
        /*
          结构示例:
            {
              title: '吃饭',
              finished: false
            }
        */
        const todos = JSON.parse(localStorage.getItem('todos')) || [];
    
        // 同步数据到本地
        function syncData2Local() {
          localStorage.setItem('todos', JSON.stringify(todos));
        }
    
        // 渲染函数,根据数据渲染页面
        function render() {
          // 数据转标签字符串数组
          let newArr = todos.map(function (item, index) {
            return `
              <li>
                <div>
                  <input data-index="${index}" class="chk" type="checkbox" ${item.finished ? "checked" : ""}>
                  <span class="title">${item.title}</span>
                </div>
                <button data-index="${index}">X</button>
              </li>
            `;
          });
          // 拼接每个标签
          let html = newArr.join("");
          // 渲染列表
          ul.innerHTML = html;
    
          // 统计未完成数量
          let unfinishCountValue = 0;
          // 统计完成数量
          let finishCountValue = 0;
          todos.forEach(function (item, index) {
            if (item.finished) {
              finishCountValue++;
            } else {
              unfinishCountValue++;
            }
          });
    
          console.log(`${unfinishCountValue}个未完成`);
          console.log(`${finishCountValue}个已完成`);
    
          unfinishCount.innerText = unfinishCountValue;
          finishCount.innerText = finishCountValue;
        }
    
        // 监听输入框的键盘事件
        wordInput.addEventListener('keyup', function (e) {
          // 限定只处理回车
          if (e.key === 'Enter') {
            // 添加代办事项到数组中
            let word = wordInput.value;
    
            // 没有输入内容,不处理
            if (word.trim() === '') {
              return;
            }
    
            // 插入到数组的最前面
            todos.unshift({
              title: word,
              finished: false
            });
    
            // 清空输入框
            wordInput.value = '';
    
            // 同步数据到本地
            syncData2Local();
            // 重新渲染
            render();
          }
        });
    
        // 事件委托,处理条目内的点击
        ul.addEventListener('click', function (e) {
          let targetEl = e.target;
          // 通过自定义属性,取出这一行数据的索引
          let index = targetEl.dataset.index;
          // 勾选框
          if (targetEl.classList.contains('chk')) {
            let todo = todos[index];
            // 切换选中和未选中
            todo.finished = !todo.finished;
    
            // 同步数据到本地
            syncData2Local();
    
            // 重新渲染
            render();
          } else if (targetEl.tagName === 'BUTTON') {
            // 删除数组中的数据
            todos.splice(index, 1);
    
            // 同步数据到本地
            syncData2Local();
            // 重新渲染
            render();
          }
        });
    
        // 清理已完成事项
        cleanFinishLink.addEventListener('click', function (e) {
          // 阻止默认行为
          e.preventDefault();
    
          // 是否有一个已完成的代办事项
          let hasFinished = false;
    
          // 删除所有已勾选的事项
          for (let i = todos.length - 1; i >= 0; i--) {
            const todo = todos[i];
            if (todo.finished) {
              hasFinished = true;
              todos.splice(i, 1);
            }
          }
    
          // 如果没有可清理的已完成代办事项,直接弹窗提示
          if (!hasFinished) {
            alert('没有一个已完成的代办事项可清理');
            return;
          }
    
          // 同步数据到本地
          syncData2Local();
          // 重新渲染
          render();
        });
    
        // 首次渲染
        render();
      </script>
    </body>
    
    </html>
    

    相关文章

      网友评论

        本文标题:纯JS实现TodoList

        本文链接:https://www.haomeiwen.com/subject/ngnmudtx.html