【问题标题】:Can svg element trigger click event of svg elements under itself?svg元素可以触发自身下svg元素的点击事件吗?
【发布时间】:2020-06-08 19:36:07
【问题描述】:

这里是菜鸟问题。

我想知道如果它们覆盖了鼠标坐标,是否可以触发它下面的元素(路径、圆、...)的“单击”事件? 最好在 typescript 中使用 d3.js 库。

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

这是我的示例的 HTML 代码[编辑:D3 的更改版本]

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

这里是相关的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();
  };
}

使用此代码单击大圆圈会显示 OUTERSVG 消息。点击小圆圈会显示 INNERSVG 消息,但我想要序列 INNER, OUTER > 和 SVG

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

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

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

感谢您的帮助。

【问题讨论】:

  • 如果您可以在此处发布代码示例,以帮助其他人解码您要完成的工作
  • @tlaminator 你是对的!我用一个例子编辑了我的帖子。
  • 已编辑帖子:在他的回答中遵循了@Gerardo Furtado 的建议。

标签: javascript typescript d3.js


【解决方案1】:

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

回到你的问题,你说:

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

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

我建议的方法在这里使用elementFromPoint,以获取您单击的坐标下的所有元素,并结合d3.dispatch 用于调度点击(我找到了getAllElements 函数here,并将其修改为与D3 一起使用)。最后,我需要clicked 标志,因为指针事件设置为none 的元素会被elementFromPoint 忽略(顺便说一下,它是pointer-events,而不是pointer-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>

【讨论】:

  • 非常感谢!这似乎符合我的需求。我将很快编辑我的问题以避免使用“传播”(当我写它时我觉得它会有问题:))并接受你的答案作为解决方案。
【解决方案2】:

解决方案将取决于控制 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
})

【讨论】:

  • 感谢您的回答,但您的解决方案对我没有帮助。我编辑了我的帖子以提供一个更好地描述我的问题的示例。
猜你喜欢
  • 1970-01-01
  • 2016-10-21
  • 2014-07-27
  • 2019-05-05
  • 2017-06-20
  • 1970-01-01
  • 2022-01-07
  • 1970-01-01
  • 2012-12-07
相关资源
最近更新 更多