【前端实例代码】使用在 HTML CSS 和 JavaScript 创建todolist待办事项列表应用

  操作前后效果:(知识点和完整代码在最后面)

 bilibili在线视频演示链接:

【前端实例代码】使用在 HTML CSS 和 JavaScript 创建todolist待办事项列表应用_哔哩哔哩_bilibili【前端实例代码】使用在 HTML CSS 和 JavaScript 创建todolist待办事项列表应用, 视频播放量 38、弹幕量 0、点赞数 0、投硬币枚数 0、收藏人数 2、转发人数 0, 视频作者 南北极之间, 作者简介 我的csdn账号:南北极之间,相关视频:程序员之间的鄙视链,【前端实例代码】霓虹灯按钮动画效果悬停| html CSS特效 惊艳,【前端实例代码】如何在HTML CSS和JavaScript|中使用正则表达式RegExp输入框校验email电子邮件验证,vue插件开发实例之vue3版图片预览,vue+nodejs商城实战项目【登录 + 购物车 + 支付】,程序员专属接头暗号,最后结果你想象不到~,创意编码:使用 JavaScript 制作视觉效果,GitHub优质项目推荐,计算机专业学习指南,汇编语言的学习 -- 汇编语言的执行过程,我是Redis,MySQL大哥被我坑惨了!,B端设计中如何定义弹窗尺寸https://www.bilibili.com/video/BV1uF411P7vK/?vd_source=5cc677504c9c936e5a0ea2042eb76a56

完整代码:

