d3 鼠标悬停事件不适用于所有矩形

d3 mouseover event doesnot work for all rect

我有一个带有鼠标悬停事件的简单条形图。在数据更新工作之前,代码仅在函数 changeData 之外。但是在数据发生变化后,仅在函数内部运行代码。 我如何编写函数来处理所有矩形的 mouseover/out?

使用按钮 OpacityNow 我可以更改所有矩形的不透明度,无论数据是否已更改。

感谢您的帮助。

  var myCanvas1 = d3.select("#chart1")
   .append("svg")
   .attr("width", svgWidth + margin.left + margin.right)
   .attr("height", svgHeight + margin.top + margin.bottom)
   .style("background", "aliceblue")
   .append("g")
   .attr("transform","translate(" + margin.left + "," + margin.top + ")")
   .append("g");

   //append rectangles to svg container
  var Bar = myCanvas1.selectAll("rect")
    .data(dataArray1)
    .enter()
    .append("rect")
    .style("fill", "steelblue")
    .attr("x", function(d, i) { return x(i); })
    .attr("width", x.bandwidth())
    .attr("y", function(d) { return (svgHeight - y(+d.balance)); } )
    .attr("height", function(d) { return y(+d.balance); })
    //.on("mouseover", function() {d3.select(this).attr("opacity", 0.5)})
    //.on("mouseout", function() {d3.select(this).attr("opacity", 1)});

  //function for button click event
   function changeData(myDataArray) {

    var Bars = myCanvas1.selectAll("rect");
    var NewBars = Bars.data(eval(myDataArray));
    //enter new data
    NewBars.enter()
        .append("rect")
        .style("fill", "steelblue")
        //.on("mouseover", function() {d3.select(this).attr("opacity", 0.5)})
        //.on("mouseout", function() {d3.select(this).attr("opacity", 1)})
        .transition()
        .duration(duration1)
        .attr("x", function(d, i) { return x(i); })
        .attr("width", x.bandwidth())
        .attr("y", function(d) { return (svgHeight - y(+d.balance)); } )
        .attr("height", function(d) { return y(+d.balance); });
    //exit data
    NewBars.exit()
        .remove();
    //update data
    NewBars.transition()
        .duration(duration1)
        .style("fill", "steelblue")
        .attr("x", function(d, i) { return x(i); })
        .attr("width", x.bandwidth())
        .attr("y", function(d) { return (svgHeight - y(+d.balance)); } )
        .attr("height", function(d) { return y(+d.balance); });

    //mouseover and mouseout event functions
    d3.select("#chart1").select("svg").selectAll("rect").on("mouseover", function(d) { d3.select(this).attr("opacity", 0.3)});
    d3.select("#chart1").select("svg").selectAll("rect").on("mouseout", function(d) { d3.select(this).attr("opacity", 1)});
   };

    //mouseover and mouseout event functions
  d3.select("#chart1").select("svg").selectAll("rect").on("mouseover", function(d) { d3.select(this).style("fill", "red")});
  d3.select("#chart1").select("svg").selectAll("rect").on("mouseout", function(d) { d3.select(this).style("fill", "green")});

    //function click button to change opacity in all rects
   function OpacityNow() {
      d3.select("#chart1").select("svg").selectAll("rect").style("opacity", 0.3);
   };
   //function click button to change color in all rects
   function ColorNow() {
       d3.select("#chart1").select("svg").selectAll("rect").style("fill", "green");
   };

这是预期的行为。与单击按钮运行的按钮代码不同,鼠标悬停代码将事件处理程序绑定到当时 SVG 中存在的 <rect> 元素.

这里有一些代码来演示它。

假设我们有这个模式:

//code for creating the bars
.on("mouseover", function(){ ... }//mouseover block, outside 'update'

update();

function update(){
    //code for updating the bars
}

在这种情况下,鼠标悬停将处理在更新函数运行之前创建的条。

这是一个向您展示的演示。您可以看到鼠标悬停最初适用于所有矩形,但是当创建新矩形时,它不再起作用:

var width = 400, height = 400;

var margin = {top:0, right:0, bottom:0, left:30};

var svg = d3.select("body")
 .append("svg")
 .attr("width", width)
 .attr("height", height);
 
var xScale = d3.scaleLinear()
 .range([margin.left, width - margin.right]);
 
var yScale = d3.scaleBand()
 .range([margin.top, height - margin.bottom])
 .paddingInner(0.2);
 
var yAxis = d3.axisLeft(yScale)
 .tickSizeOuter(0);
 
var letters = "ABCDEFGHIJ".split("");
 
var color = d3.scaleSequential(d3.interpolateViridis)
 .domain([0, 10]);
 
svg.append("g")
 .attr("class", "y axis")
 .attr("transform", "translate(" + margin.left + ",0)")
 .call(yAxis);
 
draw();
 
function draw(){

var data = getData();
 
xScale.domain([0, d3.max(data, function(d){ return d.value})]);
yScale.domain(data.map(function(d){ return d.title}));

var bars = svg.selectAll(".bars")
 .data(data, function(d){ return d.title});
 
bars.exit()
 .transition()
 .duration(1000)
 .attr("width", 0)
 .remove();
 
bars.enter()
 .append("rect")
 .attr("class", "bars")
 .attr("x", xScale(0) + 1)
 .attr("y", function(d){ return yScale(d.title)})
 .attr("width", 0)
 .attr("height", yScale.bandwidth())
 .attr("fill", function(d){ return color(letters.indexOf(d.title)+1)})
 .merge(bars).transition()
 .duration(1000)
 .delay(1000)
 .attr("y", function(d){ return yScale(d.title)})
 .attr("width", function(d){ return xScale(d.value)});

 d3.transition(svg).select(".y.axis")
   .transition()
   .duration(1000)
   .delay(750)
   .call(yAxis);
}

function getData(){
 var title = "ABCDEFGHIJ".split("");
 var data = [];
 for(var i = 0; i < 5; i++){
  var index = Math.floor(Math.random()*title.length);
  data.push({title: title[index],
  value: Math.floor(Math.random()*100)});
  title.splice(index,1);
 }
 data = data.sort(function(a,b){ return d3.ascending(a.title,b.title)});
 return data;
};

setInterval(draw, 3000);

d3.selectAll("rect").on("mouseover", function(){
    d3.select(this).attr("opacity", .5)
}).on("mouseout", function(){
    d3.select(this).attr("opacity", 1)
});
 
 
<script src="https://d3js.org/d3.v4.min.js"></script>

现在,让我们做另一个模式:

//code for creating the bars

update();

function update(){
    //code for updating the bars
    .on("mouseover", function(){ ... }//mouseover block, inside 'update'
}

在这种情况下,鼠标悬停仅在 函数 update 被调用后有效。这是另一个演示,函数将在 5 秒后调用:

var width = 400, height = 400;

var margin = {top:0, right:0, bottom:0, left:30};

var svg = d3.select("body")
 .append("svg")
 .attr("width", width)
 .attr("height", height);
 
var xScale = d3.scaleLinear()
 .range([margin.left, width - margin.right]);
 
var yScale = d3.scaleBand()
 .range([margin.top, height - margin.bottom])
 .paddingInner(0.2);
 
var yAxis = d3.axisLeft(yScale)
 .tickSizeOuter(0);
 
var letters = "ABCDEFGHIJ".split("");
 
var color = d3.scaleSequential(d3.interpolateViridis)
 .domain([0, 10]);
 
svg.append("g")
 .attr("class", "y axis")
 .attr("transform", "translate(" + margin.left + ",0)")
 .call(yAxis);
 
var data = getData();
 
xScale.domain([0, d3.max(data, function(d){ return d.value})]);
yScale.domain(data.map(function(d){ return d.title}));

var bars = svg.selectAll(".bars")
 .data(data, function(d){ return d.title});
 
bars.exit()
 .transition()
 .duration(1000)
 .attr("width", 0)
 .remove();
 
bars.enter()
 .append("rect")
 .attr("class", "bars")
 .attr("x", xScale(0) + 1)
 .attr("y", function(d){ return yScale(d.title)})
 .attr("width", 0)
 .attr("height", yScale.bandwidth())
 .attr("fill", function(d){ return color(letters.indexOf(d.title)+1)})
 .merge(bars).transition()
 .duration(1000)
 .delay(1000)
 .attr("y", function(d){ return yScale(d.title)})
 .attr("width", function(d){ return xScale(d.value)});
 
  d3.transition(svg).select(".y.axis")
   .transition()
   .duration(1000)
   .delay(750)
   .call(yAxis);
 
function draw(){

var data = getData();
 
xScale.domain([0, d3.max(data, function(d){ return d.value})]);
yScale.domain(data.map(function(d){ return d.title}));

var bars = svg.selectAll(".bars")
 .data(data, function(d){ return d.title});
 
bars.exit()
 .transition()
 .duration(1000)
 .attr("width", 0)
 .remove();
 
bars.enter()
 .append("rect")
 .attr("class", "bars")
 .attr("x", xScale(0) + 1)
 .attr("y", function(d){ return yScale(d.title)})
 .attr("width", 0)
 .attr("height", yScale.bandwidth())
 .attr("fill", function(d){ return color(letters.indexOf(d.title)+1)})
 .merge(bars).transition()
 .duration(1000)
 .delay(1000)
 .attr("y", function(d){ return yScale(d.title)})
 .attr("width", function(d){ return xScale(d.value)});
 
 d3.selectAll("rect").on("mouseover", function(){
    d3.select(this).attr("opacity", .5)
}).on("mouseout", function(){
    d3.select(this).attr("opacity", 1)
});

 d3.transition(svg).select(".y.axis")
   .transition()
   .duration(1000)
   .delay(750)
   .call(yAxis);
}

function getData(){
 var title = "ABCDEFGHIJ".split("");
 var data = [];
 for(var i = 0; i < 5; i++){
  var index = Math.floor(Math.random()*title.length);
  data.push({title: title[index],
  value: Math.floor(Math.random()*100)});
  title.splice(index,1);
 }
 data = data.sort(function(a,b){ return d3.ascending(a.title,b.title)});
 return data;
};

setInterval(draw, 5000);
<script src="https://d3js.org/d3.v4.min.js"></script>

只有在那之后,鼠标悬停才会起作用。

正如我之前所说,该按钮将适用于在 update 函数之前或之后创建的所有矩形,因为它会在按下按钮时选择 SVG 中显示的矩形.

如果你不想重复代码(以防你的mouseovermouseout函数变大),你可以单独设置函数:

//code for creating the bars
.on("mouseover", mouseover)//calls 'mouseover'
.on("mouseout", mouseout)//calls 'mouseout'

function update(){
    //code for updating the bars
    .on("mouseover", mouseover)//calls 'mouseover'
    .on("mouseout", mouseout)//calls 'mouseout'
}

function mouseover(){ ... }
function mouseout(){ ... }

这是演示:

var width = 400, height = 400;

var margin = {top:0, right:0, bottom:0, left:30};

var svg = d3.select("body")
 .append("svg")
 .attr("width", width)
 .attr("height", height);
 
var xScale = d3.scaleLinear()
 .range([margin.left, width - margin.right]);
 
var yScale = d3.scaleBand()
 .range([margin.top, height - margin.bottom])
 .paddingInner(0.2);
 
var yAxis = d3.axisLeft(yScale)
 .tickSizeOuter(0);
 
var letters = "ABCDEFGHIJ".split("");
 
var color = d3.scaleSequential(d3.interpolateViridis)
 .domain([0, 10]);
 
svg.append("g")
 .attr("class", "y axis")
 .attr("transform", "translate(" + margin.left + ",0)")
 .call(yAxis);
 
draw();
 
function draw(){

var data = getData();
 
xScale.domain([0, d3.max(data, function(d){ return d.value})]);
yScale.domain(data.map(function(d){ return d.title}));

var bars = svg.selectAll(".bars")
 .data(data, function(d){ return d.title});
 
bars.exit()
 .transition()
 .duration(1000)
 .attr("width", 0)
 .remove();
 
bars.enter()
 .append("rect")
 .attr("class", "bars")
 .attr("x", xScale(0) + 1)
 .attr("y", function(d){ return yScale(d.title)})
 .attr("width", 0)
 .attr("height", yScale.bandwidth())
 .attr("fill", function(d){ return color(letters.indexOf(d.title)+1)})
 .merge(bars).transition()
 .duration(1000)
 .delay(1000)
 .attr("y", function(d){ return yScale(d.title)})
 .attr("width", function(d){ return xScale(d.value)});
  
  d3.selectAll("rect").on("mouseover", mouseover).on("mouseout", mouseout);

 d3.transition(svg).select(".y.axis")
   .transition()
   .duration(1000)
   .delay(750)
   .call(yAxis);
}

function getData(){
 var title = "ABCDEFGHIJ".split("");
 var data = [];
 for(var i = 0; i < 5; i++){
  var index = Math.floor(Math.random()*title.length);
  data.push({title: title[index],
  value: Math.floor(Math.random()*100)});
  title.splice(index,1);
 }
 data = data.sort(function(a,b){ return d3.ascending(a.title,b.title)});
 return data;
};

setInterval(draw, 3000);

d3.selectAll("rect").on("mouseover", mouseover).on("mouseout", mouseout);

function mouseover(){
  d3.select(this).attr("opacity", .5);
}

function mouseout(){
  d3.select(this).attr("opacity", 1);
}
<script src="https://d3js.org/d3.v4.min.js"></script>