简体   繁体   中英

Can svg element trigger click event of svg elements under itself?

Noob question here.

I was wondering if it was possible to trigger the 'click' event of elements (path, circle, ...) under it if they cover the mouse coordinates? Preferably in typescript and by using the d3.js library.

As an example, consider having two circles (one small above one bigger in z-position) and that both show a message when clicked on. I would like that if I click inside the small circle, its message appears and then the message of the larger circle also appears.

Here is HTML code of my example [EDITED: changed version of D3] :

<!DOCTYPE html>
<style>
  circle { fill: lightgreen; stroke: #000; }
</style>
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>

Here is the associated javascript:

var svg = d3.select("body").append("svg")
    .style("float", "left")
    .attr("width", 480)
    .attr("height", 480)
    .attr('pointer-events', 'all')
    .on("click", log("SVG"));

svg.append("circle")
    .attr('pointer-events', 'all')
    .attr("cx", 240)
    .attr("cy", 240)
    .attr("r", 200)
    .on("click", log("OUTER"));

svg.append("circle")
    .attr('pointer-event', 'all')
    .attr("cx", 240)
    .attr("cy", 240)
    .attr("r", 100)
    .on("click", log("INNER"));

var div = d3.select("body").append("div")
    .style("float", "left");


function log(message) {
  return function() {
    div.append("p")
        .text(message)
        .style("background", "#ff0")
      .transition()
        .duration(2500)
        .style("opacity", 1e-6)
        .remove();
  };
}

With this code clicking on the big circle shows OUTER and SVG messages. And clicking on the small circle shows INNER and SVG messages, but I would like to have the sequence INNER , OUTER and SVG .

[EDIT: corrected misuse of word 'propagation' ]

I put the 'pointer-events' attribute of the elements to 'all'. I know that makes them "non pass-through", but I would like to be able to trigger their events AND ALSO trigger the events of elements under them.

Alternatively, is there was any nice mean to get a list of svg elements that cover a given coordinate [X,Y] (that can be obtained by d3.mouse ), such that their respective click event can be fired manually?

Thanks for your help.

The solution will depend on the shape type and parameters that govern the size of the svg elements. If you want a non-optimal but universal solution, you can use the bounding box of the elements. Using the bounding box, you can determine if any object is over [x,y] by testing the x and y ranges against the [x,y] target.

You can optimize and insert the following code into any d3 function block:

 <d3 function block>((shape)=>{ let bbox = this.getBBox(); let minX=bbox.x; let maxX=minX+bbox.width; let minY=bbox.y; let maxY=minY+bbox.width; let isOver=shape.x>minX && shape.x < maxX && shape.y > minY && shape.y < maxY; //..do something depending on the value of isOver })

First of all, this solution uses D3 v5 , not D3 v2 (which is 9 years old) as you referenced in your HTML.

Back to your question, you said:

I would like to be able to trigger their events AND propagate to the element under them until the SVG itself is triggered.

Well, this is not what propagation means. Bubbling up means going up in the DOM tree, but these circles are not parent/child, they are just siblings. Therefore, what you want has nothing to do with propagation, so we need a different approach.

My suggested approach here uses elementFromPoint , to get all elements under the coordinate you clicked, combined with d3.dispatch for dispatching the clicks (I found the getAllElements function here , and modified it for using with D3). Finally, I need the clicked flag because elements with pointer events set to none are ignored by elementFromPoint (by the way, it is pointer-events , not pointer-event ).

Here is the demo:

 let clicked; var svg = d3.select("body").append("svg").style("float", "left").attr("width", 480).attr("height", 480).attr('pointer-event', 'all'); svg.append("circle").attr('pointer-event', 'all').attr("cx", 240).attr("cy", 240).attr("r", 200).on("click", function() { if (;clicked) return; log("OUTER") }). svg.append("circle"),attr('pointer-event'. 'all'),attr("cx". 240),attr("cy". 240),attr("r". 100),on("click"; function() { if (;clicked) return. log("INNER") }). var div = d3.select("body"),append("div");style("float". "left"). function log(message) { div.append("p"),text(message).style("background". "#ff0").transition(),duration(2500).style("opacity"; 1e-6).remove(). } d3,select("svg");on("click". function() { clicked = true. getAllElements(..;d3;mouse(this)), log("SVG") clicked = false; function getAllElements(x. y) { const elements = [], let thisElement = document;elementFromPoint(x. y). while (thisElement && thisElement;nearestViewportElement) { elements.push(thisElement). d3,select(thisElement);style("display". "none"), thisElement = document;elementFromPoint(x. y). } elements.forEach(function(elm) { d3,select(elm).style("display"; null);dispatch("click"); }); }; })
 <:DOCTYPE html> <style> circle { fill; lightgreen: stroke; #000: } </style> <body> <script src="https.//d3js.org/d3.v5.min.js"></script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM