只有我使用 innerHTML 添加的最后一个元素保留其事件处理程序,为什么?
Only the last element I added using innerHTML keeps its event handlers, why?
我正在尝试制作一个脚本,将可交互对象信息注入标记页面的列表中。每当我尝试在 div 上添加一个 onclick 事件时,它工作正常,但是每当我尝试在 for 循环中添加更多事件时,它都不会按我预期的方式工作。
我在网页调试器中使用断点查看了发生了什么,我发现问题是它似乎在添加到下一个 [= 之前删除了前一个 div 上的事件28=]。最后,唯一剩下的事件是循环退出后的最后一个div。
我想将这些事件保留在我的所有 div 上,而不仅仅是最后一个...这似乎是什么问题?
var objects = ['Tom', 'Sauna', 'Traum'];
for (var i = 0; i < objects.length; i++){
document.getElementById('list').innerHTML += "<div class='item' id='"+ i +"'>" + objects[i] + "</div>";
document.getElementById(i).addEventListener("mouseup", function() {
Select(this);
});
}
function Select(char) {
console.log(char);
}
div.item {
border: 1px solid black;
padding: 4px;
margin: 4px;
}
<div id="list"></div>
当您更改 innerHTML 时,浏览器重建元素的内容,丢弃所有附加的事件处理程序。改为使用 DOM 方法:
for (let i = 0; i < objects.length; i++){
var block = document.createElement('div');
block.setAttribute('id', i);
document.getElementById('list').appendChild( block );
block.addEventListener("mouseup", function() {
Select(this);
});
}
UPD:或者使用 insertAdjacentHTML
方法而不是重新定义 innerHTML
:
document.getElementById('list').insertAdjacentHTML(
'beforeend', "<div id='"+ i +"'>" + i + "</div>");
原因在于您追加的方式。 innerHtml +=
有效覆盖列表中的现有内容。因此,您添加和绑定的任何元素都会消失,每次都会添加新项目。
有几种方法可以完成这项工作。
首先,您可以附加元素而不是分配 innerHtml。
const items = ['taco', 'apple', 'pork'];
const list = document.getElementById("list");
for (const item of items) {
const el = document.createElement("div");
el.addEventListener('click', (e) => console.log(`clicked ${item}`));
el.innerText = item;
list.appendChild(el);
}
<div id="list"></div>
由于我们是附加一个明确的元素而不是覆盖内容,所以这会起作用。
更好的方法是使用 delegation。我们将单个事件处理程序分配到列表中并监听任何点击。然后我们找出点击了哪个特定元素。
const items = ['taco', 'apple', 'pork'];
const list = document.getElementById("list");
const add = document.getElementById("add");
list.addEventListener('click', (e) => {
const parent = e.target.closest("[data-item]");
if (parent != null) {
console.log(`clicked on ${parent.dataset['item']}`);
}
});
for (const item of items) {
list.innerHTML += `<div data-item="${item}">${item}</div>`;
}
add.addEventListener('click', () => {
const item = `item ${Date.now()}`;
list.innerHTML += `<div data-item="${item}">${item}</div>`;
})
<div id="list"></div>
<button id="add">add</button>
这里的神奇之处在于我们在父级上分配了一个事件处理程序,并使用 closest
来确定单击了哪个项目。为简单起见,我在这里使用 innerHTML
,但出于 security 的原因,应该避免使用它。
在适当的时候使用的一个好模式是 event delegation。它允许遵循 Don't Repeat Yourself 原则,使代码维护变得相当容易,并可能使脚本 运行 显着加快。在您的情况下,它避免了元素负责修改其自身内容的陷阱。
例如:
const container = document.getElementById('container');
container.addEventListener("click", toggleColor); // Events bubble up to ancestors
function toggleColor(event) { // Listeners automatically can access triggering events
const clickedThing = event.target; // Event object has useful properties
if(clickedThing.classList.contains("click-me")){ // Ensures this click interests us
clickedThing.classList.toggle("blue");
}
}
.click-me{ margin: 1em 1.5em; padding 1em 1.5em; }
.blue{ color: blue; }
<div id="container">
<div id="firstDiv" class="click-me">First Div</div>
<div id="firstDiv" class="click-me">Second Div</div>
</div>
我正在尝试制作一个脚本,将可交互对象信息注入标记页面的列表中。每当我尝试在 div 上添加一个 onclick 事件时,它工作正常,但是每当我尝试在 for 循环中添加更多事件时,它都不会按我预期的方式工作。
我在网页调试器中使用断点查看了发生了什么,我发现问题是它似乎在添加到下一个 [= 之前删除了前一个 div 上的事件28=]。最后,唯一剩下的事件是循环退出后的最后一个div。
我想将这些事件保留在我的所有 div 上,而不仅仅是最后一个...这似乎是什么问题?
var objects = ['Tom', 'Sauna', 'Traum'];
for (var i = 0; i < objects.length; i++){
document.getElementById('list').innerHTML += "<div class='item' id='"+ i +"'>" + objects[i] + "</div>";
document.getElementById(i).addEventListener("mouseup", function() {
Select(this);
});
}
function Select(char) {
console.log(char);
}
div.item {
border: 1px solid black;
padding: 4px;
margin: 4px;
}
<div id="list"></div>
当您更改 innerHTML 时,浏览器重建元素的内容,丢弃所有附加的事件处理程序。改为使用 DOM 方法:
for (let i = 0; i < objects.length; i++){
var block = document.createElement('div');
block.setAttribute('id', i);
document.getElementById('list').appendChild( block );
block.addEventListener("mouseup", function() {
Select(this);
});
}
UPD:或者使用 insertAdjacentHTML
方法而不是重新定义 innerHTML
:
document.getElementById('list').insertAdjacentHTML(
'beforeend', "<div id='"+ i +"'>" + i + "</div>");
原因在于您追加的方式。 innerHtml +=
有效覆盖列表中的现有内容。因此,您添加和绑定的任何元素都会消失,每次都会添加新项目。
有几种方法可以完成这项工作。
首先,您可以附加元素而不是分配 innerHtml。
const items = ['taco', 'apple', 'pork'];
const list = document.getElementById("list");
for (const item of items) {
const el = document.createElement("div");
el.addEventListener('click', (e) => console.log(`clicked ${item}`));
el.innerText = item;
list.appendChild(el);
}
<div id="list"></div>
由于我们是附加一个明确的元素而不是覆盖内容,所以这会起作用。
更好的方法是使用 delegation。我们将单个事件处理程序分配到列表中并监听任何点击。然后我们找出点击了哪个特定元素。
const items = ['taco', 'apple', 'pork'];
const list = document.getElementById("list");
const add = document.getElementById("add");
list.addEventListener('click', (e) => {
const parent = e.target.closest("[data-item]");
if (parent != null) {
console.log(`clicked on ${parent.dataset['item']}`);
}
});
for (const item of items) {
list.innerHTML += `<div data-item="${item}">${item}</div>`;
}
add.addEventListener('click', () => {
const item = `item ${Date.now()}`;
list.innerHTML += `<div data-item="${item}">${item}</div>`;
})
<div id="list"></div>
<button id="add">add</button>
这里的神奇之处在于我们在父级上分配了一个事件处理程序,并使用 closest
来确定单击了哪个项目。为简单起见,我在这里使用 innerHTML
,但出于 security 的原因,应该避免使用它。
在适当的时候使用的一个好模式是 event delegation。它允许遵循 Don't Repeat Yourself 原则,使代码维护变得相当容易,并可能使脚本 运行 显着加快。在您的情况下,它避免了元素负责修改其自身内容的陷阱。
例如:
const container = document.getElementById('container');
container.addEventListener("click", toggleColor); // Events bubble up to ancestors
function toggleColor(event) { // Listeners automatically can access triggering events
const clickedThing = event.target; // Event object has useful properties
if(clickedThing.classList.contains("click-me")){ // Ensures this click interests us
clickedThing.classList.toggle("blue");
}
}
.click-me{ margin: 1em 1.5em; padding 1em 1.5em; }
.blue{ color: blue; }
<div id="container">
<div id="firstDiv" class="click-me">First Div</div>
<div id="firstDiv" class="click-me">Second Div</div>
</div>