我在做一个待办清单应用,尝试往清单中添加待办事项时,总是会重复出现

前端开发 2026-07-10

嗨,我是一名刚入门的编程新手,正在尝试构建The Odin Project的待办清单。这个项目的代码让我卡了两天,这是我第一次在论坛上求助,请你们如果我有错误或者你们不理解我的问题,请务必告诉我。这个代码确实使用了模块,逻辑与显示分离,想把所有东西塞进一个视图里有点困难,所以我把相关的部分放在这里。在我的代码里有一个“添加项目”按钮,用户点击后会生成一个表单,填写后就可以创建一个新项目。这个新项目位于一个div里,用户可以生成任意数量的项目,这个div在我的代码里叫做overview。我想点击每个单独的项目(我为每个项目设置了一个名为data-id的属性,其值与该项目的ID相同),并会显示一个“添加任务”按钮,点击后你可以填写以添加任务,但问题是如果我添加4 个项目,每个项目中的任务都会被重复4 次,我知道这是因为forEach循环,但我尝试把代码放在forEach循环之外生成,或放到另一个变量中都会让我的代码出错。

下面是我的代码的GitHub链接 https://github.com/Oyin414/to_do_list

class ListItems {
    constructor( title, priority, dues, info, id ) {
        this.title = title
        this.priority = priority
        this.due = format( new Date(), dues )
        this.info = info
        this.id = id
    }
}

class Project {
    constructor( title,id ) {
        this.title = title
        this.id = id
        this.list = []
    }

    addListItem( item ) {
        this.list.push( item )
        console.log( this.list )
    }

    findListItem( id ) {
        const result =  this.list.filter( item => item.id === id )
        return result
    }

    removeListItem( item ) {
        let newList = this.list.filter( value => value.id !== item.id )
        this.list = newList
    }
}

projectBtn.addEventListener( "click", function() {
    event.preventDefault();
    let projectName = document.getElementById( "project" ).value
    let id = crypto.randomUUID()
    const newProject = new Project( projectName,id )
    makeProject( projectName,id )
    addProject( newProject )
    console.log( id )
    console.log( getProject() )
    project = getProject()
    let overview = document.querySelectorAll( ".overview" )
    overview.forEach(item=> item.addEventListener( "click",function() {
        document.querySelector( '.hide' ).style.display = "block";
        let dataId = item.getAttribute( "data-id" )
        result = findProject( dataId )
        changeProject( result.title )
    }))  

    taskBtn.addEventListener( "click",function () {
        event.preventDefault();
        let name = document.getElementById( "task" ).value
        let priority = document.getElementById( "priority" ).value
        let date = document.getElementById( "date" ).value
        let taskId = crypto.randomUUID()
        let info = document.getElementById( "info" ).value
        let task = new ListItems( name, priority, date, info, taskId )
        makeTask(name,date,taskId)
    })
})

解决方案

确实如此,你会将所有元素加载后逐个循环,并把事件监听器加到它们的每一个上面,因此如果你创建了n 个项目,第一项就会有n 个监听器,而你真正需要的只有一个。一个解决办法是给已经初始化的项目打上一个标记类,我把它叫做 initialized

for (let item of document.querySelectorAll(".overview:not(.initialized)")) {
    item.addEventListener("click", function() {
        document.querySelector('.hide').style.display = "block";
        let dataId = item.getAttribute("data-id");
        let result = findProject(dataId);
        changeProject(result.title);
    });
    item.classList.add("initialized");
}

于是,我们遍历尚未初始化的项,添加监听器并添加 initialized 类。

概念验证:

let index = 1;
document.getElementById("add-overview").addEventListener("click", function() {
    let div = document.createElement("div");
    div.className = "overview";
    div.innerText = index++;
    document.getElementById("container").appendChild(div);
    for (let item of document.querySelectorAll(".overview:not(.initialized)")) {
        item.addEventListener("click", function() {
            document.getElementById("result").innerText = item.innerText;
            console.log("Click on " + item.innerText + " fired");
        });
        item.classList.add("initialized");
    }
});
#result, .overview {
    border: 1px solid red;
    font-size: 32pt;
    display: inline;
}
<div id="result">Empty</div>
<hr>
<div id="container">
</div>
<input type="button" value="add" id="add-overview">

如上所示,你可以在页面上动态创建任意数量的overview,并点击它们在一个容器中查看,同时在控制台你可以看到事件发生的时间,并验证监听器对任意一个项只会触发一次,即使自那以后创建了新项。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章