为了账号安全,请及时绑定邮箱和手机立即绑定

svg 元素可以触发 svg 元素的点击事件吗?

svg 元素可以触发 svg 元素的点击事件吗?

梵蒂冈之花 2022-09-23 09:59:46

新手问题在这里。


我想知道是否有可能触发其下方元素(路径,圆圈等)的“点击”事件,如果它们覆盖了鼠标坐标?最好在打字稿中通过使用d3.js库。


例如,考虑有两个圆圈(一个小圆圈在 z 位置上比一个大圆圈大),并且在单击时都显示一条消息。我希望如果我在小圆圈内单击,则会出现其消息,然后也会显示较大圆圈的消息。


以下是我的例子[编辑:D3的更改版本]的HTML代码:


<!DOCTYPE html>

<style>

  circle { fill: lightgreen; stroke: #000; }

</style>

<body>

<script src="https://d3js.org/d3.v5.min.js"></script>

以下是相关的脚本:


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();

  };

}

通过此代码,单击大圆圈将显示外部和SVG消息。单击小圆圈将显示内部和SVG消息,但我希望具有内部,外部和SVG序列。


[编辑:纠正了“传播”一词的误用]


我将元素的“指针事件”属性设置为“all”。我知道这使它们“非传递”,但我希望能够触发它们的事件,并触发它们下面的元素的事件。


或者,是否有任何好的方法可以获得覆盖给定坐标[X,Y]的svg元素列表(可以通过获得),以便可以手动触发它们各自的点击事件?d3.mouse


感谢您的帮助。


查看完整描述

2 回答

?
杨__羊羊

TA贡献1605条经验 获得超7个赞

首先,此解决方案使用您在 HTML 中引用的 D3 v5,而不是 D3 v2(已有 9 年历史)。

回到你的问题,你说:

我希望能够触发它们的事件并传播到它们下面的元素,直到SVG本身被触发。

好吧,这不是传播的意思。冒泡意味着在DOM树中上升,但这些圆圈不是父/子,它们只是兄弟姐妹。因此,你想要的与传播无关,所以我们需要一种不同的方法。

我在这里建议的方法使用 ,获取您单击的坐标下的所有元素,并结合用于调度单击(我在这里找到了该函数,并对其进行了修改以用于 D3)。最后,我需要标志,因为指针事件设置为 的元素被 忽略(顺便说一句,它是 ,不是)。elementFromPointd3.dispatchgetAllElementsclickednoneelementFromPointpointer-eventspointer-event

这是演示:

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>


查看完整回答
反对 回复 5天前
?
烙印99

TA贡献1483条经验 获得超11个赞

解决方案将取决于控制 svg 元素大小的形状类型和参数。如果需要非最优但通用的解决方案,可以使用元素的边界框。使用边界框,可以通过针对 [x,y] 目标测试 x 和 y 范围来确定是否有任何对象超过 [x,y]。


您可以优化以下代码并将其插入到任何 d3 功能块中:


<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

})


查看完整回答
反对 回复 5天前
  • 2 回答
  • 0 关注
  • 13 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信