Highlight all content inside g tag, whenever mouse above said tag. D3

I'm creating a 'hand-drawn' style chart in d3: basic bar plot

Each bar below is a path inside of a g of class bar. When I hover over each g.bar, I highlight all paths inside with a basic mouseover function:

  d3.selectAll('g.bar')
        .on('mouseover', function() {
          d3.select(this).selectAll('path').style('stroke', 'red')
        })

highlighted bar in image

The problem is, the highlighting only occurs when I hover over the paths, not the entire g.bar.

This makes the highlighting look super glitchy (running a mouse across it repeatedly highlights/unhighlights the path).

My question is: Is there a way to have all associated paths highlight whenever I'm hovering over the entire g.bar outlining the bar itself, and not just when I highlight over the path elements themselves?

A live-demo of my code is here: https://blockbuilder.org/jwilber/4bd8f5dd73666cdc5a30d7d6481e231a

Thanks for any help!

1 answer

  • answered 2019-08-24 11:58 lemming

    You could just add the following css:

    g.bar {
      pointer-events: bounding-box;
    }
    

    or directly set the g.bar elements' pointer-events attribute, which in your code would look like:

    bar.setAttribute('pointer-events', 'bounding-box');
    

    this sets up the g.bar elements to listen to events anywhere within the actual space that they take up (the bounding box).

    However, this only works in Chrome.

    Another alternative that seems to work in all the browsers I've tried is to add a transparent rect element to each g.bar element (just as a sibling of the path).

    data.forEach(function (d) {
      let node = rc.rectangle(0, y(d.trick), x(d.count), y.bandwidth());
      bar = roughSvg.appendChild(node);
      bar.setAttribute('class', 'bar');
    });
    
    d3.selectAll('g.bar')
      .data(data)
      .append('rect')
        .attr('x', 0)
        .attr('y', d => y(d.trick))
        .attr('width', d => x(d.count))
        .attr('height', y.bandwidth())
        .attr('fill', 'transparent');
    

    I'm guessing that this works because rather than the g.bar mouseout event happening whenever it falls between the strokes of the path, there is a solid rect element filling the space, even if it is transparent.