当使用嵌套在其他SVG中的空SVG时,不会触发OnClick



我有两个svg元素,一个在另一个里面是空的,当我点击它时,我需要在我的事件目标中获得内部svg元素但我无法使它工作。我注意到的第一个问题是,当使用Chrome时,内部svg显示为0高0宽,这在Firefox中是不会发生的。第二个问题是,在我为我的内部svg创建了一个onclick监听器之后,该事件永远不会触发。

我也试过:

  • 在我的外部svg中设置一个onclick监听器并获取事件目标。它总是返回外部svg,而从不返回内部svg。只有当我有一个矩形,圆,路径,(…(在里面时才有效
  • 将指针事件设置为内部svg中的all,而外部svg中没有。不会触发点击事件

我不能做的事:

  • 获取我的外部svg的第一个ElementChild,我会有更多的嵌套svg,我需要知道我点击了哪个

示例:

<div>
<svg id="0" x="0" y="0" height="100%" width="100%" viewBox="0 0 1000 1000">
<svg id="1" x="0" y="0" height="1000" width="1000" viewBox="0 0 1000 1000"></svg>
</svg>
</div>

jsfiddle示例在这里

我也有同样的问题
以下是我的调查结果
当我们点击嵌套svg中的任何svg元素时,就会触发嵌套svg上的点击事件。我用了两个圆圈来测试它。
如果我们点击svg的边框内的空白区域,则不会触发事件
Chrome和Firefox中的行为相同
我最终在最外层的svg上设置了事件处理程序。为了获得鼠标相对于内部svg的坐标,我使用d3.js中的实用函数,指针(event:any,target?:any(。

@Michael Mullany建议不要窝svgs。我不同意。我有一个复杂的图形编辑器,有几个绘图区域和两种类型的svg嵌套-有viewBox和没有它。到目前为止,它工作得很好。绝对比拥有像看不见的矩形这样的黑客要好。

我遇到的另一个小问题是,Chrome调试器中svg的边界矩形被可视化为左上角不在其给定位置。坐标原点是正确的。因此,该问题只影响调试。

严格地说,事件不是在空白处触发的问题取决于svg区域的定义。我们可以将区域定义为所有内容元素的联合。然后,我们观察到的行为是绝对正确的。

SVG似乎不支持点击侦听器。但是,您是否尝试在每个SVG周围放置另一个div,并在div中添加click listener?

<div>
<div id="svg1">
<svg id="0" x="0" y="0" height="100%" width="100%" viewBox="0 0 1000 1000">
<svg id="1" x="0" y="0" height="100%" width="100%" viewBox="0 0 1000 1000"></svg>
</svg>
</div>
</div>
document.getElementById("svg1").addEventListener("click", yourFunc)

我也遇到过同样的问题,@alero的描述是绝对正确的。

问题是浏览器不认为内部svg中的空白部分是它的一部分。内部svg的空白部分被视为外部svg的一部分。因此,解决方案非常简单:添加一个透明的矩形来转换整个内部svg,就可以了。这样做:

innersvg
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", innerSvgWidth)
.attr("height", innerSvgHeight)
.attr("fill-opacity", 0)
.attr("fill", "white");

注意:不要将fill设置为none,因为非填充矩形只有4行,而没有内部。

这实际上是预期的行为:

外部/父svg是一个HTMLDOM节点,因此它将应用css样式,如宽度、高度或背景颜色。

内部/嵌套svg是svgdom的一部分。

一个空的嵌套<svg>(以及一个<g>(元素将具有一个0 x 0 px的边界框。

尽管firefox在检查嵌套svg时显示宽度和高度值,但它也使用返回0x0px

let bb = nestedSvg.getBBox();
console.log(bb)

通过javaScript助手添加透明矩形

添加一个透明的背景矩形一点也不麻烦。

设置一个fill="transparent"——通过这种方式,您可以获得一个用于点击/指针事件的实心/填充区域。(否则就像敲开的窗户一样(。

你可以很容易地应用这样的辅助方法:

let svgOuter = document.getElementById("svg0");
// add transparent rectangles
let nestedSvgs = svgOuter.querySelectorAll("svg, g");
nestedSvgs.forEach((nestedSvg) => {
let rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", "0");
rect.setAttribute("y", "0");
rect.setAttribute("width", "100%");
rect.setAttribute("height", "100%");
rect.setAttribute("fill", "transparent");
nestedSvg.insertBefore(rect, nestedSvg.children[0]);
// add click event
nestedSvg.addEventListener("click", (e) => {
let id = e.currentTarget.id;
console.log("current id:", id);
});
});
*{
box-sizing:border-box
}
svg{
border:1px solid #ccc;
}
.svgInner{
background:red;
stroke:red;
stroke-width:1px;
}
<svg class="svgOuter" id="svg0" x="0" y="0" height="100%" width="100%" viewBox="0 0 1000 1000">
<svg class="svgInner" id="svg1" x="0" y="0" height="100%" width="33.333%" viewBox="0 0 333 1000">
</svg>
<svg class="svgInner" id="svg2" x="33.333%" y="0" height="100%" width="33.333%" viewBox="0 0 333 1000">
</svg>
<g transform="translate(666.67 0)" id="testGroup"></g>
</svg>

最好使用e.currentTarget,因为e.target将返回<rect>元素。

可选元素交集检查:Document.elementFromPoint((或Document.elementsFromPoint((

您仍然需要一个填充的背景区域,但您也可以获得document.elementsFromPoint()多个重叠元素-在某些情况下可能很方便。

let svgOuter = document.getElementById("svg0");
/**
* alternative: Document.elementFromPoint()
* https://developer.mozilla.org/en-US/docs/Web/API/Document/elementFromPoint
*/
svgOuter.addEventListener("click", (e) => {
let el = document.elementFromPoint(e.clientX, e.clientY);
let els = document.elementsFromPoint(e.clientX, e.clientY);
let ids = els.map(el=>{return el.id? el.id : el.nodeName});
let parentSVG = el.closest("svg");
console.log('current element', parentSVG.id);
console.log('all elements', ids);
});
// add transparent rectangles
let nestedSvgs = svgOuter.querySelectorAll("svg, g");
nestedSvgs.forEach((nestedSvg, i) => {
let rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.id= 'rect'+i;
rect.setAttribute("x", "0");
rect.setAttribute("y", "0");
rect.setAttribute("width", "100%");
rect.setAttribute("height", "100%");
rect.setAttribute("fill", "transparent");
nestedSvg.insertBefore(rect, nestedSvg.children[0]);
// add click event
nestedSvg.addEventListener("click", (e) => {
let id = e.currentTarget.id;
console.log("current id:", id);
});
});
*{
box-sizing:border-box
}
svg{
border:1px solid #ccc;
}
.svgOuter{
background:#ccc;
}
.svgInner{
background:red;
stroke:red;
stroke-width:1px;
}
<svg class="svgOuter" id="svg0" x="0" y="0" height="100%" width="100%" viewBox="0 0 1000 1000">
<svg class="svgInner" id="svg1" x="0" y="0" height="100%" width="33.333%" viewBox="0 0 333 1000">
</svg>
<svg class="svgInner" id="svg2" x="33.333%" y="0" height="100%" width="33.333%" viewBox="0 0 333 1000">
</svg>
<g transform="translate(666.67 0)" id="testGroup"></g>
</svg>

最新更新