jQuery案例 —— todoList待办事项列表

案例时间

这个案例前几天就看了,一直没时间作,今天就把它作出来。这个案例是一个待办事项列表,能够记录本身当天须要作的事情,完成一件就画勾,要是不想要了就删除,能够很好的督促本身完成天天的学习任务啊。看下效果吧。javascript

在这里插入图片描述

当我第一次看到这个效果时,从局部出发,个人第一反应就是点击事件的删除和添加操做。可是看视频的时候老师作了需求分析,很仔细的分析了案例的效果,事实证实并无我想的那么简单。确实是添加删除元素的操做,只是对象变了,不是父元素创造元素再添加元素的步骤,而是对存储的操做,最后把存储中的数据渲染到页面。第一次听渲染这个词,懂,也不懂。后来看完整个过程我简单的懂了渲染的意思。仍是菜鸟一个。css

数据存储格式

数据存储的格式html

var todoList = [{title:“123”,done:false},{title:“今天学习jQuery”,done:false}]java

创造的元素里包含三部分:复选框、表单输入的内容、删除按钮。jquery

可是咱们存储数据的只须要两部分,就是复选框的状态和表单输入的内容。
在存储数据时,设置对象的两个属性:文本内容和复选框状态。默认复选框的状态是没选中的,由于新建立的元素是待办的。复选框选中(true)是再已完成部分,没选中(false)是待办。web

可是咱们又会创造不少这样的元素,因此就把这些对象放到数组中存储,经过数组的索引号访问。数组

刷新页面数据不丢失 —— 本地存储

刷新页面数据不丢失,说明咱们的数据是存放在本地存储localStorage中的。
无论是当咱们点击复选框、点击删除键仍是在表单输入内容建立出来的元素,这些通通都是在本地存储中,页面中显示的数据都是从本地存储中取出来渲染的。
须要明白的是:本地储存的是字符串格式,咱们须要存储的是数组对象格式。因此咱们须要把数组对象格式转换为字符串格式存到本地存储,从本地存储取数据时再把字符串格式转换为数组对象格式。svg

数组对象格式转为字符串格式:JSON.stringify(todoList)
字符串格式转为数组对象格式:JSON.parse(stringDate)
学习

var toDo = [{
            title: "123",
            done: false
        }, {
            title: "今天学习jQuery",
            done: false
        }];
        console.log(toDo);
        console.log(toDo[0].title);
        localStorage.setItem("todo", toDo);
        console.log(localStorage.getItem("todo"));

        var stringDate = JSON.stringify(toDo);
        localStorage.setItem("todo2", stringDate);
        console.log(localStorage.getItem("todo2"));

看眼输出就明白了,中间箭头指向的是字符串类型的数据,就是本地存储中存储的数据格式。
在这里插入图片描述
在这里插入图片描述
上面的存储格式,都弄明白了,接下来就该总结一下需求吧。ui

需求分析

表单:输入内容,按enter键,创造元素,并添加到待办事件。
实质:按下enter键时,先声明一个数组用来接收保存本地存储中的数据。封装一个方法专门用来获取本地存储中的数据。再把建立的新元素追加到声明数组。而后把数组再赋值给本地储存,完成更新。最后把本地存储中的数据渲染到页面。设置本地存储的数据一样能够封装一个方法。

数据渲染页面:获取本地存储中的数据,把数据转为数组对象遍历数组,有几条数据就建立几个元素。判断对象done属性的值,true添加到已完成,false添加到待办。

复选框:点击更改复选框的状态,切换到待办或者已完成。实质:点击复选框,先获取本地存储数据,更改复选框的状态,更新本地存储数据,最后渲染页面。

删除键:点击删除当前任务。实质:获取本地存储数据,删除对应被点击的元素,更新本地存储数据,渲染页面。

须要封装的方法:获取本地存储数据、设置保存本地存储数据(更新)、渲染页面。

