JavaScript仅最后一个事件侦听器起作用。

5

由于我的代码到处都是,所以很难向您展示,但我想做的是:

我在一个函数中使用.innerHTML将html代码注入到DOM中,我希望在这一步中添加一个单击事件到被注入的图标上,因为此时我知道它的id。所以在注入后,我写下:

document.getElementById(product.id+"x").addEventListener("click", removeItem);

product.id在上面创建,这个元素是一个'X'按钮,当单击时将从屏幕中移除。

问题是,由于屏幕上要显示许多项目,所以这段代码会运行多次。当完成后,只有最后一个事件在按下'X'按钮时才会触发。

有什么建议吗?

编辑:

我无法在这个项目中使用jquery。

以下是我的代码:

function createHTML(targetID, product) {
    var target = document.getElementById(targetID);
    total = (parseFloat(total) + parseFloat(product.price)).toFixed(2);;
    target.innerHTML += '<article class="item" id="'+product.id+'"><img class="item_img" src="../'+product.image+'" width=100 height=100><h1 class="item_name">'+product.name+'</h1><p class="item_description">'+product.desc+'</p><h1 class="item_quantity">Quantity: '+product.quantity+'</h1><h1 class="item_price">&pound;'+product.price+'</h1><i id="'+product.id+'x" class="fa fa-times"></i></article>';

    document.getElementById(product.id+"x").addEventListener("click", removeItem, true);
}

JavaScript 闭包 http://stackoverflow.com/questions/17892367/javascript-listens-for-events-only-on-the-last-node-specified?rq=1 - DhruvPathak
我在想我需要一种包装函数来使它工作,但目前我不太确定它如何工作,你能详细解释一下吗? - benharris
2个回答

8
你正在通过覆盖 innerHTML 或使用 += 添加新元素到容器中。这就是你的问题。当你覆盖 innerHTML 或追加它时,你正在销毁并重新创建其中所有元素,这会导致它们失去任何绑定的事件处理程序(例如你的点击处理程序)。 此 fiddle 重现了你的问题。只有最后一个按钮有一个点击处理程序。 解决方案是使用 document.createElement() 构建 DOM 元素,并使用 appendChild() 或类似方法将它们附加,而不是创建/附加原始 HTML。这样,你以前的元素事件处理程序将保持完好无损。 此 Fiddle 使用 DOM 节点而不是原始 HTML,所有按钮都有一个点击处理程序。 修复示例:
var container = document.getElementById("container");
var elem;

function clicky(){
    alert("clicked");   
}

for(var i=0; i<4; i++){
    elem = document.createElement('button');
    elem.id = "btn_" + i;
    elem.appendChild(document.createTextNode('Click'));
    elem.addEventListener("click", clicky);
    container.appendChild(elem);
}

抱歉,我没有解释清楚,它们并没有被覆盖,因为我正在使用+=来追加到innerHTML的末尾。 - benharris
它们仍然被覆盖,+= 没有任何区别。浏览器仍然会销毁并重新创建其中的所有内容。 - MrCode
3
@benharris,请查看我的答案中的两个Fiddle。 - MrCode
嗨 @MrCode,当浏览器重新加载时,它不会重新附加事件监听器到每个元素吗?我的假设是,在重新加载时会附加新的监听器,这样就能正常工作。谢谢。 - undefined

2

我猜你是想做类似这样的事情

//Place where you add elements.
var container = document.body;

您可以创建元素并向该元素(button)添加监听器:
var button = '<button id="btn1x">Button 1</button>';
container.innerHTML += button;

//product.id = 'btn1';
document.getElementById(product.id+"x").addEventListener("click", removeItem);

然后,您以相同的方式添加新元素,并在生成下一个元素之前为它们添加事件侦听器。

如果我猜得对,那么您的问题是您替换了container的整个内容,因此先前的事件监听器丢失了。

stringVariable += 'abc'stringVariable = stringVariable + 'abc'相同。因此,您覆盖了html。

您应该从函数创建元素,而不是像现在这样从字符串创建。

var button = document.createElement('button');
button.id = product.id + 'x'; 
button.innerText = 'Button 1'; // Title of button.

//Add button to container.
container.appendChild(button);

//Add event listener to created button.
button.addEventListener('click', myFunc); 

更新:

有一种方法可以将您的字符串解析为元素。

首先创建一个容器,将从字符串设置内部HTML,然后从该临时容器中获取第一个元素(或更多元素,取决于您的HTML字符串),然后将它们添加到容器中并添加监听器。

示例:http://jsfiddle.net/3cD4G/1/

HTML:

<div id="container">

</div>

Javascript:

var container = document.getElementById("container");

function clicky(){
    alert("clicked");   
}
var tempContainer = document.createElement('div');
for(var i=0; i<4; i++){
    //Create your element as string.
    var strElem = "<button type='button' id='btn_" + i + "'>Click</button>";
    //Add that string to temp container (his html will be replaced, not added).
    tempContainer.innerHTML = strElem.trim();//Trim function used to prevent empty textnodes before element.
    //Get element from temp container.
    var button = tempContainer.children[0];
    //Empty tempContainer for better security (But about which security I'm talking in JavaScript in string element generation :) )
    tempContainer.innerHTML = '';

    //Add your button to container.
    container.appendChild(button);

    //Add event listener to button:
    //document.getElementById("btn_" + i).onclick = clicky;
    //Better way to add event listener:
    button.addEventListener('click', clicky);
}

DEMO: http://jsfiddle.net/3cD4G/1/


哦!好的,谢谢你提供这个,我感觉这会解决问题,只是需要大量编辑才能让它正常工作。 - benharris
jQuery有一个好用的函数($.parseHTML()), 但是因为你不能使用jQuery,所以你需要手动创建所有元素。但这是一个好的习惯,因为大多数的HTML编辑器都有智能提示(包含建议菜单),它们可以帮你更快地编写代码,并且你可以找到一些通过字符串无法获取的属性。 - Epsil0neR
el.id=s;el.setAttribute('id', s); 之间有什么区别吗? - vp_arth
不,没有区别,因为HTMLElement对象具有这些属性。但两种方式都是正确的和本地化的。 - Epsil0neR
@benharris 答案已更新。找到了解析您的HTML字符串的方法。 - Epsil0neR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接