<!DOCTYPE html>
<!-- Coding By CodingNepal - youtube.com/codingnepal -->
<html lang="en" dir="ltr">
<head>
    <meta charset="utf-8">
    <title>【前端实例代码】使用在 HTML CSS 和 JavaScript 创建todolist待办事项列表应用</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- Iconscout Link For Icons -->
    <link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.0/css/line.css">
    <style>
        /* Import Google Font - Poppins */
        @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Poppins', sans-serif;
        }

        body {
            width: 100%;
            height: 100vh;
            overflow: hidden;
            background: linear-gradient(135deg, #4AB1FF, #2D5CFE);
        }

        ::selection {
            color: #fff;
            background: #3C87FF;
        }

        .wrapper {
            max-width: 405px;
            padding: 28px 0 30px;
            margin: 137px auto;
            background: #fff;
            border-radius: 7px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
        }

        .task-input {
            height: 52px;
            padding: 0 25px;
            position: relative;
        }

        .task-input img {
            top: 50%;
            position: absolute;
            transform: translate(17px, -50%);
        }

        .task-input .el-icon-notebook-2 {
            top: 50%;
            position: absolute;
            transform: translate(17px, -50%);
        }

        .task-input input {
            height: 100%;
            width: 100%;
            outline: none;
            font-size: 18px;
            border-radius: 5px;
            padding: 0 20px 0 53px;
            border: 1px solid #999;
        }

        .task-input input:focus,
        .task-input input.active {
            padding-left: 52px;
            border: 2px solid #3C87FF;
        }

        .task-input input::placeholder {
            color: #bfbfbf;
        }

        .controls, li {
            display: flex;
            align-items: center;
            justify-content: space-between;
        }

        .controls {
            padding: 18px 25px;
            border-bottom: 1px solid #ccc;
        }

        .filters span {
            margin: 0 8px;
            font-size: 17px;
            color: #444444;
            cursor: pointer;
        }

        .filters span:first-child {
            margin-left: 0;
        }

        .filters span.active {
            color: #3C87FF;
        }

        .controls .clear-btn {
            border: none;
            opacity: 0.6;
            outline: none;
            color: #fff;
            cursor: pointer;
            font-size: 13px;
            padding: 7px 13px;
            border-radius: 4px;
            letter-spacing: 0.3px;
            pointer-events: none;
            transition: transform 0.25s ease;
            background: linear-gradient(135deg, #1798fb 0%, #2D5CFE 100%);
        }

        .clear-btn.active {
            opacity: 0.9;
            pointer-events: auto;
        }

        .clear-btn:active {
            transform: scale(0.93);
        }

        .task-box {
            margin-top: 20px;
            margin-right: 5px;
            padding: 0 20px 10px 25px;
        }

        .task-box.overflow {
            overflow-y: auto;
            max-height: 300px;
        }

        .task-box::-webkit-scrollbar {
            width: 5px;
        }

        .task-box::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 25px;
        }

        .task-box::-webkit-scrollbar-thumb {
            background: #e6e6e6;
            border-radius: 25px;
        }

        .task-box .task {
            list-style: none;
            font-size: 17px;
            margin-bottom: 18px;
            padding-bottom: 16px;
            align-items: flex-start;
            border-bottom: 1px solid #ccc;
        }

        .task-box .task:last-child {
            margin-bottom: 0;
            border-bottom: 0;
            padding-bottom: 0;
        }

        .task-box .task label {
            display: flex;
            align-items: flex-start;
        }

        .task label input {
            margin-top: 7px;
            accent-color: #3C87FF;
        }

        .task label p {
            user-select: none;
            margin-left: 12px;
            word-wrap: break-word;
        }

        .task label p.checked {
            text-decoration: line-through;
        }

        .task-box .settings {
            position: relative;
        }

        .settings :where(i, li) {
            cursor: pointer;
        }

        .settings .task-menu {
            z-index: 10;
            right: -5px;
            bottom: -65px;
            padding: 5px 0;
            background: #fff;
            position: absolute;
            border-radius: 4px;
            transform: scale(0);
            transform-origin: top right;
            box-shadow: 0 0 6px rgba(0, 0, 0, 0.15);
            transition: transform 0.2s ease;
        }

        .task-box .task:last-child .task-menu {
            bottom: 0;
            transform-origin: bottom right;
        }

        .task-box .task:first-child .task-menu {
            bottom: -65px;
            transform-origin: top right;
        }

        .task-menu.show {
            transform: scale(1);
        }

        .task-menu li {
            height: 25px;
            font-size: 16px;
            margin-bottom: 2px;
            padding: 17px 15px;
            cursor: pointer;
            justify-content: flex-start;
        }

        .task-menu li:last-child {
            margin-bottom: 0;
        }

        .settings li:hover {
            background: #f5f5f5;
        }

        .settings li i {
            padding-right: 8px;
        }

        @media (max-width: 400px) {
            body {
                padding: 0 10px;
            }

            .wrapper {
                padding: 20px 0;
            }

            .filters span {
                margin: 0 5px;
            }

            .task-input {
                padding: 0 20px;
            }

            .controls {
                padding: 18px 20px;
            }

            .task-box {
                margin-top: 20px;
                margin-right: 5px;
                padding: 0 15px 10px 20px;
            }

            .task label input {
                margin-top: 4px;
            }
        }
    </style>
</head>
<body>
<div class="wrapper">
    <div class="task-input">
        <!--        <img src="bars-icon.svg" alt="icon">-->
        <i class="el-icon-notebook-2"></i>
        <input type="text" placeholder="Add a new task">
    </div>
    <div class="controls">
        <div class="filters">
            <span class="active" id="all">All</span>
            <span id="pending">Pending</span>
            <span id="completed">Completed</span>
        </div>
        <button class="clear-btn">Clear All</button>
    </div>
    <ul class="task-box"></ul>
</div>

<script>
    const taskInput = document.querySelector(".task-input input"),
        filters = document.querySelectorAll(".filters span"),
        clearAll = document.querySelector(".clear-btn"),
        taskBox = document.querySelector(".task-box");

    let editId,
        isEditTask = false,
        todos = JSON.parse(localStorage.getItem("todo-list"));

    filters.forEach(btn => {
        btn.addEventListener("click", () => {
            document.querySelector("span.active").classList.remove("active");
            btn.classList.add("active");
            showTodo(btn.id);
        });
    });

    function showTodo(filter) {
        let liTag = "";
        if (todos) {
            todos.forEach((todo, id) => {
                let completed = todo.status == "completed" ? "checked" : "";
                if (filter == todo.status || filter == "all") {
                    liTag += `<li class="task">
                            <label for="${id}">
                                <input onclick="updateStatus(this)" type="checkbox" id="${id}" ${completed}>
                                <p class="${completed}">${todo.name}</p>
                            </label>
                            <div class="settings">
                                <i onclick="showMenu(this)" class="uil uil-ellipsis-h"></i>
                                <ul class="task-menu">
                                    <li onclick='editTask(${id}, "${todo.name}")'><i class="uil uil-pen"></i>Edit</li>
                                    <li onclick='deleteTask(${id}, "${filter}")'><i class="uil uil-trash"></i>Delete</li>
                                </ul>
                            </div>
                        </li>`;
                }
            });
        }
        taskBox.innerHTML = liTag || `<span>You don't have any task here</span>`;
        let checkTask = taskBox.querySelectorAll(".task");
        !checkTask.length ? clearAll.classList.remove("active") : clearAll.classList.add("active");
        taskBox.offsetHeight >= 300 ? taskBox.classList.add("overflow") : taskBox.classList.remove("overflow");
    }

    showTodo("all");

    function showMenu(selectedTask) {
        let menuDiv = selectedTask.parentElement.lastElementChild;
        menuDiv.classList.add("show");
        document.addEventListener("click", e => {
            if (e.target.tagName != "I" || e.target != selectedTask) {
                menuDiv.classList.remove("show");
            }
        });
    }

    function updateStatus(selectedTask) {
        let taskName = selectedTask.parentElement.lastElementChild;
        if (selectedTask.checked) {
            taskName.classList.add("checked");
            todos[selectedTask.id].status = "completed";
        } else {
            taskName.classList.remove("checked");
            todos[selectedTask.id].status = "pending";
        }
        localStorage.setItem("todo-list", JSON.stringify(todos))
    }

    function editTask(taskId, textName) {
        editId = taskId;
        isEditTask = true;
        taskInput.value = textName;
        taskInput.focus();
        taskInput.classList.add("active");
    }

    function deleteTask(deleteId, filter) {
        isEditTask = false;
        todos.splice(deleteId, 1);
        localStorage.setItem("todo-list", JSON.stringify(todos));
        showTodo(filter);
    }

    clearAll.addEventListener("click", () => {
        isEditTask = false;
        todos.splice(0, todos.length);
        localStorage.setItem("todo-list", JSON.stringify(todos));
        showTodo()
    });

    taskInput.addEventListener("keyup", e => {
        let userTask = taskInput.value.trim();
        if (e.key == "Enter" && userTask) {
            if (!isEditTask) {
                todos = !todos ? [] : todos;
                let taskInfo = {name: userTask, status: "pending"};
                todos.push(taskInfo);
            } else {
                isEditTask = false;
                todos[editId].name = userTask;
            }
            taskInput.value = "";
            localStorage.setItem("todo-list", JSON.stringify(todos));
            showTodo(document.querySelector("span.active").id);
        }
    });
</script>

</body>
</html>

涉及到的知识点:

1、HTML DOM querySelector() 方法

querySelector() 方法返回文档中匹配指定 CSS 选择器的一个元素。

注意: querySelector() 方法仅仅返回匹配指定选择器的第一个元素。如果你需要返回所有的元素,请使用 querySelectorAll() 方法替代。

语法

document.querySelector(CSS selectors)

参数值

参数 类型 描述
CSS 选择器 String 必须。指定一个或多个匹配元素的 CSS 选择器。 可以使用它们的 id, 类, 类型, 属性, 属性值等来选取元素。

对于多个选择器,使用逗号隔开,返回一个匹配的元素。

提示: 更多 CSS 选择器,请参阅我们的 CSS 选择器参考手册

技术细节

DOM 版本: Selectors Level 1 Document Object
返回值: 匹配指定 CSS 选择器的第一个元素。 如果没有找到,返回 null。如果指定了非法选择器则 抛出 SYNTAX_ERR 异常。

推荐阅读:

HTML DOM querySelector() 方法 | 菜鸟教程HTML DOM querySelector() 方法 Document 对象 实例 获取文档中 id='demo' 的元素: document.querySelector('#demo'); 尝试一下 » 定义和用法 querySelector() 方法返回文档中匹配指定 CSS 选择器的一个元素。 注意: querySelector() 方法仅仅返回匹配指定选择器的第一个元..https://www.runoob.com/jsref/met-document-queryselector.html

2、HTML DOM addEventListener() 方法

定义和用法

addEventListener() 方法用于向指定元素添加事件句柄。

提示: 使用 removeEventListener() 方法来移除 addEventListener() 方法添加的事件句柄。

语法

element.addEventListener(eventfunctionuseCapture)

参数值

参数 描述
event 必须。字符串,指定事件名。

注意: 不要使用 "on" 前缀。 例如,使用 "click" ,而不是使用 "onclick"。

提示: 所有 HTML DOM 事件,可以查看我们完整的 HTML DOM Event 对象参考手册
function 必须。指定要事件触发时执行的函数。

当事件对象会作为第一个参数传入函数。 事件对象的类型取决于特定的事件。例如, "click" 事件属于 MouseEvent(鼠标事件) 对象。
useCapture 可选。布尔值,指定事件是否在捕获或冒泡阶段执行。

可能值:
  • true - 事件句柄在捕获阶段执行
  • false- false- 默认。事件句柄在冒泡阶段执行

推荐阅读:

HTML DOM addEventListener() 方法 | 菜鸟教程HTML DOM addEventListener() 方法 元素对象 实例 为 <button> 元素添加点击事件。 当用户点击按钮时,在 id='demo' 的 <p> 元素上输出 'Hello World' : document.getElementById('myBtn').addEventListe..https://www.runoob.com/jsref/met-element-addeventlistener.html

3、HTML DOM classList 属性

定义和用法

classList 属性返回元素的类名,作为 DOMTokenList 对象。

该属性用于在元素中添加,移除及切换 CSS 类。

classList 属性是只读的,但你可以使用 add() 和 remove() 方法修改它。

语法

element.classList

Properties

属性 Description
length 返回类列表中类的数量

该属性是只读的

推荐阅读:

HTML DOM classList 属性 | 菜鸟教程HTML DOM classList 属性 元素对象 实例 为 <div> 元素添加 class: document.getElementById('myDIV').classList.add('mystyle'); 尝试一下 » 定义和用法 classList 属性返回元素的类名,作为 DOMTokenList 对象。 该属性用于在元素中添加,移除..https://www.runoob.com/jsref/prop-element-classlist.html

4、HTML DOM innerHTML 属性

定义和用法

innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML。

语法

HTMLElementObject.innerHTML=text

4、forEach() 方法

5、map() 方法

6、filter()方法

7、join()方法

8、toLowerCase()方法

9、es6模板字符串

猜你喜欢

转载自blog.csdn.net/qq_22182989/article/details/126181062