提问人:Randy 提问时间:9/26/2020 最后编辑:Randy 更新时间:9/28/2020 访问量:262
如何将变量的当前值传递给事件处理程序 (Javascript)
How can I pass the current value of a variable to an event handler (Javascript)
问:
对此有点尴尬,但假设我在一个名为 iBlock 的父元素中有这组“子”元素,并且在一个函数中,我正在为每个子元素添加一个“onclick”事件,就像这样......
someFunction(iBlock) {
var el, iTotal = iBlock.children.length;
for (var i= 0; i < iTotal; i++) {
el=iBlock.children[i];
el.onclick=function(event){myHandler(this, i)};
}
}
因此,给定这样的 onclick 处理程序......
function myHandler(elem, n) {
alert("number is: " + n);
}
有点令人惊讶的是,无论我单击哪个子元素,此示例中的处理程序都会显示小于子元素总数的元素。但我知道总数 - 1 是变量“i”在循环结束前达到的最高数字,并且处理程序不会传递任何东西,只会传递事件发生时变量的值。但是理解这一点并不能帮助我回答这两个问题......
- 如果我真的希望 onclick 函数中的第二个参数传递值“i”,那么实际上在 onclick 处理程序 附加,我可以传递什么来代替原始的“i”变量?
- 由于 'i' 是由函数中的 'var' 关键字声明的 添加了处理程序,为什么它仍然有一个值(在这种情况下 总计 - 1),安装功能是否返回?如果处理程序显示“未定义”,我会不那么惊讶。我意识到 这是一个“变量范围”的问题,但我敢肯定它是相关的。
关于第一个问题,我尝试传递:i+'x',只是为了把它变成一个字符串,希望它能生成一个唯一的实例。那也行不通......元素“单击”只会触发显示“4x”的处理程序。我还尝试创建第二个变量,作为字符串,然后提取数字部分,并将最终的“值”从新变量传递给 onclick 处理程序,如下所示......
someFunction(iBlock) {
var el, iTotal = iBlock.children.length;
for (var i= 0; i < iTotal; i++) {
el=iBlock.children[i];
var n= i+'x'; // make 'i' into a new longer string variable
var r = /\d+/; // regex, to capturei digits
n = n.match(r); // convert the string to only contain the digits
el.onclick=function(event){myHandler(this, n)};
}
}
没有变化。处理程序仍然在我单击的任何元素上显示 total-1。
我看过关于这个主题的类似帖子,例如从字符串中提取数字 (JavaScript),但我仍然不明白我的第一个问题的补救措施。
PS(我没有尝试使用“let”而不是“var”创建变量,因为我非常致力于支持某些较旧的浏览器)。编辑:如果可能的话,我想将IE-8包含在那些“旧浏览器”中。我意识到我不能总是这样做,但在这种情况下,我愿意这样做。
编辑:我找到了一个有趣的解决方法,即在添加处理程序的循环中将我自己的变量添加到每个元素中,如下所示......
someFunction(iBlock) {
var el, iTotal = iBlock.children.length;
for (var i= 0; i < iTotal; i++) {
el=iBlock.children[i];
el.onclick=function(event){myHandler(this, i)};
el.myIndex=i;
}
}
然后当然,在我的处理程序中,无论如何我都在传递“this”,我可以通过检查“this.myIndex”来正确恢复“i”的所需值。我听说这很危险,因为它可能会与未来的 DOM 属性发生冲突。
但这似乎是“笨拙的”,所以我仍然对答案感兴趣!
答:
el
没有被分配到任何东西。尝试:
someFunction(iBlock) {
var iTotal = iBlock.children.length;
for (var i= 0; i < iTotal; i++) {
iBlock.children[i].onclick=function(event){myHandler(this, i)};
}
}
评论
您需要使用 IIFE 创建一个闭包来捕获迭代器变量。
function handleClick(element, n) {
alert("number is: " + n);
}
function iterateOverChildNOde(parentNode) {
var el = parentNode.children
var iTotal = parentNode.children.length;
for (var i= 0; i < iTotal; i++) {
(function(iterator){
el[i].onclick=function(event){handleClick(el[iterator], iterator)};
})(i)
}
}
iterateOverChildNOde(document.getElementById('parent'));
<div id="parent">
<div class="child">C</div>
<div class="child">L</div>
<div class="child">I</div>
<div class="child">I</div>
<div class="child">C</div>
<div class="child">L</div>
</div>
此外,您需要意识到这是一种不好的做法。您应该只将事件处理程序附加到父元素,然后使用 e.target。
function handleClick(element, n) {
alert("number is: " + n);
}
document.getElementById('parent').onclick=function(e){handleClick(e.target, Array.prototype.indexOf.call(this.children, e.target))}
<div id="parent">
<div class="child">C</div>
<div class="child">L</div>
<div class="child">I</div>
<div class="child">I</div>
<div class="child">C</div>
<div class="child">L</div>
</div>
评论
如注释中所述,问题在于您的内联单击处理程序函数引用了闭包中的变量,该变量在循环时发生了变异。每个处理程序都引用该循环变量,因此在循环结束时,将设置为 ,并且所有处理程序都引用相同的值。i
i
iTotal
这可能是使用函数的 bind 方法的好时机。这样做的目的是返回函数的新版本,其中已建立上下文并预填充了参数。this
for (var i = 0; i < iTotal; i++) {
el = iBlock.children[i];
el.addEventListener('click', myHandler.bind(this, this, i));
}
每个函数都接收一个唯一的函数,其中预填充了前两个参数。el
评论
bind
bind
在仔细考虑了提供的所有答案之后,我将坚持使用我发现并发布的解决方案,在我的 OP 的最终编辑中。 具体来说,当我添加事件时,它会为每个元素分配一个新属性......
someFunction(iBlock) {
var el, iTotal = iBlock.children.length;
for (var i= 0; i < iTotal; i++) {
el=iBlock.children[i];
el.onclick=function(event){myHandler(this)};
el.myIndex=i;
}
}
在这里,由于注意到的问题,我什至不再尝试将“i”的值作为第二个参数发送到处理程序。然而,无论出于何种原因,当添加的元素“myIndex”被分配给“i”时,它会在分配时保留标识值。因此,当调用处理程序时,可以通过“this.myIndex”访问它。我不完全理解为什么这样做会导致“i”被值传递,而不是引用/实例本身,但它有效。
没有人提供任何理由不这样做,而且就添加的代码而言,它似乎是最不复杂的。我相信唯一的风险是选择一个与未来的“标准”属性相冲突的名称。也许这就是为什么我看到建议 tp 调用添加属性之类的东西,例如“data-myindex”。但无论哪种方式,它似乎都适用于我尝试过的所有 IE 平台,包括 Edge、Chrome 和 Firefox,直到最新的 Win-10 为 Windows-XP 分发的平台。它适用于 Mobil Safari 回到 IOS-7,以及我能够测试的所有 Android 设备。仍然对建议持开放态度,但有时,正如他们所说,“最简单的解决方案是最好的”
评论
el.onclick=(function(localn){return function(event){myHandler(this, localn)};})(n);
el
el.onclick=....
el = iBlock.children[i]
还是什么?