28

I have a graph for which I need a reference line everywhere the mouse-cursor is inside this graph. And this reference line will follow the mouse movements inside the graph.

But this doesn't seems to work fine. It works only on the axis and the ticks (.axis lines) of the axis. On debugging, I found that mouse event works fine when applied over SVG but not on the group, why so ?

Here is my code :

test.html

<html>
<head>
<script src="jquery.js">
</script>
<script src="d3.v2.js">
</script>
<script src="retest.js">
</script>
<style type="text/css">
  .g_main {
    cursor:pointer;
  }

  .axis path, .axis line {
    stroke: #DBDBDB;
    /*shape-rendering: crispEdges;
    */
  }

  .y g:first-child text {
    display:none;
  }

  .y g:first-child line {
    stroke: #989898  ;
    stroke-width: 2.5px;
  }

  /*.x g:first-child line {
  stroke: black  ;
  stroke-width: 2.5px;
}
  */

  .y path {
    stroke: #989898  ;
    stroke-width: 2.5px;
  }

</style>
</head>
<body>  
<center>
  <button id="reload" onclick="loadViz();">
    load Graph
  </button>
  <div id="viz" class="viz">
  </div>    
</center>
<script>
  loadViz();
</script>
</body>
</html>

retest.js

var series,
classifications,
minVal,
maxVal,

svgW = 600,
svgH = 600,
//w = 1200,
//h = 1200,

vizPadding = {
    top: 120,
    right: 30,
    bottom: 120,
    left: 50
},

yAxMin_PA = 0,
yAxMax_PA = 50,
xAxMin_PA = 2002,
xAxMax_PA = 2008,
areaStrokeColors = ['#FF6600', '#3366FF', '#B8860B', '#458B00', 'white'];

var loadViz = function () {

    color = d3.scale.category10();

    data = {
        "lines": [{
                "line": [{
                        "X": 2002,
                        "Y": 42
                    }, {
                        "X": 2003,
                        "Y": 45
                    },

                    {
                        "X": 2005,
                        "Y": 47
                    },

                    {
                        "X": 2007,
                        "Y": 41
                    }
                ]
            }, {
                "line": [{
                        "X": 2003,
                        "Y": 33
                    }, {
                        "X": 2005,
                        "Y": 38
                    }, {
                        "Y": 36,
                        "X": 2008
                    }
                ]
            }, {

                "line": [{
                        "X": 2004,
                        "Y": 13
                    }, {
                        "X": 2005,
                        "Y": 19
                    }, {
                        "X": 2008,
                        "Y": 21
                    }
                ]
            }, {

                "line": [{
                        "X": 2003,
                        "Y": 20
                    }, {
                        "X": 2005,
                        "Y": 27
                    }, {
                        "X": 2008,
                        "Y": 29
                    }
                ]
            }
        ]
    };


    $("#viz").html("");
    buildBase();
    //setScales();
};

