简体   繁体   English

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

[英]OnClick not being triggered when using empty SVG nested inside of other SVG

I have 2 svg elements, one empty inside of the other, and I need to get the inner svg element in my event target when I click on it but I can't make it work.我有 2 个 svg 元素,一个在另一个内部是空的,当我单击它时,我需要在我的事件目标中获取内部 svg 元素,但我无法使其工作。 The first problem I noticed is that when using Chrome the inner svg is displayed with 0 height and 0 width, something that doesn't happen in Firefox. The second problem is that after I create a onclick listener for my inner svg, the event never triggers.我注意到的第一个问题是,当使用 Chrome 时,内部 svg 显示为 0 高度和 0 宽度,这在 Firefox 中不会发生。第二个问题是,在我为内部 svg 创建 onclick 侦听器后,事件永远不会触发器。

I also tried:我也试过:

  • Setting a onclick listener in my outer svg and get the event target.在我的外部 svg 中设置一个 onclick 侦听器并获取事件目标。 It always returns the outer svg and never the inner svg. Only works if I have a rect, circle, path, (...) inside.它总是返回外部 svg 而从不返回内部 svg。只有当我有一个 rect,circle,path,(...) inside 时才有效。
  • Setting pointer-events to all in my inner svg and none in the outer one.将指针事件设置为我内部 svg 中的所有事件,而外部事件中则没有。 Doesn't trigger the click event.不触发点击事件。

What I can't do:我不能做什么:

  • Get the firstElementChild of my outer svg, I will have more nested svg's and I need to know exactly which one I clicked.获取我外部 svg 的第一个 ElementChild,我将有更多嵌套的 svg,我需要确切地知道我点击了哪个。

Example:例子:

<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 example here这里的 jsfiddle 示例

It looks like SVG does not supports the click listeners.看起来 SVG 不支持点击监听器。 But have you tried to put another div around each SVG and add click listener to the div?但是您是否尝试在每个 SVG 周围放置另一个 div 并将点击侦听器添加到该 div?

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

I have the same problem.我也有同样的问题。
Here are results of my investigation.以下是我的调查结果。
The click event on nested svg gets triggered when we click on any svg element inside the nested svg.当我们单击嵌套 svg 内的任何 svg 元素时,将触发嵌套 svg 上的单击事件。 I used two circles to test it.我用了两个圆圈来测试它。
The event gets not triggered if we click on the empty space withing bounding rectangle of the svg.如果我们单击 svg 的边界矩形的空白区域,则不会触发该事件。
The behavior is the same in Chrome and Firefox. Chrome 和 Firefox 中的行为相同。
I ended up setting event handler on outermost svg.我最终在最外面的 svg 上设置了事件处理程序。 To get mouse coordinates relative to inner svg I use utility function from d3.js, pointer(event: any, target?: any).为了获得相对于内部 svg 的鼠标坐标,我使用来自 d3.js 的实用程序 function,指针(事件:任意,目标?:任意)。

@Michael Mullany recommends not to nest svgs at all. @Michael Mullany 建议根本不要嵌套 svgs。 I don't agree with that.我不同意这一点。 I have complex graphical editor with several drawing areas and two types of svg nesting - with viewBox and without it.我有复杂的图形编辑器,有几个绘图区域和两种类型的 svg 嵌套 - 有 viewBox 和没有它。 So, far it is working fine.所以,到目前为止它工作正常。 Definitely better than to have hacks like invisible rectangles.绝对比拥有像隐形矩形这样的技巧更好。

The only other minor issue I have it is that bounding rectangle of svg in Chrome debugger gets visualized with top-left corner not at its given position.我唯一遇到的另一个小问题是 Chrome 调试器中 svg 的边界矩形被可视化,左上角不在其给定的 position 处。 Coordinates origin is correct though.坐标原点是正确的。 So, the issue affects only debugging.因此,该问题仅影响调试。

Speaking strictly, the issue of events not being triggered on empty space depends on definition of what should be considered the area of the svg.严格来说,没有在空白空间上触发事件的问题取决于对 svg 区域的定义。 We might define the area as union of all content elements.我们可以将区域定义为所有内容元素的联合。 And then, the behavior we observe is absolutely correct.然后,我们观察到的行为是绝对正确的。

I've encountered the same problem, and the description from @alehro is absolutely right.我遇到了同样的问题,@alehro 的描述是完全正确的。

The problem turns out to be that the browser doesn't think the white space in the inner svg as part of it.问题原来是浏览器不认为内部svg中的空白是它的一部分。 The empty part of the inner svg is seen to be part of the outter svg .内部svg的空部分被视为外部svg的一部分。 So the solution is pretty simply: add a transparent rectangle convers the whole inner svg , and it will be alright.所以解决方法很简单:加一个透明的矩形覆盖整个内部的svg就可以了。 Do something like this:做这样的事情:

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

Note: do not set fill to none , since a non-filled rect only has 4 lines but no the inner part.注意:不要将fill设置为none ,因为未填充的矩形只有 4 行但没有内部部分。

That's actually the expected behaviour:这实际上是预期的行为:

The outer/parent svg is an HTML DOM node - so it will be applied css styles like width, height or background colour.外部/父 svg 是一个 HTML DOM 节点 - 因此它将应用 css styles 像宽度,高度或背景颜色。

The inner/nested svg is part of the SVG DOM.内部/嵌套的 svg 是 SVG DOM 的一部分。

An empty nested <svg> (as well aa <g> ) element will have a bounding box of 0 x 0 px.一个空的嵌套<svg> (以及 aa <g> )元素将有一个 0 x 0 像素的边界框。

Although, firefox shows width and height values while inspecting the nested svgs – it also returns 0x0px using尽管 firefox 在检查嵌套的 svg 时显示了宽度和高度值——它也返回 0x0px 使用

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

Add transparent rectangles via javaScript helper通过 javaScript helper 添加透明矩形

Appending a transparent background rectangle isn't a hack at all.附加透明背景矩形根本不是 hack。

Set a fill="transparent" - this way, you get a solid/filled area for click/pointer events.设置一个fill="transparent" - 这样,您就可以为点击/指针事件获得一个实心/填充区域。 (otherwise it's like knocking on a open window). (否则就像敲一扇打开的窗户)。

You can easily apply a helper method like so:您可以像这样轻松地应用辅助方法:

 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>

Better use e.currentTarget since e.target will return the <rect> element.最好使用e.currentTarget ,因为e.target将返回<rect>元素。

Alternative element intersection check: Document.elementFromPoint() or Document.elementsFromPoint()替代元素交叉检查: Document.elementFromPoint()Document.elementsFromPoint()

You still need a filled background area, but you can also get document.elementsFromPoint() multiple overlapping elements - might be handy in some scenarios.您仍然需要填充背景区域,但您也可以获得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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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