yaodebian / blog

blog notes

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

原生 js 如何获取定位

yaodebian opened this issue · comments

foreword(前言)

本篇文章作为前面一篇文章(原生 js 如何获取宽高)的后续,主要分析原生 javascript 获取元素定位的 api 以及一些优化。

JS 定位都有哪些获取方式呢

  • clientLeft & clientTop
  • offsetLeft & offsetTop
  • scrollLeft & scrollTop

以下则会针对 clientTop、offsetTop、scrollTop 作为讲解。

clientLeft & clientTop

The width of the top border of an element in pixels. It is a read-only, integer property of element.

clientTop 是一个元素顶部边框的宽度,也就是 border-top-width。它是一个只读属性,返回一个整数。

offsetLeft & offsetTop

The HTMLElement.offsetTop read-only property returns the distance of the current element relative to the top of the offsetParent node.

offsetTop 同样也是一个只读属性,它返回一个元素相对于最近一个 offsetParent 元素的 top 值。

关于 offsetParent 的定义,MDN 文档上是这样描述的:

HTMLElement.offsetParent 是一个只读属性,返回一个指向最近的(指包含层级上的最近)包含该元素的定位元素或者最近的 table,td,th,body 元素。当元素的 style.display 设置为 "none" 时,offsetParent 返回 null。offsetParent 很有用,因为 offsetTop 和 offsetLeft 都是相对于其内边距边界的。

以下为 demo 示例:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>position test</title>
  <style>
    .box {
      width: 600px;
      height: 600px;
      padding: 20px;
      background: red;
      border: 10px solid green;
      position: relative;
    }

    .box__container {
      width: 400px;
      height: 400px;
      background: blue;
      border: 10px solid #ffffff;
    }

    .box__content {
      width: 100px;
      height: 100px;
      padding: 10px;
      background: yellow;
      border: 10px solid #cccccc;
      margin-top: 20px;
    }
  </style>
</head>

<body>
  <div class="box">
    <div class="box__container">
      <div class="box__content"></div>
    </div>
  </div>
  <script>
    var box = document.querySelector('.box'),
      content = document.querySelector('.box__content')

    console.log('box offsetTop:', box.offsetTop)
    console.log('box__content offsetTop:', content.offsetTop)
  </script>
</body>

</html>

打印结果如下:

box offsetTop: 8
box__content offsetTop: 50

如果把 .box 的 position 属性去掉,则打印如下:

box offsetTop: 8
box__content offsetTop: 68

也就是说,当元素不做任何定位处理的时候(不加 position 属性),元素会往上找祖先元素,如果找到一个以 offsetParent 元素,则相对于它的 boder 内边开始计算,而如果没有找到这样的祖先,则相对于窗口顶部开始计算。

让我们再看一个 demo:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>position test</title>
  <style>
    .box {
      width: 600px;
      height: 600px;
      padding: 20px;
      background: red;
      border: 10px solid green;
      position: relative;
    }

    .box__container {
      width: 400px;
      height: 400px;
      background: blue;
      border: 10px solid #ffffff;
    }

    .box__content {
      width: 100px;
      height: 100px;
      padding: 10px;
      background: yellow;
      border: 10px solid #cccccc;
      margin-top: 20px;
      position: absolute;
      left: 0;
      top: 10px;
    }
  </style>
</head>

<body>
  <div class="box">
    <div class="box__container">
      <div class="box__content"></div>
    </div>
  </div>
  <script>
    var box = document.querySelector('.box'),
      content = document.querySelector('.box__content')

    console.log('box offsetTop:', box.offsetTop)
    console.log('box__content offsetTop:', content.offsetTop)
  </script>
</body>

</html>

打印结果为:

box offsetTop: 8
box__content offsetTop: 30

而如果我们将 .box__content 的 position 改成 fixed,我们会发现结果还是不变的。

经过一些其他的测试,我们可以得出以下结论:

  • 当元素没有做定位处理,或者元素的 position 值为 relative、absolute 时,素会往上找祖先元素,如果找到一个 offsetParent 元素,则相对于它的 boder 内边开始计算,而如果没有找到这样的祖先,则相对于窗口顶部开始计算;
  • 当元素做了 fixed 定位时,计算结果始终是相对于窗口顶部的;

值得注意:

In compliance with the specification, this property will return null on Webkit if the element is hidden (the style.display of this element or any ancestor is "none") or if the style.position of the element itself is set to "fixed".
This property will return null on Internet Explorer (9) if the style.position of the element itself is set to "fixed". (Having display:none does not affect this browser.)

文档中说:

  • 在 webkit 内核的浏览器中,如果元素本身或者任何祖先元素的 display 设为 none 值,或者元素的 position 设为 fixed,offsetTop 将会返回 null;
  • IE9 中,如果 position 设为 fixed,offsetTop 将会返回 null;

而经过测试(在谷歌浏览器中进行测试),并没有文档中所说返回 null,实际上在我浏览器中返回的是 0。(猜想或许是我浏览器版本比较高的原因,浏览器已经修复了这个问题,个人浏览器版本为:86.0.4240.111)

scrollLeft & scrollTop

The Element.scrollTop property gets or sets the number of pixels that an element's content is scrolled vertically.

当元素出现垂直滚动条时,scrollTop 是元素垂直滚动的距离。(scrollTop 是可读/写的)

注意:当根节点出现滚动条时,根节点返回的 scrollTop 是 window.scrollY。

我们可以写一段 demo 来测试一下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>scrollTop</title>
  <style>
    .box {
      width: 500px;
      height: 500px;
      background: yellow;
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <script>
    window.addEventListener('scroll', function () {
      console.log(document.documentElement.scrollTop === window.scrollY)
    })
  </script>
</body>

</html>

注:clientXXX & offsetXXX & scrollXXX 获取的数据均为 round 四舍五入取整过,如果想要精确的数值,则可以通过 element.getBoundingClientRect() 进行获取。

浏览器兼容性

详见以下文章末尾:

参考文章

last(最后)

非常感谢您能阅读完这篇文章,您的阅读是我不断前进的动力。

对于上面所述,有什么新的观点或发现有什么错误,希望您能指出。

最后,附上个人常逛的社交平台:

知乎:https://www.zhihu.com/people/bi-an-yao-91/activities

csdn:https://blog.csdn.net/YaoDeBiAn

github: https://github.com/yaodebian

个人目前能力有限,并没有自主构建一个社区的能力,如有任何问题或想法与我沟通,请通过上述某个平台联系我,谢谢!!!