注意:在封装获取和设置本地存储数据方法时,注意数据格式的转换
JSON.stringify 转为字符串
JSON.parse 转为数组对象

代码部分

  1. 读取本地存储数据和保存设置本地存储数据 由于有不少地方须要读取和保存本地存储数据,单独封装成一个方法调用。
// 读取本地存储的数据
    function getLocalStorage() {
        var date = localStorage.getItem("toDoList");
        // 判断本地存储是否为空
        if (date != null) {
            return JSON.parse(date);
        } else {
            return []; // 为空返回空数组
        }
    }
    // 保存本地存储的数据
    function setLocalStorage(date) {
        localStorage.setItem("toDoList", JSON.stringify(date));
    }
  1. 渲染页面
    这里面要注意的点:
    a. 遍历中的本地存储数据,创造添加元素;
    b. 根据数据的属性判断元素添加的位置;
    c. 设定计数器,记录已完成和正在进行任务的个数;
    d. 设定删除键的索引号,以便找到对应的元素。
    这些需求不是一下在都写出来的,刚开始咱们可能只会想到第一点,写到后面才想到其余的点,再回来完善就能够了。这里我是把最终结果呈现出来了,方便看。
// 2. 把本地存储数据渲染到页面
    function loadPage() {
        // 2.1 先读取本地存储数据
        var date = getLocalStorage();
        // 遍历以前先清空页面中的ul和ol的内容 防止后面重复添加
        $("#todolist,#donelist").empty();
        // 记录已完成和进行中的个数
        var toDoCount = 0;
        var doneCount = 0;
        // 2.2 遍历本地存储数据 有多少个对象就建立多少个li
        $.each(date, function (i, ele) {
            // 根据复选框的状态,决定添加到那个部分
            // done为true 加到 donelist中
            if (ele.done) {
                // 自定义属性 id 记录数据在本地存储中的位置
                $("#donelist").prepend("<li><input type = 'checkbox' checked><p>" + ele.title + "</p><a href = '#' id=" + i + "></a></li>")
                // 更新个数
                doneCount++;
            } else {
                $("#todolist").prepend("<li><input type = 'checkbox'><p>" + ele.title + "</p><a href = '#' id=" + i + "></a></li>")
                toDoCount++;
            }
        })
        $("#todocount").text(toDoCount);
        $("#donecount").text(doneCount);
    }
  1. 删除按钮,删除操做
    这里就须要完善上面渲染页面中自定义属性的值了,经过删除键寻找到对应的元素,删除本地存储中的数据,完成更新。
    下面是添加任务项的结构,能够看出,a删除键与p是兄弟关系。能够经过a的索引号找到p,进而找到存储中p对应的title的对象,删除,完成更新数据的操做,最后渲染页面。
<li>
                <input type="checkbox">
                <p>123</p>
                <a href="#"></a>
            </li>
  1. 点击复选框 匹配对应的状态
// 4. 点击复选框 匹配状态
    // 本质是,点击更改本地存储数据中复选框状态,从新渲染页面
    $("#todolist,#donelist").on("click", "input", function () {
        // 读取本地存储数据
        var localDate = getLocalStorage();
        // 获取被点击的元素的索引
        var index = $(this).siblings("a").attr("id");
        // 修改数据
        localDate[index].done = $(this).prop("checked");
        // 更新本地数据
        setLocalStorage(localDate);
        // 从新渲染页面
        loadPage();
    })
  1. 统计个数的操做
    在渲染页面中操做,由于无论是添加、删除、变换状态都须要更新个数。
