【问题标题】:document.elementFromPoint returning different elements for same inputdocument.elementFromPoint 为相同的输入返回不同的元素
【发布时间】:2022-01-25 05:53:49
【问题描述】:

我正在使用document.elementFromPoint 来确定某个点的 svg 形状。我在image 上试验elementFromPoint,当我注意到相同的输入得到不同的输出时。

这正是我为重现性所做的。

  1. 我从链接的 svg 中剪切并粘贴了 svg 标记,并将其插入到 html 文件的正文中。 html 文件是使用单个 ! vscode 中的 emmet 快捷方式。身体里没有别的东西。唯一的 CSS 是 body{margin:0;}
  2. 在 chrome 中,我到处打电话给elementFromPoint。大部分在 0.25,50,但有时只是稍微向右或向左一点。有时会一遍又一遍地用相同的参数调用,看看它是否会改变。
  3. 在此期间没有进行滚动。甚至没有选项,因为没有滚动条。

我的问题是为什么会这样?它有规律吗?有没有办法防止它? 谢谢。

【问题讨论】:

  • 无法复制。你能发布一个确切的坐标序列来触发这个吗?

标签: javascript svg viewport webapi


【解决方案1】:

虽然我无法告诉您为什么会发生这种情况,或者如何防止这种情况发生,但“模式”,或者更好地说,可以缩小不一致行为的原因。

让我们看看调用elementFromPoint 返回的路径。其中有两个,如果省略 id 和类,即使考虑到它们的父元素,它们看起来也一样:

<g transform="translate(-1.775,-1.575)">
    <path d="M 1.9,551.3 V 1.7 H 1102 V 551.3 H 2.3" />
</g>

如果您重写路径以解析 transform,您将在(浏览器)视口坐标中获得路径 - 为清楚起见,我已将 VH 命令重写为 L

<path d="M 0.125,549.725 L 0.125,0.125 L 1100.125,0.125 L 1100.125,549.725 L 0.525,549.725" />

SVG 包含一个样式表,用于描述这些元素的填充和描边。这是相关的摘录:

.seabase {
  fill: #C6ECFF;
  stroke: none;
}
.mapborder {
  fill: none;
  stroke: #0978AB;
}
path, circle {
  stroke-width: 0.25;
}

那么应该从document.elementFromPoint(0.25,50) 返回什么?如果 pointer-events 属性允许该元素成为命中测试的目标,则在给定坐标处找到的最顶部元素。

pointer-events 没有为元素明确设置,所以默认值visiblePainted 适用。这意味着可以命中(至少部分)不透明的填充或描边。

两个候选项的底部元素&lt;path class="seabase"/&gt; 有一个可见的填充,但没有边框。点 (0.25,50) 在该填充内。

两个候选元素中最顶部的元素&lt;path class="mapborder"/&gt; 没有填充,但有一个宽度为 0.25 的可见边框,一半延伸到路径轮廓的内侧,一半延伸到路径轮廓的外侧。在左侧,有一个垂直子路径M 0.125,549.725 L 0.125,0.125。不涉及角的细节,笔画可以等效地绘制为具有定义的填充路径

M 0,549.725 L 0,0.125 L 0.25,0.125 L 0.25,549.725 Z

换句话说,点(0.25,50) 正好在笔划的轮廓上。该规范对consequences 非常清楚:

形状的零宽度几何轮廓​​包含在要绘制的区域中。

该点应该命中mapborder 元素,因为该点是其可见的绘制笔划的一部分。如果如您所描述的,浏览器有时会返回 seabase 元素,那么它就是错误的。

我可以继续推测这种行为的原因,但我认为这没有实际意义。

你能阻止它吗?仅当您能够始终避免所有路径的确切轮廓,或者如果它们有笔划,则该笔划的轮廓。这几乎不切实际,因为您必须找出浏览器是否错过了它应该点击的东西,而您却不知道什么错过了。

最后,还有第二个界面可以使用。它是 SVG 规范的一部分,而elementFromPoint 是 CSSOM 规范的一部分。这是否会对浏览器的实现产生影响,我不能说。

document.querySelector('.seabase').isPointInFill(new DOMPoint(2.025,48.425));
document.querySelector('.mapborder').isPointInStroke(new DOMPoint(2.025,48.425));

请注意,您需要提供一个要测试的元素,您无法获得哪个元素位于顶部的信息,并且坐标在本地用户空间中表示(在应用 transform 之前)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多