练习小项目6:简单待办事项筛选器

📝 项目目标:

创建一个待办事项输入和筛选功能,可以输入多个任务,实时筛选出匹配的内容。

✅ 功能拆解

  • 用户可添加任务到列表中

  • 用户可以通过输入关键词进行筛选

  • 用户点击删除按钮可以移除对应的任务

  • 若没有匹配项,显示提示信息“没有匹配的任务”

💡 你将练习到的重点知识

  • inputinput 事件(实时监听输入)

  • 数组的 filter() 方法

  • DOM 动态渲染基础

  • 小模块化思维

  • 掌握了事件委托的基本原理和使用

  • 理解 dataset 与类型转换在前端中的应用

HTML 部分结构 (可自己定义):

<input type="text" id="todoInput" placeholder="添加任务">
<button id="addBtn">添加</button>

<input type="text" id="filterInput" placeholder="筛选任务">

<ul id="todoList"></ul>

 实践代码如下:

const todoInput = document.getElementById('todoInput')
const addBtn = document.getElementById('addBtn')
const filterInput = document.getElementById('filterInput')
const todoList = document.getElementById('todoList')

let taskList = []

renderList = (keyword = '') => {
    // 清空原有列表
    todoList.innerHTML = ''

    // 使用 filter 进行筛选(注意保留原始 taskList)
    const filterList = taskList.filter((data) => {
        return data.toLowerCase().includes(keyword.toLowerCase())
    })

    if (filterList.length === 0) {
        const li = document.createElement('li')
        li.textContent = '没有匹配的任务'
        li.style.color = 'gray'
        todoList.appendChild(li)
        return
    }

    // 遍历筛选结果,创建 <li> 并添加到 todoList 中
    filterList.forEach((element, index) => {
        const li = document.createElement('li')
        li.textContent = element
        const delBtn = document.createElement('button')
        delBtn.textContent = '❌'
        // 给每个任务绑定索引 data-index
        delBtn.setAttribute('data-index', taskList.indexOf(element))
        li.appendChild(delBtn)
        todoList.appendChild(li)
    });
}

addBtn.addEventListener('click', () => {
    // 获取输入,判断非空,添加到 taskList 中,并清空输入框
    const text = todoInput.value.trim()
    if (text) {
        taskList.push(text)
    }
    todoInput.value = ''

    // 调用 renderList 渲染当前任务列表(可传当前 filterInput.value)
    renderList(filterInput.value)

})

filterInput.addEventListener('input', () => {
    // 监听输入变化,调用 renderList 进行筛选显示
    renderList(filterInput.value)

})

// 绑定删除事件(事件委托)
todoList.addEventListener('click', (e) => {
    if (e.target.tagName === 'BUTTON') {
        const index = Number(e.target.dataset.index)
        if (index !== undefined && !isNaN(index))
            taskList.splice(index, 1)
        renderList(filterInput.value)
    }
})

页面效果展示 :  

 

❗遇到的问题和解决过程

🧩 问题1:为什么需要index !== undefined ?

if (index !== undefined) 这一句是一个防御性写法,意思是:确保我们拿到了有效的 data-index 才继续执行删除逻辑。

但你可能会问:“我们不是每个按钮都加了 data-index 吗?那什么时候会是 undefined 呢?”

 什么时候可能出现 index === undefined

  1. 点击的不是你渲染的删除按钮

    • 比如将来你可能在 todoList 里加了别的按钮(比如“编辑”按钮),但没设置 data-index

    • 用户点了那个按钮,e.target.dataset.index 就会是 undefined

  2. 你手滑忘了加 data-index

    • 假设你在某次更新中遗漏给按钮加 data-index 属性,点这个按钮也会导致 undefined

  3. 开发或调试时 DOM 改变了

    • 有时 DOM 结构可能被意外改动,比如清空列表但按钮没移除,残留元素可能没 data-index

🧠 总结一句话:

虽然现在看起来永远都会有 data-index,但写这个判断,是为了避免将来出错导致程序崩溃,是一种编写健壮代码的习惯。

🧩 问题2:为什么需要 !isNaN(index)

  • dataset.index 是字符串,如 "2"

  • Number("2") 会变成数字 2

  • 但如果是 "abc"undefined 会变成 NaN,所以要判断 isNaN

🧩 问题3: 为什么要用 taskList.indexOf(element) 而不是 index? 

  •  filterList 是基于关键词筛选出来的任务子集,是 taskList 的“视图”副本。
  •  filterList  中的元素索引与 taskList中的索引不一定对应。
  • 删除操作必须针对原始数组 taskList 进行,而非 filterList
  • 因此,删除按钮的索引必须是taskList 中对应任务的真实索引,避免删除错误的任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值