// 记录已完成和进行中的个数
        var toDoCount = 0;
        var doneCount = 0;
        // 2.2 遍历本地存储数据 有多少个对象就建立多少个li
        $.each(date, function (i, ele) {
            // 根据复选框的状态,决定添加到那个部分
            // done为true 加到 donelist中
            if (ele.done) {
                // 自定义属性 id 记录数据在本地存储中的位置
                $("#donelist").prepend("<li><input type = 'checkbox' checked><p>" + ele.title + "</p><a href = '#' id=" + i + "></a></li>")
                // 更新个数
                doneCount++;
            } else {
                $("#todolist").prepend("<li><input type = 'checkbox'><p>" + ele.title + "</p><a href = '#' id=" + i + "></a></li>")
                toDoCount++;
            }
        })
        $("#todocount").text(toDoCount);
        $("#donecount").text(doneCount);

完整JavaScript

$(function () {
    // 需求分析:
    // 在表单中输入内容,按enter键添加待办事项
    // 点击表单能够切换状态
    // 点击删除能够删除数据
    // 当刷新页面时,页面的数据不会丢失 —— localStorage存储方式
    // 这就意味着无论咱们点击的是什么都是对本地存储的数据进行操做
    // 操做完成后把本地存储的数据渲染到页面中
    // 一开始就要渲染页面 保证数据不丢失
    loadPage();
    // 1. 按回车键把数据添加到本地存储
    $("#title").on("keyup", function (e) {
        if (e.keyCode === 13) {
            if ($(this).val() === "") {
                alert("请输入您要输入的内容")
            } else {
                // 首先声明一个数组用来接收本地存储的数据
                var localDate = getLocalStorage();
                console.log(localDate);
                // 把新元素追加到本地存储
                localDate.push({
                    title: $(this).val(),
                    done: false
                });
                // 更新本地存储数据
                setLocalStorage(localDate);
                // 2. 渲染页面
                loadPage();
            }
            // 清空表单的内容
            $(this).val("");
        }
    })

    // 3. 删除按钮的操做
    // 实质是,删除的是本地存储中的数据,再从新渲染页面
    $("#todolist,#donelist").on("click", "a", function () {
        // 获取当前点击的索引号
        var index = $(this).attr("id");
        console.log(index);
        // 读取本地存储
        var localDate = getLocalStorage();
        // 删除对应的数据 splice(删除的其实位置,删除的个数)
        localDate.splice(index, 1);
        // 更新本地存储数据
        setLocalStorage(localDate);
        // 从新渲染页面
        loadPage();
    })

    // 4. 点击复选框 匹配状态
    // 本质是,点击更改本地存储数据中复选框状态,从新渲染页面
    $("#todolist,#donelist").on("click", "input", function () {
        // 读取本地存储数据
        var localDate = getLocalStorage();
        // 获取被点击的元素的索引
        var index = $(this).siblings("a").attr("id");
        // 修改数据
        localDate[index].done = $(this).prop("checked");
        // 更新本地数据
        setLocalStorage(localDate);
        // 从新渲染页面
        loadPage();
    })

    // 5. 统计个数 —— 渲染页面的时候操做

    // 2. 把本地存储数据渲染到页面
    function loadPage() {
        // 2.1 先读取本地存储数据
        var date = getLocalStorage();
        // 遍历以前先清空页面中的ul和ol的内容 防止后面重复添加
        $("#todolist,#donelist").empty();
        // 记录已完成和进行中的个数
        var toDoCount = 0;
        var doneCount = 0;
        // 2.2 遍历本地存储数据 有多少个对象就建立多少个li
        $.each(date, function (i, ele) {
            // 根据复选框的状态,决定添加到那个部分
            // done为true 加到 donelist中
            if (ele.done) {
                // 自定义属性 id 记录数据在本地存储中的位置
                $("#donelist").prepend("<li><input type = 'checkbox' checked><p>" + ele.title + "</p><a href = '#' id=" + i + "></a></li>")
                // 更新个数
                doneCount++;
            } else {
                $("#todolist").prepend("<li><input type = 'checkbox'><p>" + ele.title + "</p><a href = '#' id=" + i + "></a></li>")
                toDoCount++;
            }
        })
        $("#todocount").text(toDoCount);
        $("#donecount").text(doneCount);
    }

    // 读取本地存储的数据
    function getLocalStorage() {
        var date = localStorage.getItem("toDoList");
        // 判断本地存储是否为空
        if (date != null) {
            return JSON.parse(date);
        } else {
            return []; // 为空返回空数组
        }
    }
    // 保存本地存储的数据
    function setLocalStorage(date) {
        localStorage.setItem("toDoList", JSON.stringify(date));
    }



})

