document.elementFromPoint 为相同的输入返回不同的元素
document.elementFromPoint returning different elements for same input
我正在使用 document.elementFromPoint
来确定某个点的 svg 形状。我在这个 image when I noticed that I was getting different outputs for the same input.
上试验 elementFromPoint
这正是我为再现性所做的。
- 我从链接的 svg 中剪切并粘贴了 svg 标签,并将其插入到 html 文件的 body 中。 html 文件是使用单个 ! vscode 中的 emmet 快捷方式。 body 中没有其他内容。唯一的 css 是
body{margin:0;}
.
- 在chrome我到处打电话给
elementFromPoint
。大部分在 .25,50,但有时稍微向右或向左一点。有时用相同的参数一遍又一遍地调用以查看它是否会改变。
- 这段时间没有滚动。甚至没有选项,因为没有滚动条。
我的问题是为什么会这样?有模式吗?有办法防止吗?
谢谢。
虽然我无法告诉您为什么会发生这种情况,或者如何防止这种情况发生,但“模式”,或者更确切地说,可以缩小不一致行为的原因。
让我们看看您对 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
,您将获得(浏览器)视口坐标中的路径 - 为清楚起见,我重写了 V
和 H
命令为 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
属性 允许该元素成为 hit-test.
的目标
pointer-events
没有为元素明确设置,因此应用默认值 visiblePainted
。这意味着可以命中(至少部分)不透明的填充或描边。
两个候选项的底部元素 <path class="seabase"/>
具有可见的填充,但没有边框。点 (0.25,50)
位于该填充内。
两个候选项的最上面的元素,<path class="mapborder"/>
,没有填充,但有一个可见的宽度为 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:
非常清楚
The zero-width geometric outline of a shape is included in the area to be painted.
该点应命中 mapborder
元素,因为该点是其可见的绘制笔划的一部分。如果像你描述的那样,浏览器有时 returns 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
之前)。
我正在使用 document.elementFromPoint
来确定某个点的 svg 形状。我在这个 image when I noticed that I was getting different outputs for the same input.
elementFromPoint
这正是我为再现性所做的。
- 我从链接的 svg 中剪切并粘贴了 svg 标签,并将其插入到 html 文件的 body 中。 html 文件是使用单个 ! vscode 中的 emmet 快捷方式。 body 中没有其他内容。唯一的 css 是
body{margin:0;}
. - 在chrome我到处打电话给
elementFromPoint
。大部分在 .25,50,但有时稍微向右或向左一点。有时用相同的参数一遍又一遍地调用以查看它是否会改变。 - 这段时间没有滚动。甚至没有选项,因为没有滚动条。
我的问题是为什么会这样?有模式吗?有办法防止吗? 谢谢。
虽然我无法告诉您为什么会发生这种情况,或者如何防止这种情况发生,但“模式”,或者更确切地说,可以缩小不一致行为的原因。
让我们看看您对 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
,您将获得(浏览器)视口坐标中的路径 - 为清楚起见,我重写了 V
和 H
命令为 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
属性 允许该元素成为 hit-test.
pointer-events
没有为元素明确设置,因此应用默认值 visiblePainted
。这意味着可以命中(至少部分)不透明的填充或描边。
两个候选项的底部元素 <path class="seabase"/>
具有可见的填充,但没有边框。点 (0.25,50)
位于该填充内。
两个候选项的最上面的元素,<path class="mapborder"/>
,没有填充,但有一个可见的宽度为 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:
The zero-width geometric outline of a shape is included in the area to be painted.
该点应命中 mapborder
元素,因为该点是其可见的绘制笔划的一部分。如果像你描述的那样,浏览器有时 returns 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
之前)。