循环中的D3不能按预期工作

时间:2015-01-18 00:20:59

标签: javascript svg d3.js

我在rect元素中有16个svg,其ID为photo0photo1 ... photo15

我想为每个单元格创建mouser over效果,因此当用户将鼠标悬停在某个rect上时,它会抓取某些图片来填充照片单元格。

然而,当我逐一编写mouserover时,它完美无缺。如下所示:

d3.select('#photo'+0).on("mouseover", function(d){
        d3.select('#photo').selectAll('img').remove();
        d3.select('#photo').append("img")
                .attr("src","/path/images/" + 0 + ".jpeg" )
                .attr("x", -8)
                .attr("y", -8)
                .attr("width","500px")                  
                .attr("height","500px"); 
});

d3.select('#photo'+1).on("mouseover", function(d){
        d3.select('#photo').selectAll('img').remove();
        d3.select('#photo').append("img")
                .attr("src","/path/images/" + 1 + ".jpeg" )
                .attr("x", -8)
                .attr("y", -8)
                .attr("width","500px")                  
                .attr("height","500px"); 
});

d3.select('#photo'+2).on("mouseover", function(d){
        d3.select('#photo').selectAll('img').remove();
        d3.select('#photo').append("img")
                .attr("src","/path/images/" + 2 + ".jpeg" )
                .attr("x", -8)
                .attr("y", -8)
                .attr("width","500px")                  
                .attr("height","500px"); 
});
...

然而,当我把它们放入for循环中时,它不会起作用。似乎每个细胞都以某种方式调用最后一张图片,任何人都可以帮忙吗?

for(i=0;i<16;i++){
d3.select('#photo'+i).on("mouseover", function(d){
        d3.select('#photo').selectAll('img').remove();
        d3.select('#photo').append("img")
                .attr("src","/path/images/" + i + ".jpeg" )
                .attr("x", -8)
                .attr("y", -8)
                .attr("width","500px")                  
                .attr("height","500px"); 
});
}

1 个答案:

答案 0 :(得分:2)

以下是发生的事情:

for(i=0;i<16;i++){
d3.select('#photo'+i).on("mouseover", function(d){
        d3.select('#photo').selectAll('img').remove();
        d3.select('#photo').append("img")
                .attr("src","/path/images/" + i + ".jpeg" ) // Accesses `i` from closure, 
                                                            // will always be last value
                                                            // that met `i < 16`
                .attr("x", -8)
                .attr("y", -8)
                .attr("width","500px")                  
                .attr("height","500px"); 
});
}

您需要的是一个功能值,它可以满足您的需求,然后使用该值而不是在每次迭代中创建新函数。这是一个例子(免责声明,我从未使用过d3,这是基于你的代码):

var populateCell = function(i) { 
  d3.select('#photo').selectAll('img').remove(); 
  d3.select('#photo').append('img')
      .attr('src', '/path/images/' + i + '.jpeg')
      .attr('x', -8)
      .attr('y', -8)
      .attr('width', '500px')
      .attr('height', '500px');
};

var selectCell = function(i) { 
  return d3.select('#photo' + i); 
}; 

var i = 0; // explicit definition of `i`
for (; i < 16; i++) { 
  selectCell(i).on('mouseover', populateCell(i)); 
}

以下是使用JavaScript并假设console的意外行为示例,以说明原则:

// Will output 16, 16 times. 

var i = 0; 
var f = [];
for (; i < 16; i++) { 
  f.push(function() { // new function created in each iteration 
    console.log(i);   // captures access to `i`, outputs value of `i` at
                      // at time function is called. 
  });
}

for (var j = 0; j < f.length; j++) { 
  f[j]();
}

这是一个所需行为的例子,我修改了一些修正案。

// Will output 0 through 16.  
var i = 0; 
var f = [];
var fn = function(i) { 
  console.log(i);
}

for (; i < 16; i++) { 
  f.push(fn(i));
}

for (var j = 0; j < f.length; j++) { 
  f[j]();
}

更多信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures