当循环通过dom元素时;这个“;以及何时使用循环变量
when looping through dom elements, when to use "this" and when to use loop variable?
我是一个javascript/dom noob,主要来自Python和Clojure,在dom中引用事物的语义真的让我很困惑。
以下是我刚刚写在chrome扩展中的一些代码的摘录,用于识别与testdownloadpage
中定义的标准匹配的页面子集,然后将eventlistener挂在pdf下载链接上以拦截和抓取它们
function hanglisteners() {
if (testdownloadpage(url)) {
var links = document.getElementsByTagName("a");
for (i = 0, len = links.length; i < len; i++) {
var l = links[i];
if (testpdf(l.href)) {
l.addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
getlink(this.href);
}, false);
};
};
};
};
最初,我在底部调用了getlink
,读取了getlink(l.href)
,我(显然很天真)认为,该代码会循环通过每个匹配的链接,并在该链接的url上的每个调用getlink的链接上加上一个监听器。但这根本不起作用。我试着用this.href
替换l.href
,只是猜测,它开始工作了。
我不知道为什么this.href
有效,而l.href
无效。我的最佳猜测是,javascript解释器不会在addEventListener
调用中评估l.href
,直到稍后l
更改为其他内容(??)。但我不知道为什么应该这样,也不知道如何知道javascript何时对函数调用的参数求值,何时不求值。。。
现在我在担心testpdf(l.href)
的上级电话。该函数的目的是在将侦听器挂在链接上之前检查以确保链接是pdf下载链接。但这是否会在循环中评估l.href
,从而正确评估每个链接?或者这也会在循环后的某个点进行评估,我应该使用this.href
吗?
能否请一些灵魂向我解释一下底层语义,这样我就不必猜测引用循环变量或引用this
是否正确了?谢谢
编辑/添加:
人们一致认为,我的问题是一个(众所周知的)问题,循环中的内部函数是范围泄漏的受害者。当调用它们时,除非内部函数关闭它使用的所有变量,否则它们最终会绑定到循环的最后一个元素。但是:为什么这个代码会起作用?
var links = document.getElementsByTagName("a");
for (i = 0, len = links.length; i < len; i++) {
let a = links[i];
a.addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
console.log(a.href);
});
};
<html>
<head>
<title>silly test</title>
</head>
<body>
<p>
<a href="link1">Link 1</a>
<a href="link2">link2</a>
<a href="link3">link 3</a>
</p>
</body>
</html>
根据这些答案,我希望点击每个链接来记录"链接3",但它们实际上记录了正确/预期的天真结果。。。
问题的出现是因为在侦听器实际启动时,循环变量已经发生了更改。您应该能够通过下面的示例看到这一点。
function setup(){
var root = document.getElementById('root');
var info = document.createElement('div');
for (var i = 0; i < 5; i++){
// create a button
var btn = document.createElement('button');
// set up the parameters
btn.innerHTML = i
btn.addEventListener('click', function(event){
info.innerHTML = ('i = ' + i + ', but the button name is ' + event.target.innerHTML);
})
// add the button to the dom
root.appendChild(btn)
}
var info = document.createElement('div');
info.innerHTML = 'The Value of i is ' + i
root.appendChild(info)
}
// run our setup function
setup();
<div id="root">
</div>
所以您需要做的是保存一份l
的副本以备将来使用。CCD_ 16正是出于这个原因而自动地将该元素存储在CCD_。然而,如果出于某种原因不想使用this
,另一个非常好的方法是使用生成器函数(创建函数的函数)。
例如:
function clickListener(i, info){
return function(event){
info.innerHTML = ('i = ' + i + ', but the button name is ' + event.target.innerHTML);
}
}
通过这种方式,我们可以将变量i
和info
放入稍后调用的侦听器函数的作用域中,而不会更改值。
将此代码放入上面的示例中,我们得到以下片段
function clickListener(i, info){
return function(event){
info.innerHTML = ('i = ' + i + ', but the button name is ' + event.target.innerHTML);
}
}
function setup(){
var root = document.getElementById('root');
var info = document.createElement('div');
for (var i = 0; i < 5; i++){
// create a button
var btn = document.createElement('button');
// set up the parameters
btn.innerHTML = i
// use the generator to get a click handler
btn.addEventListener('click', clickListener(i, info))
// add the button to the dom
root.appendChild(btn)
}
info.innerHTML = 'The Value of i is ' + i
root.appendChild(info)
}
// run our setup function
setup();
<div id="root">
</div>
编辑:回答修改后的问题
在代码的第二次迭代中,使用ES6
引入的let
关键字。这个关键字是专门为javascript变量提供更传统的块作用域而设计的。在我们的例子中,它使变量的作用域为循环的特定迭代。类似的例子可以在MDN上看到。如果您可以在应用程序/构建系统中支持ES6
,那么使用let是解决问题的好方法。
当您使用事件侦听器的功能时,this
绑定到事件侦听器所针对的DOM元素。想象一下以下代码:
var links = document.getElementsByTagName("a");
for (i = 0, len = links.length; i < len; i++) {
let a = links[i];
a.addEventListener("click", function(e) {
console.log(this === a); // => true
})
}
这两者是相同的,所以无论何时
- 如何重写下面的函数,使其不会't用于循环
- 为什么这个For循环会使浏览器实验室崩溃
- 使循环滑块具有延迟
- 如何使for循环等待回调返回值
- 为什么这会造成一个无休止的循环并使我的浏览器崩溃
- for 循环使输出并排 jQuery
- 使用 setTimeout() 循环,使矩形变大,直到它到达画布边缘,然后重置
- 为什么这 while 循环会使浏览器崩溃
- JSlint - 在 for 循环中使函数与评估函数
- 循环jquery $.get,如何知道循环何时完成
- $.extend() = 与服务器循环一起使用时不扩展
- node.js事件循环何时终止
- 我想添加一个循环,使我的加密更难破解
- 需要帮助创建一个循环,使一个对象将重新出现
- 浏览器的事件循环何时开始?
- 更新循环,使13是num1和num2之间的数字
- 如何在基于数据库数据的ng-repeat循环中使Kendo-UI Editor只读
- 如何知道async for循环何时完成
- while循环中的If else语句,其中If else语句决定while循环何时停止
- javascript循环会使我的页面最终卡住吗?