如何将变量的当前值传递给事件处理程序 (Javascript)
How can I pass the current value of a variable to an event handler (Javascript)
对此有点尴尬,但可以说我在一个名为 iBlock 的 parent 元素中有这组 'child' 元素,并且在一个函数中我添加了一个 'onclick' 事件到每个 child,像这样...
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);
}
有点意外的是,无论我点击哪个child元素,这个例子中的处理程序总是显示小于children总数的那个。但我知道 total - 1 是变量 'i' 在循环结束之前达到的最高数字,并且处理程序除了事件发生时的变量值外不会传递任何东西。但是理解这并不能帮助我回答这两个问题...
- 如果我真的想让 onclick 函数中的第二个参数 传递 值 'i' 实际上是 在 onclick 处理程序是
附加 ,我可以传递什么来代替原始 'i' 变量?
- 由于 'i' 是由函数中的 'var' 关键字声明的
添加了处理程序,为什么它仍然有一个值(在这种情况下
总 - 1),安装功能恢复后?如果处理程序显示 'undefined',我就不会那么惊讶了。我意识到
这是一个 'scope of variables' 问题,但我确定它是相关的。
关于第一个问题,我试过传递:i+'x',只是为了把它变成一个字符串,希望它能生成一个唯一的实例。这也不起作用...并且元素 'clicked' on 只会触发显示“4x”的处理程序。我还尝试创建第二个变量,作为一个字符串,然后提取数字部分,并将最终的 'value' 从新变量传递给 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)};
}
}
没有变化。处理程序仍然在我单击的任何元素上显示总计 1。
我看过关于这个主题的类似帖子,例如 Extract a number from a string (JavaScript),但我仍然不明白第一个问题的补救方法。
PS(我没有尝试使用 'let' 而不是 'var' 创建变量,因为我非常致力于支持某些较旧的浏览器)。 编辑:如果可能的话,我想在 'old browsers' 中包括 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 属性.
但这似乎 'kludge',所以我仍然对答案感兴趣!
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 创建一个闭包来捕获迭代器变量。
More about closures
More about 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.
More about event delegation
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 方法的好时机。它所做的是 return 一个新版本的函数,其中建立了 this
上下文和参数 pre-populated.
for (var i = 0; i < iTotal; i++) {
el = iBlock.children[i];
el.addEventListener('click', myHandler.bind(this, this, i));
}
每个 el
接收一个具有前两个参数的唯一函数 pre-populated。
在仔细考虑所有提供的答案后,我将在我的 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' 的值作为第二个 arg 发送到处理程序。然而,无论出于何种原因,当添加的元素 'myIndex' 被分配给 'i' 时,它会保留分配时的标识值。因此,当调用处理程序时,可以通过 'this.myIndex' 访问它。我不完全理解为什么这样做会导致 'i' 按值传递而不是引用/实例本身,但它有效。
没有人提出不这样做的任何理由,就添加的代码而言,它似乎是最不复杂的。我认为唯一的风险是选择一个与将来的 'standard' 属性冲突的名称。也许这就是为什么我看到建议 tp call add on sttributes something like "data-myindex"。但无论哪种方式,它似乎都适用于我尝试过的所有 IE 平台,包括 IE8、Edge、Chrome 和 Firefox,以及通过 Win-10 上的最新版本为 Windows-XP 分发的最后一个平台。它适用于 IOS-7 之前的 Mobil Safari,以及我能够测试的所有 android 设备。仍然对建议持开放态度,但有时,正如他们所说,“最简单的解决方案是最好的”
对此有点尴尬,但可以说我在一个名为 iBlock 的 parent 元素中有这组 'child' 元素,并且在一个函数中我添加了一个 'onclick' 事件到每个 child,像这样...
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);
}
有点意外的是,无论我点击哪个child元素,这个例子中的处理程序总是显示小于children总数的那个。但我知道 total - 1 是变量 'i' 在循环结束之前达到的最高数字,并且处理程序除了事件发生时的变量值外不会传递任何东西。但是理解这并不能帮助我回答这两个问题...
- 如果我真的想让 onclick 函数中的第二个参数 传递 值 'i' 实际上是 在 onclick 处理程序是 附加 ,我可以传递什么来代替原始 'i' 变量?
- 由于 'i' 是由函数中的 'var' 关键字声明的 添加了处理程序,为什么它仍然有一个值(在这种情况下 总 - 1),安装功能恢复后?如果处理程序显示 'undefined',我就不会那么惊讶了。我意识到 这是一个 'scope of variables' 问题,但我确定它是相关的。
关于第一个问题,我试过传递:i+'x',只是为了把它变成一个字符串,希望它能生成一个唯一的实例。这也不起作用...并且元素 'clicked' on 只会触发显示“4x”的处理程序。我还尝试创建第二个变量,作为一个字符串,然后提取数字部分,并将最终的 'value' 从新变量传递给 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)};
}
}
没有变化。处理程序仍然在我单击的任何元素上显示总计 1。
我看过关于这个主题的类似帖子,例如 Extract a number from a string (JavaScript),但我仍然不明白第一个问题的补救方法。
PS(我没有尝试使用 'let' 而不是 'var' 创建变量,因为我非常致力于支持某些较旧的浏览器)。 编辑:如果可能的话,我想在 'old browsers' 中包括 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 属性.
但这似乎 'kludge',所以我仍然对答案感兴趣!
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 创建一个闭包来捕获迭代器变量。
More about closures
More about 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.
More about event delegation
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 方法的好时机。它所做的是 return 一个新版本的函数,其中建立了 this
上下文和参数 pre-populated.
for (var i = 0; i < iTotal; i++) {
el = iBlock.children[i];
el.addEventListener('click', myHandler.bind(this, this, i));
}
每个 el
接收一个具有前两个参数的唯一函数 pre-populated。
在仔细考虑所有提供的答案后,我将在我的 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' 的值作为第二个 arg 发送到处理程序。然而,无论出于何种原因,当添加的元素 'myIndex' 被分配给 'i' 时,它会保留分配时的标识值。因此,当调用处理程序时,可以通过 'this.myIndex' 访问它。我不完全理解为什么这样做会导致 'i' 按值传递而不是引用/实例本身,但它有效。
没有人提出不这样做的任何理由,就添加的代码而言,它似乎是最不复杂的。我认为唯一的风险是选择一个与将来的 'standard' 属性冲突的名称。也许这就是为什么我看到建议 tp call add on sttributes something like "data-myindex"。但无论哪种方式,它似乎都适用于我尝试过的所有 IE 平台,包括 IE8、Edge、Chrome 和 Firefox,以及通过 Win-10 上的最新版本为 Windows-XP 分发的最后一个平台。它适用于 IOS-7 之前的 Mobil Safari,以及我能够测试的所有 android 设备。仍然对建议持开放态度,但有时,正如他们所说,“最简单的解决方案是最好的”