结构

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>ToDoList—待办事项列表</title>
    <link rel="stylesheet" href="css/index.css">
    <script src="js/jquery.min.js"></script>
    <script src="js/todolist.js"></script>
</head>

<body>
    <header>
        <section>
            <label for="title">ToDoList</label>
            <input type="text" id="title" name="title" placeholder="添加ToDo" required="required" autocomplete="off" />
        </section>
    </header>
    <section>
        <h2>正在进行 <span id="todocount"></span></h2>
        <ol id="todolist" class="demo-box">
            <!-- <li> <input type="checkbox"> <p>123</p> <a href="#"></a> </li> -->
        </ol>
        <h2>已经完成 <span id="donecount"></span></h2>
        <ul id="donelist">

        </ul>
    </section>
    <footer>
        Copyright &copy; 2014 todolist.cn
    </footer>


</body>

</html>

样式

body {
    margin: 0;
    padding: 0;
    font-size: 16px;
    background: #CDCDCD;
}

header {
    height: 50px;
    background: #333;
    background: rgba(47, 47, 47, 0.98);
}

section {
    margin: 0 auto;
}

label {
    float: left;
    width: 100px;
    line-height: 50px;
    color: #DDD;
    font-size: 24px;
    cursor: pointer;
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

header input {
    float: right;
    width: 60%;
    height: 24px;
    margin-top: 12px;
    text-indent: 10px;
    border-radius: 5px;
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.24), 0 1px 6px rgba(0, 0, 0, 0.45) inset;
    border: none
}

input:focus {
    outline-width: 0
}

h2 {
    position: relative;
}

span {
    position: absolute;
    top: 2px;
    right: 5px;
    display: inline-block;
    padding: 0 5px;
    height: 20px;
    border-radius: 20px;
    background: #E6E6FA;
    line-height: 22px;
    text-align: center;
    color: #666;
    font-size: 14px;
}

ol, ul {
    padding: 0;
    list-style: none;
}

li input {
    position: absolute;
    top: 2px;
    left: 10px;
    width: 22px;
    height: 22px;
    cursor: pointer;
}

p {
    margin: 0;
}

li p input {
    top: 3px;
    left: 40px;
    width: 70%;
    height: 20px;
    line-height: 14px;
    text-indent: 5px;
    font-size: 14px;
}

li {
    position: relative;
    height: 32px;
    line-height: 32px;
    background: #fff;
    margin-bottom: 10px;
    padding: 0 45px;
    border-radius: 3px;
    border-left: 5px solid #629A9C;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07);
}

ol li {
    cursor: move;
}

ul li {
    border-left: 5px solid #999;
    opacity: 0.5;
}

li a {
    position: absolute;
    top: 2px;
    right: 5px;
    display: inline-block;
    width: 14px;
    height: 12px;
    border-radius: 14px;
    border: 6px double #FFF;
    background: #CCC;
    line-height: 14px;
    text-align: center;
    color: #FFF;
    font-weight: bold;
    font-size: 14px;
    cursor: pointer;
}

footer {
    color: #666;
    font-size: 14px;
    text-align: center;
}

footer a {
    color: #666;
    text-decoration: none;
    color: #999;
}

@media screen and (max-device-width: 620px) {
    section {
        width: 96%;
        padding: 0 2%;
    }
}

@media screen and (min-width: 620px) {
    section {
        width: 600px;
        padding: 0 10px;
    }
}