for 循环中的 D3 无法按预期工作

D3 inside for loop not working as expected

我在一个 svg 元素中有 16 个 rect,其 id 是 photo0photo1... photo15

我想为每个单元格创建鼠标悬停效果,这样当用户将鼠标悬停在某个 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"); 
});
}

这是正在发生的事情:

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