为了移动元素,我们应该熟悉坐标。
大多数 JavaScript 方法处理两个坐标系之一
- 相对于窗口 – 类似于
position:fixed
,从窗口顶部/左侧边缘计算。- 我们将这些坐标表示为
clientX/clientY
,这种名称的原因将在我们学习事件属性时变得清晰。
- 我们将这些坐标表示为
- 相对于文档 – 类似于文档根元素中的
position:absolute
,从文档顶部/左侧边缘计算。- 我们将其表示为
pageX/pageY
。
- 我们将其表示为
当页面滚动到最开始,使得窗口的顶部/左侧角恰好是文档的顶部/左侧角时,这些坐标彼此相等。但在文档移动后,元素的窗口相对坐标会发生变化,因为元素在窗口中移动,而文档相对坐标保持不变。
在这张图片中,我们在文档中取一个点,并演示它在滚动之前(左)和滚动之后(右)的坐标
当文档滚动时
pageY
– 文档相对坐标保持不变,它从文档顶部(现在已滚动出去)开始计算。clientY
– 窗口相对坐标发生了变化(箭头变短),因为同一个点更靠近窗口顶部。
元素坐标:getBoundingClientRect
方法 elem.getBoundingClientRect()
返回内置 DOMRect 类的对象,该对象表示包围 elem
的最小矩形的窗口坐标。
主要 DOMRect
属性
x/y
– 矩形原点相对于窗口的 X/Y 坐标,width/height
– 矩形的宽度/高度(可以为负)。
此外,还有派生属性
top/bottom
– 矩形顶部/底部边缘的 Y 坐标,left/right
– 矩形左侧/右侧边缘的 X 坐标。
例如,单击此按钮以查看其窗口坐标
如果您滚动页面并重复,您会注意到,随着窗口相对按钮位置的变化,其窗口坐标(如果您垂直滚动,则为 y/top/bottom
)也会发生变化。
以下是 elem.getBoundingClientRect()
输出的图片
如您所见,x/y
和 width/height
完全描述了矩形。派生属性可以轻松地从它们计算出来
left = x
top = y
right = x + width
bottom = y + height
请注意
- 坐标可能是小数,例如
10.5
。这是正常的,浏览器在内部计算中使用小数。当设置到style.left/top
时,我们不必对它们进行四舍五入。 - 坐标可能是负数。例如,如果页面滚动到
elem
现在位于窗口上方,则elem.getBoundingClientRect().top
为负数。
x/y
,为什么 top/left
存在?在数学上,一个矩形由其起始点 (x,y)
和方向向量 (width,height)
唯一定义。因此,其他派生属性是为了方便。
从技术上讲,width/height
可能为负,这允许使用“定向”矩形,例如,用正确标记的开始和结束来表示鼠标选择。
负的 width/height
值表示矩形从其右下角开始,然后“向上”增长。
这是一个具有负宽度
和高度
的矩形(例如,宽度=-200
,高度=-100
)
如您所见,在这种情况下,left/top
并不等于 x/y
。
然而在实践中,elem.getBoundingClientRect()
始终返回正宽度/高度,在此我们仅提及负宽度/高度
,以便您理解为什么这些看似重复的属性实际上并非重复。
x/y
由于历史原因,Internet Explorer 不支持 x/y
属性。
因此,我们可以制作一个填充(在 DomRect.prototype
中添加 getter),或者仅使用 top/left
,因为对于正宽度/高度
,它们始终与 x/y
相同,尤其是在 elem.getBoundingClientRect()
的结果中。
窗口相对坐标与 CSS position:fixed
之间存在明显的相似之处。
但在 CSS 定位中,right
属性表示到右边缘的距离,bottom
属性表示到底边缘的距离。
如果我们只看上面的图片,我们就可以看到在 JavaScript 中并非如此。所有窗口坐标都是从左上角开始计算的,包括这些坐标。
elementFromPoint(x, y)
对 document.elementFromPoint(x, y)
的调用返回窗口坐标 (x, y)
处最嵌套的元素。
语法为
let elem = document.elementFromPoint(x, y);
例如,以下代码突出显示并输出当前位于窗口中间的元素的标签
let centerX = document.documentElement.clientWidth / 2;
let centerY = document.documentElement.clientHeight / 2;
let elem = document.elementFromPoint(centerX, centerY);
elem.style.background = "red";
alert(elem.tagName);
由于它使用窗口坐标,因此元素可能因当前滚动位置而异。
elementFromPoint
返回 null
方法 document.elementFromPoint(x,y)
仅在 (x,y)
位于可见区域内时才起作用。
如果任何坐标为负或超过窗口宽度/高度,则它将返回 null
。
如果我们不检查它,则可能会发生以下典型错误
let elem = document.elementFromPoint(x, y);
// if the coordinates happen to be out of the window, then elem = null
elem.style.background = ''; // Error!
用于“固定”定位
大多数时候,我们需要坐标来定位某些内容。
要在元素附近显示某些内容,我们可以使用 getBoundingClientRect
获取其坐标,然后将 CSS position
与 left/top
(或 right/bottom
)一起使用。
例如,下面的函数 createMessageUnder(elem, html)
在 elem
下显示消息
let elem = document.getElementById("coords-show-mark");
function createMessageUnder(elem, html) {
// create message element
let message = document.createElement('div');
// better to use a css class for the style here
message.style.cssText = "position:fixed; color: red";
// assign coordinates, don't forget "px"!
let coords = elem.getBoundingClientRect();
message.style.left = coords.left + "px";
message.style.top = coords.bottom + "px";
message.innerHTML = html;
return message;
}
// Usage:
// add it for 5 seconds in the document
let message = createMessageUnder(elem, 'Hello, world!');
document.body.append(message);
setTimeout(() => message.remove(), 5000);
单击按钮以运行它
该代码可以修改为在左侧、右侧、下方显示消息,应用 CSS 动画以“淡入”显示消息,等等。这很容易,因为我们拥有该元素的所有坐标和大小。
但请注意一个重要细节:当页面滚动时,消息会远离按钮。
原因很明显:消息元素依赖于 position:fixed
,因此当页面滚动时,它会停留在窗口的同一位置。
要更改此设置,我们需要使用基于文档的坐标和 position:absolute
。
文档坐标
相对于文档的坐标从文档的左上角开始,而不是窗口。
在 CSS 中,窗口坐标对应于 position:fixed
,而文档坐标类似于顶部的 position:absolute
。
我们可以使用 position:absolute
和 top/left
将某个内容放置在文档的特定位置,以便它在页面滚动期间停留在该位置。但我们首先需要正确的坐标。
没有标准方法可以获取元素的文档坐标。但编写该方法很容易。
这两个坐标系通过以下公式连接
pageY
=clientY
+ 文档已滚动出的垂直部分的高度。pageX
=clientX
+ 文档已滚动出的水平部分的宽度。
函数 getCoords(elem)
将从 elem.getBoundingClientRect()
获取窗口坐标,并将当前滚动量添加到其中
// get document coordinates of the element
function getCoords(elem) {
let box = elem.getBoundingClientRect();
return {
top: box.top + window.pageYOffset,
right: box.right + window.pageXOffset,
bottom: box.bottom + window.pageYOffset,
left: box.left + window.pageXOffset
};
}
如果在上面的示例中,我们将其与 position:absolute
一起使用,那么消息将在滚动时停留在元素附近。
修改后的 createMessageUnder
函数
function createMessageUnder(elem, html) {
let message = document.createElement('div');
message.style.cssText = "position:absolute; color: red";
let coords = getCoords(elem);
message.style.left = coords.left + "px";
message.style.top = coords.bottom + "px";
message.innerHTML = html;
return message;
}
摘要
页面上的任何点都有坐标
- 相对于窗口 –
elem.getBoundingClientRect()
。 - 相对于文档 –
elem.getBoundingClientRect()
加上当前页面滚动量。
窗口坐标非常适合与 position:fixed
一起使用,而文档坐标非常适合与 position:absolute
一起使用。
这两个坐标系各有优缺点;有时我们需要其中一个,就像 CSS position
absolute
和 fixed
一样。
评论
<code>
标签,对于多行代码,请将其包装在<pre>
标签中,对于 10 行以上的代码,请使用沙盒 (plnkr、jsbin、codepen…)