You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jQuery Todo应用点击事件堆叠问题求助

问题分析与解决方案

问题根源

你遇到的多次弹窗问题,核心原因是编辑操作时的keydown事件被重复绑定且未解绑:

  • 每次按下Alt键点击待办项时,都会给document绑定一个新的keydown事件处理函数,这些事件会持续累积。
  • 即使待办项被重新渲染(DOM元素替换),绑定在document上的事件引用依然存在,后续操作可能触发多个事件处理函数,导致多次弹出确认框。
  • 另外,checkTodos函数中错误使用了$('#notices')选择器,而HTML中对应的元素ID是todo-notices,会导致通知无法正常显示。

修复方案

  1. 使用.one()绑定编辑的keydown事件:直接给目标待办项绑定keydown事件,并设置仅触发一次,触发后自动解绑,避免事件累积。
  2. 修复通知选择器:将$('#notices')改为$('#todo-notices')。
  3. 增加ESC键取消编辑功能:提升用户体验,允许用户按下ESC键放弃编辑。

修改后的完整代码

HTML部分

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js" integrity="sha512-pumBsjNRGGqkPzKHndZMaAG+bir374sORyzM3uulLV14lN5LyykqNk8eEeUlUkB3U0M4FApyaHraT65ihJhDpQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="container">
    <header>
        <div id="title">Todo List</div>
    </header>
    <nav></nav>
    <aside>
        <input id="todo" type="text" placeholder="Add new items here." />
    </aside>
    <main> 
        <div class="container">
            <!-- incomplete todo -->
            <div id="todo-incomplete"></div>
        </div>                            
        <div id="todo-notices"></div>
        <div id="todo-notes">   
            <p class="text-info">Edit Item - Windows: Alt-click; Mac: Option-click</p>
            <p class="text-info">Delete Item - Shift-click</p>
        </div>
    </main>
    <footer>
        <div id="copyright"></div>
        <div id="page-load"></div>
    </footer>
</div>

JavaScript部分

function checkTodos() {
    var list = JSON.parse(localStorage['todo-list']);
    if (list.length == 0) {
        // 修复选择器错误
        $('#todo-notices').html(`<div class='card'>No list items exist yet. Use the input box on the left to add items.</div>`);
    } else {
        let html = "";
        list.forEach((todo, index) => {
            html += `<div id='item-${index}' class='card' data-index='${index}'>${todo}</div>`;
        });
        $("#todo-incomplete").html(html);
        // 修复选择器错误
        $('#todo-notices').empty();
    }
}

function updateTodos(list) {
    $('#todo').val('');
    localStorage.setItem('todo-list', JSON.stringify(list));
    checkTodos();
}

function doConfirm(type = 'delete') {
    let message = type == 'delete' ? 'Do you confirm todo item deletion?' : 'Do you confirm todo item editing?';
    return confirm(message);
}

$(document).ready(function() {  
    let list;

    if (localStorage) {
        list = localStorage.hasOwnProperty('todo-list') ? JSON.parse(localStorage['todo-list']) : [];
    }

    checkTodos();

    // 新增待办项
    $(document).on('keydown', '#todo', (event) => {
        if (event.which === 13 && $(event.target).val() !== "") {
            list.push($(event.target).val());
            updateTodos(list);
        }
    });

    // 待办项点击事件
    $(document).on('click', '#todo-incomplete > .card', (event) => {
        event.preventDefault();

        if (event.shiftKey) {        // 删除待办项
            if (doConfirm()) {
                const index = parseInt($(event.target).attr('data-index'));
                list.splice(index, 1);
                updateTodos(list);
            }
        } else if (event.altKey) {   // 编辑待办项
            const $target = $(event.target);
            $target.attr('contenteditable', true);
            $target.focus();

            // 使用.one()绑定keydown事件,确保仅触发一次
            $target.one('keydown', (e) => {
                if (e.which === 13) { // 回车确认编辑
                    e.preventDefault(); // 避免添加换行符
                    if (doConfirm('edit')) {
                        $target.removeAttr('contenteditable');
                        const index = parseInt($target.attr('data-index'));
                        list[index] = $target.html();
                        updateTodos(list);
                    } else {
                        // 取消编辑,恢复原内容
                        $target.removeAttr('contenteditable');
                        $target.html(list[index]);
                    }
                } else if (e.which === 27) { // ESC取消编辑
                    e.preventDefault();
                    $target.removeAttr('contenteditable');
                    $target.html(list[index]);
                }
            });
        } else {
            console.log(event.which);
        }
    });
});

关键修改说明

  • 编辑操作的keydown事件改为直接绑定在目标元素上,并使用.one(),确保事件仅执行一次后自动解绑,彻底解决事件累积问题。
  • 修复了通知区域的选择器错误,确保空列表提示正常显示。
  • 增加了ESC键取消编辑的逻辑,同时在用户取消编辑确认时恢复原内容,提升体验。
  • 删除了不必要的event.stopPropagation(),因为事件委托绑定在document上,该方法无法阻止事件到达document。

内容的提问来源于stack exchange,提问作者JLDN Admin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 21:12:14