如何捕获SVG点击事件并命中viewbox,而不是其上的元素?

3

我有一个包含多个路径的SVG文件,它被嵌入到HTML页面中使用object标签。JavaScript用于为每个路径提供交互功能 - 当单击路径时,将显示一个工具提示rect。这是它的样子:

SVG文件中的路径

我希望当有人点击与工具提示相关联的路径之外的区域时,工具提示会消失。为此,需要为每个路径添加此类事件监听器:

path.addEventListener("click", function(event){
    if (!isTipShown()){
        createTooltip()
    }
    else{
        hideTooltip()
    }
})

isTipShown, createTooltiphideTooltip是用于检查SVG DOM并相应地修改它的函数。

这个方法可以正常工作,但如果点击发生在路径本身之间的空白空间中,则会失败-因为没有对象来捕获它。

有哪些实现此功能的方法?

我的现有想法:

  • 创建一个覆盖整个视口的透明矩形,并将其用作点击目标。如何确保矩形到达所有底部?
  • 为整个HTML文档创建一个点击处理程序会起到作用,但仅当用户在视口本身外单击时。

您可以为文档使用单击处理程序并检查 event.currentTarget,如果单击目标是路径,则不执行任何操作,否则隐藏工具提示。 - Abhay Srivastav
我会做正确的事情。不确定你所说的“到底部”是什么意思。 - Robert Longson
Abhay,你所描述的方法是可行的,但当点击事件发生在路径之间的空白区域时会失败(因为没有元素接收到点击事件)。Robert,可以将其视为图层——透明矩形放置在所有元素的顶部,因此所有的点击事件都会传递给它(而不是下面的路径)。你看不见它,因为它是透明的——但它仍然会接收到点击事件。 - ralien
2个回答

2

不是我的SVG上的提示

如何在按下svg时最好地删除提示?
我会这样解决:

  1. 在提示上加ID。
  2. 使用ID修改现有提示。
  3. 在其他任何地方按下时删除带有ID的提示

通过重复使用提示,页面上只能有一个提示。
删除提示(而不是删除)使得可以在按下新路径时再次重用相同的提示。

以下是示例:

document.addEventListener("DOMContentLoaded", function() {
  var pathRed = document.getElementById("red");
  var pathOrange = document.getElementById("orange");
  var pathBlue = document.getElementById("blue");
  var paths = [pathRed, pathOrange, pathBlue];

  var toolTip = document.createElement("div");
  toolTip.id = "toolTip";
  var svg = document.getElementById("box");
  var shown = false;

  paths.forEach(function(element) {
    element.addEventListener("click", function(event) {
      if (shown == false) {
        toolTip.innerText = element.id;
        toolTip.style.top = (event.pageY) + "px";
        toolTip.style.left = (event.pageX) + "px";
        document.body.appendChild(toolTip);
        shown = true;
        //Only click the path
        event.stopPropagation();
      } else {
        removeToolTip();
      }
    });
  });

  svg.addEventListener("click", function(event) {
    removeToolTip();
    event.preventDefault();
  });

  function removeToolTip() {
    shown = false;
    if (document.body.contains(toolTip)) {
      document.body.removeChild(toolTip);
    }
  }

});
#toolTip {
  position: absolute;
  background-color: #00000099;
  padding: 2px;
  border-radius: 2px;
  color: white;
  font-size: 20px;
}
<h1>Click the boxes!</h1>
<svg id="box" viewBox="0 0 15 15" width="250px">
  <path id="red" fill="red" d="m0,0 5,0 0,5 -5,0Z"/>
  <path id="orange" fill="orange" d="m5,5 5,0 0,5 -5,0Z"/>
  <path id="blue" fill="blue" d="m10,10 5,0 0,5 -5,0Z"/>
</svg>


1
解决方案是确保矩形在路径下方,就像它是底层一样。
SVG没有图层的概念,但可以通过确保rect在SVG DOM中位于所有元素之前,并且所有后续元素将在其上方视觉上放置来实现:SVG没有图层的概念
<rect x="0" y="0" width="30" height="30" fill="purple"/>
<rect x="20" y="5" width="30" height="30" fill="blue"/>
<rect x="40" y="10" width="30" height="30" fill="green"/>
<rect x="60" y="15" width="30" height="30" fill="yellow"/>
<rect x="80" y="20" width="30" height="30" fill="red"/>

Layered squares

以下是实际操作中的步骤(svgDoc变量是根SVG元素):

    function createBackgroundRectangle(svgDoc){
        var rect = svgDoc.createElementNS("http://www.w3.org/2000/svg", 'rect')
        rect.setAttributeNS(null, 'height', 500)
        rect.setAttributeNS(null, 'width', 900)
        rect.setAttributeNS(null, 'id', 'pseudo-background')
        rect.setAttributeNS(null, 'x', 0)
        rect.setAttributeNS(null, 'y', 0)
        // the opacity is set to 0, so it doesn't get in the way visually. For debugging
        // purposes, you can change it to another value and see the actual rectangle
        rect.setAttributeNS(null, 'style', 'opacity:0;fill:#ffd42a;fill-opacity:1;')
        svgDoc.rootElement.insertBefore(rect, svgDoc.rootElement.children[0])
    }

svgDoc.rootElement.insertBefore(rect, svgDoc.rootElement.children[0])使其成为第一个,因为它插入在当前索引为0的子元素之前。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接