[英]Bind click event to elements created in a nested for loop
今天,我一直在大量閱讀閉包,並瀏覽了許多類似的問題。 我一直找不到能完全解決我所遇到的相同問題以及如何解決它的問題。 大多數答案似乎與jQuery有關,但是關於閉包的工作原理,還有一些很棒的答案。 我知道這就是我的問題所在。 我已經嘗試了很多不同的關閉示例,但都沒有運氣。
我有一組代表3個校園的數據。 每個校園都有一些建築物。 第一個for循環遍歷校園。 嵌套的for循環遍歷當前校園中的建築物,並向其綁定click事件(這使我可以以編程方式平移和縮放傳單地圖)。
我創建了一個非常簡化的JSFiddle來表示我要執行的操作,它顯示了問題。 只有最后一個“校園”組中的建築物保留點擊事件。
這是JS Fiddle的JS代碼。 http://jsfiddle.net/scwutpno/3/
var campuses = {
"campus1": {
"buildings": {
"building1": {
"id": 1
},
"building2": {
"id": 2
},
"building3": {
"id": 3
}
},
"name": "Campus 1"
},
"campus2": {
"buildings": {
"building1": {
"id": 4
},
"building2": {
"id": 5
},
"building3": {
"id": 6
}
},
"name": "Campus 2"
},
"campus3": {
"buildings": {
"building1": {
"id": 7
},
"building2": {
"id": 8
},
"building3": {
"id": 9
}
},
"name": "Campus 3"
}
};
var buildingLinksContainer = document.getElementById("building-list");
function buildExternalLinks() {
for (var campus in campuses) {
var container,
item;
if (buildingLinksContainer !== null) {
buildingLinksContainer.innerHTML += '<div class="building-links" id="' + campus + '-buildings">' +
'<div class="building-links__campus-name">' + campuses[campus].name + '</div></div>';
container = document.getElementById(campus + '-buildings');
}
for (var building in campuses[campus].buildings) {
item = container.appendChild(document.createElement('li'));
item.innerHTML = 'Building ' + campuses[campus].buildings[building].id;
item.addEventListener("click", function () {
alert('clicked!');
});
}
}
}
buildExternalLinks();
我曾嘗試將click事件放入一個自動調用的閉包中,但我認為click事件內部的數據並不存在問題。 這是單擊事件所應用到的節點。 我不知道是否因為使用嵌套的for循環而需要某種嵌套的閉包概念?
很想看看這個問題是如何解決的。 通過在for循環中使用閉包,我已經克服了它一次,但是嵌套的for循環讓我很困惑。
問題在於,每次附加到“ buildingLinksContainer”元素時,您的代碼都會重寫DOM。 每當您的代碼使用“ + =”附加到“ buildingLinksContainer”的innerHTML時,都會從頭開始創建DOM。 先前在“ buildingLinksContainer”內部具有事件偵聽器的所有元素都將創建為沒有任何事件偵聽器的新元素。
僅在最后一次迭代中,之后才將DOM拆除,事件偵聽器才會粘住。 並且由於在最后一次迭代中僅創建了最后三個li元素,因此只有它們具有事件偵聽器。
罪魁禍首:
buildingLinksContainer.innerHTML += '<div class="building-links" id="' + campus + '-buildings">' +
'<div class="building-links__campus-name">' + campuses[campus].name + '</div></div>';
我已經以一種可行的方式重寫了您的解決方案(具有更好的性能和標記)
var buildingLinksContainer = document.getElementById("building-list");
function buildExternalLinks(campuses, rootElement) {
var ul = document.createElement('ul'),
li = document.createElement('li'),
h1 = document.createElement('h1'),
list = document.createDocumentFragment();
for(var campus in campuses) {
if(campuses.hasOwnProperty(campus)) {
var c = ul.cloneNode(false);
c.classList.add('building-links');
c.setAttribute('id', campus+'-buildings');
var name = h1.cloneNode(false);
name.appendChild(document.createTextNode(campuses[campus].name));
c.appendChild(name);
for(var building in campuses[campus].buildings) {
if(campuses[campus].buildings.hasOwnProperty(building)) {
var b = li.cloneNode(false);
var text = 'Building ' + campuses[campus].buildings[building].id;
b.appendChild(document.createTextNode(text));
b.addEventListener('click', function(event) {
alert(event.target.innerHTML);
});
c.appendChild(b);
}
}
}
list.appendChild(c);
}
rootElement.appendChild(list);
}
buildExternalLinks(campuses, buildingLinksContainer);
我還更新了/派生了您的JSFiddle 。
+=
只是x = x + y
類的快捷方式。 設置buildingLinksContainer.innerHTML += ...
您實際上將重新創建該元素的全部內容。 刪除了所有先前的內容,包括附加到元素的事件。 然后,將一個新的字符串設置為DOM,並且在解析它時,不再有任何事件。
一個快速解決方案是使用insertAdjacentHTML()
而不是innerHTML
,如下所示:
buildingLinksContainer.insertAdjacentHTML('beforeend',
'<div class="building-links" id="' + campus + '-buildings">' +
'<div class="building-links__campus-name">' +
campuses[campus].name +
'</div></div>'
);
使用事件委托僅將事件偵聽器添加到building-list元素
document.querySelector('#building-list').addEventListener('click', function(evt){
var clickedElement = evt.target;
//just use the clickedElement variable to get the element that was clicked
//ex: you can get the id from it or whatever you want
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.