var buildBase = function () {

    margin = {
        top: 80,
        right: 120,
        bottom: 40,
        left: 40
    },
    width = 960 - margin.left - margin.right,
    height = 550 - margin.top - margin.bottom;

    t2 = height + margin.top + margin.bottom;

    x = d3.scale.linear()
        .domain([xAxMin_PA, xAxMax_PA])
        .range([0, width]);

    y = d3.scale.linear()
        .domain([yAxMin_PA, yAxMax_PA])
        .range([height, 0]);

    tickSizeToApplyX = 5;

    tickSizeToApplyY = 10;

    // Function to draw X-axis
    xAxis = d3.svg.axis()
        .scale(x)
        .ticks(tickSizeToApplyX)
        .tickSize(-height, 0, 0)
    //.tickSize(10)
    .orient("bottom")
        .tickPadding(5);

    // Function to draw Y-axis
    yAxis = d3.svg.axis()
        .scale(y)
        .ticks(tickSizeToApplyY)
        .tickSize(-width, 0, 0)
    //.tickSize(0)
    .orient("left")
        .tickPadding(5);

    // Define the line
    var valueline = d3.svg.line()
        .x(function (d) { /*console.log(d.X);*/
            return x(d.X);
        })
        .y(function (d) { /*console.log(d.Y);*/
            return y(d.Y);
        });

    // Define the line
    var referline = d3.svg.line()
        .x(function (dx) { /*console.log(d.X);*/
            return dx;
        })
        .y(function (dy) { /*console.log(d.Y);*/
            return dy;
        });

    // Append SVG into the html
    var viz = d3.select("#viz")
        .append("svg")
        .attr("width", width + margin.left + margin.right + 10)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("class", "g_main")
        .attr("transform", "translate(" + margin.left + "," + ((margin.top) - 30) + ")");

    viz.on("mousemove", function () {
        cx = d3.mouse(this)[0];
        cy = d3.mouse(this)[1];
        console.log("xx=>" + cx + "yy=>" + cy);
        redrawline(cx, cy);
    })
        .on("mouseover", function () {
            d3.selectAll('.line_over').style("display", "block");
        })
        .on("mouseout", function () {
            d3.selectAll('.line_over').style("display", "none");
        });

    //console.log(this);
    viz.append("line")
    //d3.select("svg").append("line")
    .attr("class", 'line_over')
        .attr("x1", 0)
        .attr("y1", 0)
        .attr("x2", x(xAxMax_PA))
        .attr("y2", 0)
        .style("stroke", "gray")
        .attr("stroke-dasharray", ("5,5"))
        .style("stroke-width", "1.5")
        .style("display", "none");

    // Draw X-axis
    viz.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    // Draw Y-axis
    viz.append("g")
        .attr("class", function (d, i) {
            return "y axis"
        })
        .call(yAxis);


    function redrawline(cx, cy) {
        d3.selectAll('.line_over')
            .attr("x1", 0)
            .attr("y1", cy)
            .attr("x2", x(xAxMax_PA))
            .attr("y2", cy)
            .style("display", "block");
    }
};
musically_ut
  • 34,028
  • 8
  • 94
  • 106
sudh_
  • 520
  • 1
  • 8
  • 14

1 Answers1

65

The g element is just an empty container which cannot capture click events (see documentation for pointer-events property for details).

However, mouse events do bubble up to it. Hence, the effect you desire can be achieved by first making sure that the g receives all pointer events:

.g_main {
  // ..
  pointer-events: all;
}

And then appending an invisible rectangle to it as a place to hover over:

viz.on("mousemove", function () {
        cx = d3.mouse(this)[0];
        cy = d3.mouse(this)[1];
        redrawline(cx, cy);
    })
    .on("mouseover", function () {
        d3.selectAll('.line_over').style("display", "block");
    })
    .on("mouseout", function () {
        d3.selectAll('.line_over').style("display", "none");
    })
  .append('rect')
  .attr('class', 'click-capture')
  .style('visibility', 'hidden')
  .attr('x', 0)
  .attr('y', 0)
  .attr('width', width)
  .attr('height', height);

Working example: http://jsfiddle.net/H3W3k/

As for why they work when applied to the svg element (from the docs):

Note that the ‘svg’ element is not a graphics element, and in a Conforming SVG Stand-Alone File a rootmost ‘svg’ element will never be the target of pointer events, though events can bubble to this element. If a pointer event does not result in a positive hit-test on a graphics element, then it should evoke any user-agent-specific window behavior, such as a presenting a context menu or controls to allow zooming and panning of an SVG document fragment.

musically_ut
  • 34,028
  • 8
  • 94
  • 106
  • 1
    This can also be achieved with `viz.style('pointer-events', 'all')` – user31415629 Sep 11 '17 at 08:36
  • This answer is good but is about D3 v3... **Some news in 2019**? no simple way to set "catch all" node for events? I am using a background-rectangle, and have same problem. – Peter Krauss Oct 13 '19 at 12:57
  • Can I use the same rectangle for .g_main and .click-capture classes? – Peter Krauss Oct 13 '19 at 13:02
  • 9
    `The g element is just an empty container which cannot capture click events` - incorrect. You can set `pointer-events: bounding-box` on a `g` element, and capture click events on it regardless of its children. This answer has actually cost me 2 good hours trying to figure out how to have a `g` element as a target when the click listener is on one of its ancestors. – Izhaki Dec 02 '19 at 23:46
  • 1
    Hmm if I do this the mouseover works but I can't click the other elements in the svg anymore – User Apr 13 '21 at 20:00