How do you create a mouse event bubble of an SVG element through another element?

I have a D3 line graph where I put a rectangle “behind” the graph. A mouse event is attached to this rectangle, but the problem is that my diagram has another rectangle superimposed “above” the diagram, to which events are also attached.

How do I make the events of the bottom rectangular mouse pop up over the top rectangle that overlays the top? Thanks!

I created a fiddle here:

http://jsfiddle.net/TnjCC/1/

And here is my code. Look at the comment “This is where I need to hover over” to see which element I would like to pop up.

var data = [ {"date":"1-May-13","close":58.13}, {"date":"30-Apr-13","close":53.98}, {"date":"27-Apr-13","close":67.00}, {"date":"26-Apr-13","close":89.70}, {"date":"25-Apr-13","close":99.00}, {"date":"24-Apr-13","close":130.28}, {"date":"23-Apr-13","close":166.70}, {"date":"20-Apr-13","close":234.98}, {"date":"19-Apr-13","close":345.44}, {"date":"18-Apr-13","close":443.34}, ]; var margin = {top: 20, right: 50, bottom: 30, left: 50}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var parseDate = d3.time.format("%d-%b-%y").parse, bisectDate = d3.bisector(function(d) { return d.date; }).left, formatValue = d3.format(",.2f"), formatCurrency = function(d) { return "$" + formatValue(d); }; var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var line = d3.svg.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); }); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); data.forEach(function(d) { d.date = parseDate(d.date); d.close = +d.close; data.sort(function(a, b) { return a.date - b.date; }); x.domain([data[0].date, data[data.length - 1].date]); y.domain(d3.extent(data, function(d) { return d.close; })); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("Price ($)"); <!-- This is where I need the mouseover to bubble up --> var left = x(new Date("Apr 23 2013")); var right = x(new Date("Apr 26 2013")); var wid = right - left; svg.append("rect") .attr("id", "range") .attr("class", "range") .attr("x", left) .attr("width", wid) .attr("height", height) .on("mouseover", function () { alert("I can see you!"); }) svg.append("path") .datum(data) .attr("class", "line") .attr("d", line); var focus = svg.append("g") .attr("class", "focus") .style("display", "none"); focus.append("circle") .attr("r", 4.5); focus.append("text") .attr("x", 9) .attr("dy", ".35em"); svg.append("rect") .attr("class", "overlay") .attr("width", width) .attr("height", height) .on("mouseover", function() { focus.style("display", null); }) .on("mouseout", function() { focus.style("display", "none"); }) .on("mousemove", mousemove); function mousemove() { var x0 = x.invert(d3.mouse(this)[0]), i = bisectDate(data, x0, 1), d0 = data[i - 1], d1 = data[i], d = x0 - d0.date > d1.date - x0 ? d1 : d0; focus.attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")"); focus.select("text").text(formatCurrency(d.close)); } }); 
+7
source share
3 answers

For a quick fix, you can move the range over the overlay and manually call overlay event handlers from that range.

http://jsfiddle.net/Rk5Hp/

  svg.append("rect") .attr("class", "overlay") .attr("width", width) .attr("height", height) .on("mouseover", function() { focus.style("display", null); }) .on("mouseout", function() { focus.style("display", "none"); }) .on("mousemove", mousemove); // move range above overlay and call the overlay event handlers from there svg.append("rect") .attr("id", "range") .attr("class", "range") .attr("x", left) .attr("width", wid) .attr("height", height) .on("mousemove", mousemove) .on("mouseout", function() { focus.style("display", "none"); }) .on("mouseover", function() { focus.style("display", null); // event handling for range mouseover (alert broke mouse move) console.log("I can see you!"); }); 

Bubbles operate at the dom level, and since there is no way for a rectangle to be a descendant of another rectangle, the bubbles will not take care of this for you. Grouping the elements together and placing a handler that checks the purpose of the event in the group will not allow you to register the event handler twice, but it suffers from the same main problem: when the elements overlap, any element that is declared last in the original order will receive the event.

+6
source

You can also use the following style to “hide” certain svg elements for mouse events. In my case, this is a mouseover event that I wanted to skip:

 pointer-events: none; 
+7
source

All of the above answers are correct, but I would like to give another example:

  let log = console.log let data = [] let pointStart = document.querySelector("svg").createSVGPoint() let pointStop = document.querySelector("svg").createSVGPoint() let divLog = d3.select("#log") var svg = d3.select("svg") var linearfn = d3.line() .x(d => dx) .y(d => dy) .curve(d3.curveLinear) function logTagName(eventName, tagName) { divLog.html(divLog.html() + eventName + " : " + tagName + "<br/>") } svg.on("click", function() { log("tagName: ", event.target.tagName) logTagName("svg click", event.target.tagName) pointStart.x = event.x - 8 pointStart.y = event.y - 8 data.push({ x: pointStart.x, y: pointStart.y }) svg.selectAll("path") // SVG içinde tanımlı path elemanlarını bul .data([1]).enter() // 1 elemanlı dizinin eleman sayısı kadarı için enter() .append('path') // dizi elemanı kadar path oluştur .attr("fill", "none") .attr("stroke", "black") .attr("stroke-width", 8) .attr("d", linearfn(data)) .on("click", function() { log("tagName: ", event.target.tagName) logTagName("path click", event.target.tagName) /* click event will start from path and pass to the svg element */ // event.stopPropagation(); // letting pass event bubbling }) svg.selectAll("circle") .data(data) .enter() .append("circle") .attr("cx", d => dx + .5) .attr("cy", d => dy + .5) .attr("r", 20) .attr("stroke-width", 3) .attr("stroke", "red") .attr("cursor", "move") .style("fill", "transparent") .attr("pointer-events", "all") // when clicked in/outside of circle, it'll handle events .on("mouseover", function() { log("over oldu") d3.select(this).style("stroke", "blue"); }) .on("mouseout", function() { log("out oldu") d3.select(this).style("stroke", "red"); }) .on("click", function() { event.stopPropagation(); // not letting pass event bubbling event.preventDefault(); log("click oldu") d3.select(this).style("stroke", "black"); }) }) .on("mousemove", function() { // fare hareketinde de çizdireceğiz ama x,y noktasını // tıklanıncaya kadar diziye eklemeyeceğiz pointStop.x = event.x - 8 pointStop.y = event.y - 8 svg.select("path") .attr("d", linearfn(data.concat({ x: pointStop.x, y: pointStop.y }))) }) 

https://jsfiddle.net/jsfiddleCem/hnsu68jw/

0
source

Source: https://habr.com/ru/post/1498029/


All Articles