Home > Software design >  Apply opacity to elements not hovered on with d3 mouseover
Apply opacity to elements not hovered on with d3 mouseover

Time:05-12

I'm trying to apply a mouseover function that adds opacity to the elements that are not hovered on.

I have an SVG map with regions and my current function applies 0.8 opacity to the selected region.

I'm trying to invert that so the other regions are the ones that get the applied 0.8 opacity

Here is my code:

function render(svg) {

  d3.select("body").node().append(svg.documentElement)

  data.forEach(d => {

    d3.selectAll(`g[aria-label="${d.id}"]`)
    .datum(data)
      .selectAll('path')
      .style("fill", colorScale(d.value))

      .on('mouseover', function() {
      d3.selectAll(`g[aria-label="${d.id}"]`)
                .filter(function(e) {
            return e.id !== d.id
          })
          .style('opacity', 0.8)
      })
      
      
       .on('mouseout', function() {
       d3.selectAll(`g[aria-label="${d.id}"]`)
         
          .style('opacity', 1)
      })

  })

I thought by adding e.id !==d.id would do this but is has the opposite effect

My fiddle: jsfiddle

CodePudding user response:

e.id is undefined because e is bound to data. Consider the use of this selector:

g[aria-label]:not([aria-label="${d.id}"])

Which says select all g elements with an aria-label but not those with an aria-label equalling the d.id

Working example - I've used mouseenter to minimise the events firing and also 0.5 opacity for effect:

var data = [{
    "id": "Scotland ",
    "value": 5000
  },
  {
    "id": "Wales ",
    "value": 3000
  },
  {
    "id": "Ireland ",
    "value": 750
  },
  {
    "id": "North West ",
    "value": 1250
  },
  {
    "id": "North East ",
    "value": 4500
  },
  {
    "id": "East Midlands ",
    "value": 2000
  },
  {
    "id": "South East ",
    "value": 350
  },
  {
    "id": "South West ",
    "value": 6000
  },
  {
    "id": "East of England ",
    "value": 4000
  },
  {
    "id": "West Midlands ",
    "value": 2500
  },
  {
    "id": "Northern Ireland ",
    "value": 1000
  },
  {
    "id": "Isle of Man ",
    "value": 3000
  },
  {
    "id": "Yorkshire and the Humber ",
    "value": 1500
  },
  {
    "id": "Greater London ",
    "value": 5000
  }

];


var colorScale = d3
  .scaleLinear()
  .domain(d3.extent(data, (d) => d.value))
  .range(["#5C4D87", "#EC4E6B"])
  .interpolate(d3.interpolateHcl)


const svgUrl = "https://raw.githubusercontent.com/gangrel11/samplefiles/main/amCharts.pixelMap.svg";

d3.xml(svgUrl).then(render);


function render(svg) {

  d3.select("body").node().append(svg.documentElement);
  
  data.forEach(d => {
    d3.selectAll(`g[aria-label="${d.id}"]`)
      .datum(data)
      .selectAll('path')
      .style("fill", colorScale(d.value))  
      .on('mouseenter', function() {
        d3.selectAll(`g[aria-label]:not([aria-label="${d.id}"])`)
          .style('opacity', 0.5)
      })
      .on('mouseout', function() {
        d3.selectAll(`g[aria-label]:not([aria-label="${d.id}"])`)
          .style('opacity', 1)
      })
  });
}
.amcharts-bg {
  fill: #EEF1FA
}

.amcharts-map-image {
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Because the hex shapes are slightly separated we see a lot of flicker. This can be mititgated by a update to the example above where the downside is that once a hex is hovered then the map won't go back to the default position of all hexes have opacity of 1:

var data = [{
    "id": "Scotland ",
    "value": 5000
  },
  {
    "id": "Wales ",
    "value": 3000
  },
  {
    "id": "Ireland ",
    "value": 750
  },
  {
    "id": "North West ",
    "value": 1250
  },
  {
    "id": "North East ",
    "value": 4500
  },
  {
    "id": "East Midlands ",
    "value": 2000
  },
  {
    "id": "South East ",
    "value": 350
  },
  {
    "id": "South West ",
    "value": 6000
  },
  {
    "id": "East of England ",
    "value": 4000
  },
  {
    "id": "West Midlands ",
    "value": 2500
  },
  {
    "id": "Northern Ireland ",
    "value": 1000
  },
  {
    "id": "Isle of Man ",
    "value": 3000
  },
  {
    "id": "Yorkshire and the Humber ",
    "value": 1500
  },
  {
    "id": "Greater London ",
    "value": 5000
  }

];


var colorScale = d3
  .scaleLinear()
  .domain(d3.extent(data, (d) => d.value))
  .range(["#5C4D87", "#EC4E6B"])
  .interpolate(d3.interpolateHcl)


const svgUrl = "https://raw.githubusercontent.com/gangrel11/samplefiles/main/amCharts.pixelMap.svg";

d3.xml(svgUrl).then(render);


function render(svg) {

  d3.select("body").node().append(svg.documentElement);
  
  data.forEach(d => {
    d3.selectAll(`g[aria-label="${d.id}"]`)
      .datum(data)
      .selectAll('path')
      .style("fill", colorScale(d.value))  
      .on('mouseenter', function() {
        d3.selectAll(`g[aria-label]:not([aria-label="${d.id}"])`)
          .style('opacity', 0.5)
        d3.selectAll(`g[aria-label="${d.id}"]`)
          .style('opacity', 1)
      })
      .on('mouseout', function() {
      })
  });
    
}
.amcharts-bg {
  fill: #EEF1FA
}

.amcharts-map-image {
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

  • Related