我今天阅读了几篇关于将循环生成的变量传递给函数的文章,甚至成功地重现了我看到的一些问题,并正确地实现了这些问题的修复。但是,有一个我似乎无法找到答案。我编写了一个循环,用于将项目添加到下拉列表中,同时它通过 id
为这些项目的 click
事件创建事件监听器。我已验证 id
是唯一的并且没有任何冲突。但是,当我检查我的 Items
数组时,只有最后一项与 onclick
相关联,因此我相信我遇到了另一个范围问题。
class Test {
Items = [];
constructor(containerID) {
this.ContainerID = containerID;
this.Container = document.getElementById(containerID);
}
AddItems(items) {
for (let i = 0; i < items.length; i++) {
let itemID = 'li-' + this.ContainerID + '-' + this.Items.length;
this.Container.innerHTML += '<li id="' + itemID + '">' + items[i] + '</li>';
let item = document.getElementById(itemID);
item.addEventListener("click", this.OnClick.bind(this, item));
this.Items.push(item);
}
}
OnClick(item) {
alert(item.innerText);
}
}
var testObject = new Test("test-container");
testObject.AddItems(["Option 1", "Option 2", "Option 3", "Option 4"]);
html, body {
height: 100%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
ul {
padding: 0;
margin: 0;
list-style-type: none;
max-height: 200px;
overflow-y: auto;
}
ul > li {
padding: 5px;
margin: 5px;
cursor: pointer;
background-color: #333;
color: #fff;
}
<ul id="test-container"></ul>
我在这里关注的功能是 AddItems
和 OnClick
。当我将一个项目添加到我的项目集合时,我需要将该项目的 click
事件绑定(bind)到 OnClick
函数,我将在该函数中填充 textbox
使用该项目的 innerText
进行控制。最后一部分很简单,但出于某种原因,我什至无法获得 click
事件来注册这些元素。
我试过:
- 仅传递元素的
id
。 - 传递元素。
- 使用
var
而不是let
。
我尝试过的所有方法都只导致最后一项引发点击事件。我在当前环境之外遇到并解决了同样的问题,没有遇到任何问题。即使是我发现的最流行的解决方案也无法解决这个问题:
AddItems(items) {
for (let i = 0; i < items.length; i++) {
(function(instance, _i) {
let itemID = 'li-' + instance.ContainerID + '-' + instance.Items.length;
instance.Container.innerHTML += '<li id="' + itemID + '">' + items[_i] + '</li>';
let item = document.getElementById(itemID);
item.addEventListener("click", instance.OnClick.bind(instance, item));
instance.Items.push(item);
})(this, i);
}
}
这仍然只会导致最后一个项目具有已注册的 click
事件。
更新
奇怪的是,如果我在我的实现代码中对 AddItems
进行二次调用,要添加到 Items
数组的最后一项仍然是唯一具有注册了一个 click
事件。我什至添加了一个 setTimeout
来查看 JavaScript
是否正在合并我的调用,并且仍然得到相同的结果。
var testObject = new Test("test-container");
testObject.AddItems(["Option 1", "Option 2", "Option 3", "Option 4"]);
setTimeout(function() {
testObject.AddItems(["Option 5", "Option 6", "Option 7", "Option 8"]);
}, 5000);
如何将我的循环范围变量传递给 click
事件的绑定(bind) function
?
最佳答案
当您连接 innerHTML
时Container
的属性(property),您将替换 DOM 元素的全部内容,从而丢失所有已绑定(bind)的事件。当你处理完 innerHTML
时,你要么绑定(bind)事件的 Container
(这将是一种解决方法,但仍然很糟糕)或者您只需使用正确的方法:appendChild
:
class Test {
Items = [];
constructor(containerID) {
this.ContainerID = containerID;
this.Container = document.getElementById(containerID);
}
AddItems(items) {
let itemID, item;
for (let i = 0; i < items.length; i++) {
itemID = 'li-' + this.ContainerID + '-' + this.Items.length;
item = document.createElement('li');
item.id = itemID;
item.innerHTML = items[i];
this.Container.appendChild(item);
item.addEventListener("click", this.OnClick.bind(this, item))
this.Items.push(item);
}
}
OnClick(item) {
alert(item.innerText);
}
}
var testObject = new Test("test-container");
testObject.AddItems(["Option 1", "Option 2", "Option 3", "Option 4"]);
html,
body {
height: 100%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
ul {
padding: 0;
margin: 0;
list-style-type: none;
max-height: 200px;
overflow-y: auto;
}
ul>li {
padding: 5px;
margin: 5px;
cursor: pointer;
background-color: #333;
color: #fff;
}
<ul id="test-container"></ul>
编辑:没有直接关系,但我认为应该提及,出于教育目的:应尽可能避免为每个 child 绑定(bind)一个事件,因为:
- 它更重(在处理大量 child 时很明显)
- 如果您动态更新子项,则没有 API 来获取没有特定绑定(bind)的子项;并且忘记在添加新成员后立即绑定(bind)它们是一个容易犯的错误
但是您可以利用事件冒泡:您只在父级上绑定(bind)一个事件。当您进行绑定(bind)时,这甚至适用于 DOM 中不存在的 child 。您更新的示例:
class Test {
Items = [];
constructor(id) {
this.Container = document.getElementById(id);
this.Container.addEventListener("click", this.OnClick.bind(this));
}
AddItems(items) {
let item;
for (let i = 0; i < items.length; i++) {
item = document.createElement("li");
item.id = `li-${this.Container.id}-${this.Items.length}`;
item.innerHTML = items[i];
this.Container.appendChild(item);
this.Items.push(item);
}
}
OnClick(event) {
const item = event.target.closest(`#${this.Container.id} li`);
if (item) {
alert(item.innerText);
}
}
}
const testObject = new Test("test-container");
testObject.AddItems(["Option 1", "Option 2", "Option 3", "Option 4"]);
html,
body {
height: 100%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
}
ul {
padding: 0;
margin: 0;
list-style-type: none;
max-height: 200px;
overflow-y: auto;
}
ul>li {
padding: 5px;
margin: 5px;
cursor: pointer;
background-color: #333;
color: #fff;
}
<ul id="test-container"></ul>
如您所见,即使绑定(bind)是在 constructor
中完成的,这发生在您添加 <li>
之前s,它可以工作,如果您删除它们并添加其他人,它将继续工作。而且它只是一种绑定(bind),适用于所有现在和 future 的 child 。
请注意,我更改了 OnClick
方法,不再覆盖默认参数(即 event
)并使用它来选择 <li>
调用方法时。
关于javascript - 只有最后一次循环迭代才能成功注册我的点击事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57982511/