在迭代期间添加点击事件监听器?

Adding Click Event Listeners During Iteration?

我试图在 for 循环中为一堆新创建的 div 添加一个点击事件侦听器。我遇到的问题是只有最后一个 div 保留其事件侦听器。我已经阅读了有关闭包的内容并阅读了其他几篇文章和问题及其答案,据我所知我已经正确设置了它。但它仍然不适合我。只有最后要迭代的div正在接收事件监听器。

function edit_entry(k) {
    wrd_input.value = k;
    def_input.value = lexicon[k][1];
    wrd_input.onkeyup();

    delete lexicon[k];
    rewrite_entries();
}

function rewrite_entries(keys = null) {
    if (keys === null) { keys = []; }
    let sorted_keys = sort_lex_keys();
    lex_body.style.color = 'rgb(200, 200, 200)';
    lex_body.innerHTML = '';

    sorted_keys.forEach((key) => {
        if (!keys.length || keys.includes(key)) {
        lex_body.innerHTML += 
        `<div class='lex-entry' id=${key}><i>${key}</i>\n<p class='pronunciation'>${lexicon[key][0]}</p>${lexicon[key][1]}</div>\n`;
        let entry = document.getElementById(key)
        entry.addEventListener('click', edit_entry.bind(this, key) );
        }
    });
}

上面相关代码的当前状态。如果有人知道这个问题,那将非常有帮助。 如果相关,此代码是 运行 通过 Electron (17.0.0)。

一个尝试的解决方案,使用匿名箭头函数而不是绑定:

entry.addEventListener('click', () => edit_entry(key) );

产生相同的结果。


更新: 更改某些内容的 .innerHTML 属性显然会剥离所有事件侦听器。所以解决方案就是简单的完全在js中创建元素,将其添加到容器中,然后然后添加监听器。对 forEach 循环的这种更改解决了问题:

    sorted_keys.forEach((key) => {
        if (!keys.length || keys.includes(key)) {
            let entry = document.createElement('div');
            entry.className = 'lex-entry';
            
            let word = document.createElement('p');
            word.appendChild( document.createTextNode(key) );
            word.style.fontStyle = 'italic';
            
            let pron = document.createElement('p');
            pron.className = 'pronunciation';
            pron.appendChild( document.createTextNode(lexicon[key][0]) );
            
            let defn = document.createTextNode(lexicon[key][1]);
            
            entry.append(word, pron, defn);
            entry.addEventListener('click', () => edit_entry(key) );

            lex_body.appendChild(entry);

我认为每次使用 this 绑定函数都会覆盖每次绑定,这应该是您只获得最后一个事件的原因。

forEach lambda 内部,this 关键字指的是调用 forEach 而不是您的 rewrite_entries 函数的内部 class。尝试不绑定调用,像这样:

entry.addEventListener('click', () => edit_entry(key) );

所以我想到了一些事情:

首先,edit_entry.bind(this, key);:你确定this指向你想要的吗?它应该指向 window 对象。另外,是否有任何理由为该特定功能绑定上下文?

其次innerHTML:一般不鼓励直接使用innerHTML。我不知道 how/when 浏览器布局更改为 innerHTML,当您调用 getElementById 时,div 可能不在页面上。作为替代方案,您可以尝试 document.createElement("div"),相应地设置其属性,最后将其附加到 lex_body.

编辑

来自 mdn:

Please note that using innerHTML to append html elements (e.g. el.innerHTML += "link") will result in the removal of any previously set event listeners. That is, after you append any HTML element that way you won't be able to listen to the previously set event listeners.