D3js: hovering over one element changes the opacity of several other elements

Thanks to the previous answers, I made a map and associated graph with D3js .

The row and map are in specific divs and I am not using the same data source. This is part of my problem.

For the map, I used queue.js to download multiple files at a time. One of these files is .csv, which follow exactly the same order as the geojson where the polygons are stored. If I sort the .csv data differently, then the match with my polygons .geojson is bad and my choropleth map goes false.

Here's the relevant code for interactive map polygons:

  svg.append("g").attr("class","zones")
    .selectAll("path")
    .data(bureaux.features)   //"bureaux" is a reference to the geojson
    .enter()
      .append("path")
      .attr("class", "bureau")
      .attr("d", path)
      .attr("fill", function(d,i){
          if (progression[i].diff_ries<-16.1){  //"progression" is the reference to my .csv
        return colors[0] // colors is a previous array with the choropleth colors
      }
      else if (progression[i].diff_ries<-12.6){
        return colors[1]
      }
       else if (progression[i].diff_ries<-9){
        return colors[2]
      }
      else {return colors[3]
      }
    })
    .on('mouseover', tip.show) // tip.show and tip.hide are specific functions of d3.js.tip
    .on('mouseout', tip.hide)

};

      

No problem here, the code works fine . We arrived on schedule. It used a .json array named at the beginning of the script like this

var array=[{"id_bureau":905,"diff_keller":4.05,"diff_ries":-15.02},{etc}];

      

"id_bureau" is the common "index of my .geojson, my .csv and this .json array. Then I sort the array with a specific function. Here's some of the graph related code:

svg2.selectAll(".bar")
.data(array)   
.enter().append("rect")
// I colour on part of the bars like the map
.attr("fill", function(d,i){
          if (array[i].diff_ries<-16.1){
        return colors[0]
      }
      else if (array[i].diff_ries<-12.6){
        return colors[1]
      }
       else if (array[i].diff_ries<-9){
        return colors[2]
      }
      else {return colors[3]
      }
    })
.attr("x", function (d) {
return x(Math.min(0, d.diff_ries));
})
.attr("y", function (d) {
return y(d.id_bureau);
})
.attr("width", function (d) {
return Math.abs(x(d.diff_ries) - x(0));
})
.attr("height", y.rangeBand());

// this part is for the other bars
svg2.selectAll(".bar")
.data(tableau)
.enter().append("rect")
// the others bars are always blue, so I used a simple class
.attr("class", "bar_k")
.attr("x", function (d) {
return x(Math.min(0, d.diff_keller));
})
.attr("y", function (d) {
return y(d.id_bureau);
})
.attr("width", function (d) {
return Math.abs(x(d.diff_keller) - x(0));
})
.attr("height", y.rangeBand());

svg2.append("g")
.attr("class", "x axis")
.call(xAxis);

svg2.append("g")
.attr("class", "y axis")
.append("line")
.attr("x1", x(0))
.attr("x2", x(0))
.attr("y2", height2);

      

So now I will not do when the mouse is over one polygon in order to keep the correspondent panel of the plot more visible than the others with the opacity attribute (and when the mouse is pulled out, the opacity of the whole plot goes back to 1).

It might seem obvious, but I don't understand how to properly link the map and graph with "id_bureau" because they do not follow the same order as in this question: Change the class of one element when hovering over another d3 element .

Does anyone know if I can easily convert the mouseover and mouseout events on the map part to change the graph at the same time?

Thanks in advance.

+3


source to share


1 answer


To highlight a function on the map

To focus on a single feature, you just need a few lines of CSS:

/* Turn off every features */
#carte:hover .bureau {
  opacity:0.5;
}

/* Turn on the one you are specifically hovering */
#carte:hover .bureau:hover {
  opacity:1;
}

      

To highlight the panel in the second graph

First of all, you need to distinguish between two types of bar with two classes:

// First set of bars: .bar_k
svg2.selectAll(".bar_j")
    .data(tableau)
    .enter().append("rect")
    // Important: I use a common class "bar" for both sets
    .attr("class", "bar bar_j")
    // etc...

// Second set of bars: .bar_k
svg2.selectAll(".bar_k")
    .data(tableau)
    .enter().append("rect")
    .attr("class", "bar bar_k")
    // etc...

      

Then you need to change the mouseenter / mouseleave functions accordingly:



svg.append("g").attr("class","zones")
      .selectAll("path")
      .data(bureaux.features)
      .enter()
        // creating paths
        // ...
        // ...
        .on('mouseover', function(d, i) {
          // You have to get the active id to highligth the right bar
          var id = progression[i].id_bureau
          // Then you select every bars (with the common class)
          // to update opacities.
          svg2.selectAll(".bar").style("opacity", function(d) {
            return d.id_bureau == id ? 1 : 0.5;
          });
          tip.show(d,i);
        })
        .on('mouseout',  function(d, i) {
          // To restore the initial states, select every bars and 
          // set the opcitiy to 1
          svg2.selectAll(".bar").style("opacity", 1);
          tip.hide(d,i);
        });

      

Here's a demo .

Performance issue

This implementation is slow. You can improve it by toggling the "active" class on the panels you want to highlight.

Another good tail might be to collect two types of bar in one group, which you describe singularly with an id (e.g. bureau187). This way you can directly select the panel you want into the mouseenter function and include it using the "active" class.

With this class, you could mimic the strategy I implemented to highlight the function and then remove svg2.selectAll(".bar").style("opacity", 1);

from the mouseleave function:

/* Turn off every bars */
#carte:hover .bar {
  opacity:0.5;
}

/* Turn on the one you want to highligth */
#carte:hover .bar.active {
  opacity:1;
}

      

+5


source







All Articles