十、坐标

两种坐标系(通过 event 获取):

  • 相对于窗口的clientX/clientY —— 类似于position:fixed,从窗口的顶部/左侧边缘计算而出
  • 相对于文档的pageX/pageY —— 从文档的顶部/左侧边缘计算得出

当页面在最开始时,窗口的左上角与文档的左上角对齐,它们的坐标彼此相等。

image-20220107223937227

当文档移动之后,元素的窗口相对坐标会发生变化,因为元素在窗口中移动,而元素在文档中的相对坐标是保持不变的。

image-20220107224052234

上两张图中表示,当元素滚动了:

pageY —— 元素在文档中的相对坐标是不变的,从文档顶部开始计算

clientY —— 元素在窗口中的相对坐标变化了,因为元素被滚上去了,它越来越靠近窗口顶部

10.1 元素坐标 getBoundingClientRect

elem.getBoundingClientRect()返回最小矩形的窗口坐标、返回元素的大小及其相对于视口的位置。

如果是标准盒子模型,元素的尺寸等于width/height + padding + border-width的总和。如果box-sizing: border-box,元素的的尺寸等于 width/height。

这个矩形将elem作为 DOMRect 类的对象。

主要的DOMRect的属性如下:

  • x/y —— 矩形原点相对于窗口的 x/y 坐标
  • width/height —— 矩形的 width/height (可以为负)
  • top/bottom —— 顶部/底部矩形边缘的坐标
  • left/right —— 左/右矩形边缘的 X 坐标
image-20220107225539273
  • left = x
  • top = y
  • right = x + width
  • bottom = y + height

当我们使用getBoundingClientRect获取元素的各坐标时,很有可能我们会获取到:

  • 小数
  • 负数,例如元素被滚上去之后,y 就是 top,相对于窗口来说就是负的

top/left与x/y从矩形的角度来说很有可能是不一致的,而且 width/height 属性也不一定是正数(虽然一直都会返回正数)。

原因是矩形有定向这么一说,如果是左上角开始往下伸展算是正向,那么从右下角往上伸展就是负向。所以矩形的 height/width 就是负数的。

以下就是一个由右下角往上伸展的矩形,它的 width/height 就应该是负数的(矩形角度分析)。那么此时,它的 x/y 就应该在右下角的起点位置,因为那才是矩形的原点。

image-20220107231453410

坐标的 right/bottom 与 css position 属性不同 我们用css position:fixed让元素根据文档定位后的left/top与getBoundingClientRect获取的left/top是类似的逻辑(如果不滚动的话,抛开单位,值可能相同),但是right/bottom是完全不一样的逻辑。

因为 css 定位中的 right 属性是距右边缘的距离,bottom 是距下边缘的距离。

而坐标的right/bottom永远是按照窗口的左上角开始计算的。

10.2 elementFromPoint(x, y)

对 document.elementFromPoint(x, y) 的调用会返回在窗口坐标 (x, y) 处嵌套最多(the most nested)的元素。

因为它使用的是窗口坐标,所以元素可能会因当前滚动位置而有所不同。

注意:

方法 document.elementFromPoint(x,y) 只对在可见区域内的坐标 (x,y) 起作用。

对于在窗口之外的坐标,elementFromPoint 返回 null

10.3 用于“fixed”的定位

如果我们希望创建一个独立存在于窗口上的元素(即使滚动后也依然存在),那么就可以用 fixed 定位后,再设置元素的 left 或者 top 即可。

下面是一个在button附近创建一个文字提示的例子

html
1<style>
2  body {
3    min-height: 2000px;
4  }
5  button {
6    display: block;
7    margin: 0 auto;
8  }
9</style>
10<button id="btn">在我下面创建一个提示信息</button>
11<script>
12  const rect = btn.getBoundingClientRect()
13  const [left, bottom] = [rect.left, rect.bottom]
14  btn.onclick = createMessage
15  function createMessage() {
16    const span = document.createElement('span')
17    span.style.cssText = 'color:red;position:fixed'
18    span.innerHTML = '我是提示信息'
19    span.left = left + 'px'
20    span.bottom = bottom + 'px'
21    document.body.append(span)
22  }
23</script>

在最小高度为 2000px 的页面高度下,提示信息会始终出现在窗口的同一位置上,即使滚动了也是如此。

如果不想要这种效果,可以将fixed改成absolute。

10.4 元素的文档坐标

文档坐标是从文档的左上角开始计算而不是窗口。

在 css 中,窗口坐标对应position:fixed,文档坐标则对应顶部元素的position:absolute

我们可以结合absolute来将元素放到文档的某个位置。

目前我们没有标准的方法可以获取元素的文档坐标。我们可以通过计算:

  • 元素的文档坐标 x = 文档水平滚出的部分的宽度 + 元素的窗口水平坐标 x
  • 元素的文档坐标 y = 文档垂直滚出的部分的宽度 + 元素的窗口垂直坐标 y

下面的函数是获取元素的文档坐标的函数

javascript
1function getCoords(elem) {
2  const rect = elem.getBoundingClientRect()
3  let left = window.pageXOffset + rect.left
4  let right = window.pageXOffset + rect.right
5  let top = window.pageYOffset + rect.top
6  let bottom = window.pageYOffset + rect.bottom
7  return {
8    left,
9    right,
10    top,
11    bottom,
12  }
13}

10.5 小结

页面的任何点都有坐标:

  1. 元素相对于窗口的坐标可以通过getBoundingClientRect获取
  2. 元素相对于文档的坐标可以通过window.pageXoffset/pageYoffset + getBoundingClientReact获取

如果我们想要让元素相对于窗口调整位置,可以设置 css 属性position:fixed

如果我们想要让元素相对于文档调整位置,可以设置 css 属性position:absolute