我在jQuery插件中有以下代码,我正在编写标签云。我传入的数据格式为[{tag:“028”,count:15},{tag:“101”,count:357}]。我现在正在创建云作为跨度,根据计数对大小进行标准化。正确创建跨距,即它们具有正确的大小和文本。我添加了几个事件,点击和鼠标事件。无论点击什么跨度,它总是向我显示数组中最后一个元素的警报。
在尝试调试正在进行的操作时,我将元素创建代码提取为单独的函数。一旦我这样做,点击事件就能正常工作,即点击事件显示点击了什么跨度的正确数据。
我认为两个版本都会产生相同的结果。为什么一旦我将元素创建提取到它自己的函数中,click事件就会起作用?
这是不起作用的版本:
for (var i = 0; i < tagList.length; ++i) {
if (tagList[i] != null) {
var tagValue = tagList[i].tag;
var tagCount = tagList[i].count;
var size = getNormalizedSize(tagCount);
var theSpan = getText(tagValue, tagCount); // <span style="font-size: {1}em">{0}<\/span>
var theAlert = getAlert(tagValue, tagCount); // "Project {0} is has logged in {1} drawings"
var newElement = $(theSpan);
newElement.click(function() {
alert(theAlert); // Always shows data from last element in array
}).mouseenter(function(event) {
$(this).css('backgroundColor', '#FFC');
}).mouseleave(function() {
$(this).css('backgroundColor', '#FFF');
});
this.append(newElement).append(" ");
}
}
这是有效的版本:
for (var i = 0; i < tagList.length; ++i) {
if (tagList[i] != null) {
var tagValue = tagList[i].tag;
var tagCount = tagList[i].count;
var tagElem = buildElement(tagValue, tagCount);
this.append(tagElem).append(" ");
}
}
function buildElement(tagValue, tagCount) {
var size = getNormalizedSize(tagCount);
var theSpan = getText(tagValue, tagCount); // <span style="font-size: {1}em">{0}<\/span>
var theAlert = getAlert(tagValue, tagCount); // "Project {0} is has logged in {1} drawings"
var newElement = $(theSpan);
newElement.click(function() {
alert(theAlert);
}).mouseenter(function(event) {
$(this).css('backgroundColor', '#FFC');
}).mouseleave(function() {
$(this).css('backgroundColor', '#FFF');
});
return newElement;
}
答案 0 :(得分:3)
您正在每个处理程序中捕获相同的变量,作用于循环。然后,当调用处理程序时,它包含分配给它的最后一个值。将它移动到函数时,变量的作用域是函数调用的那个实例,因此它对于循环的每次迭代都是不同的,并且在该迭代期间赋值。
答案 1 :(得分:1)
这是因为这两种解决方案具有不同的范围规则。在第一个中没有函数调用,这意味着theAlert
只定义一次,而您只是更新click
回调处理程序引用的引用。在第二个中,您通过调用buildElement
来创建新范围,在这种情况下,这意味着为列表中的每个标记定义theAlert
,并且仅在定义时更新,因此每个{{1} } closure指的是不同的变量。
这里的关键是闭包内的变量(例如click
回调)在运行之前不会被解析。这是一个简单的例子,说明了这一点:
click
因此,即使在值更改之前创建了引用var name = 'John';
setTimeout(function(){ alert(name); }, 1000);
name = 'Joe';
的闭包时,它实际上并非运行直到稍后,因此将被警告的名称为“Joe”而不是“约翰”。