2

I have a grouped bar chart similar to https://bl.ocks.org/mbostock/3887051
I used a mouseover function to fade the bars the mouse is currently not over

function mouseover(bar)
{
   d3.selectAll(".bar")
     .filter(function(d){ return (d != bar);})
     .transition(t)
        .style("opacity", 0.5);
}

While this works nicely to highlight a single bar, I now need to highlight the entire group / fade everything but this group.
So far I haven't been able to figure out though how to get from the datum element d passed via .on("mouseover", function(d) ... back to the entire group this element belongs to.
Is there a simple way to achieve this in D3v4?

TommyF
  • 6,660
  • 8
  • 37
  • 61

2 Answers2

2

In D3 4.0 the callback function for the .on() method is passed 3 arguments: the current datum (d), the current index (i), and the current group (nodes).

Within the mouseover callback, you can selectAll("rect"), and filter out items which are in the current group (node). With this selection, you then set opacity to 0.5. On mouseout, you just need to set all opacity back to 1.0. The pertinent code is:

 ...
   .on('mouseover', function(d, i, node) {
    d3.selectAll("rect")
      .filter(function (x) { return !isInArray(this, node)})
      .attr('opacity', 0.5);
   }
  )
  .on('mouseout', function() {
    d3.selectAll("rect").attr('opacity', 1.0);
    });

with a small helper function to check if a value is present in an array (array of DOM elements in our case):

 function isInArray(value, array) {
     return array.indexOf(value) > -1;
 }

The full code in context (given your linked example):

g.append("g")
 .selectAll("g")
 .data(data)
 .enter().append("g")
   .attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; })
 .selectAll("rect")
 .data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
 .enter().append("rect")
   .attr("x", function(d) { return x1(d.key); })
   .attr("y", function(d) { return y(d.value); })
   .attr("width", x1.bandwidth())
   .attr("height", function(d) { return height - y(d.value); })
   .attr("fill", function(d) { return z(d.key); })
   .on('mouseover', function(d, i, node) {
    d3.selectAll("rect")
      .filter(function (x) { return !isInArray(this, node)})
      .attr('opacity', 0.5);
   }
  )
  .on('mouseout', function() {
    d3.selectAll("rect").attr('opacity', 1.0);
    });
Andrew Guy
  • 9,310
  • 3
  • 28
  • 40
1

One solution could be:

Make a function which selects all group and gives it a transition of opacity 0.

The DOM on which mouse is over give opacity 1.

  function hoverIn(){
    d3.selectAll(".group-me").transition()
        .style("opacity", 0.01);//all groups given opacity 0
    d3.select(this).transition()
        .style("opacity", 1);//give opacity 1 to group on which it hovers.
  }  

Make a function which selects all group and gives it a transition of opacity 1, when the mouse is out.

  function hoverOut(){
    d3.selectAll(".group-me").transition()
        .style("opacity", 1);
  }  

On the group add a class and add the mouse out and in function like

  g.append("g")
    .selectAll("g")
    .data(data)
    .enter().append("g")
    .classed("group-me", true)//add a class for selection.
    .on("mouseover", hoverIn)
    .on("mouseout", hoverOut)

working code here

Cyril Cherian
  • 32,177
  • 7
  • 46
  • 55