乐趣区

浏览器元素尺寸与位置查询指南

前言

这篇文章主要介绍了有关浏览器中获取坐标以及尺寸的几种途径, 算是比较全的一篇文章了.

在浏览器中获取元素的坐标以及尺寸是非常容易的, 有非常多种方式来完成这些需求, 但是杂乱的 API 和很多兼容处理导致了浏览器中没有直接的方式来获取我们想要的结果.

仔细想想这个问题, 为什么浏览器并没有直接提供一个简单的属性就告诉你浏览器窗口的大小, 或者一个元素的宽高.

就拿 div 元素来举例, 我们有很多的问题影响到了元素宽高:

  • border 是否纳入宽高的计算?
  • padding 是否纳入宽高的计算?
  • magin 是否纳入宽高的计算?
  • box-sizing:border-box; 时候该如何计算?
  • 父元素使用了 overflow:hidden; 把我们的元素裁剪了, 这时候的宽高该如何计算?
  • 元素使用了 overflow 出现了滚动条此时该如何计算?

而如果要获取一个浏览器窗口的大小, 你还要面对我们到底是要获取哪个大小?

  • 屏幕大小?
  • 浏览窗口大小?
  • 浏览区域大小?
  • 是否包含滚动条?

当然最终你还要面临一个兼容问题, 致我们敬爱的 IE 浏览器, 不过本文可不探讨浏览器之间的差异. 不过本文的涉及到的内容应该在 IE9 以上都是可以正常使用的(不过建议你还是查下 can i use 或者 MDN).

浏览器部分

浏览器的宽高计算主要通过 window 对象来完成.

这个对象上提供了几个关键属性:

  • window.innerWidth
  • window.innerHeight
  • window.outerWidth
  • window.outerHeight

用人类语言来描述这几个属性就是.

属性名称 人类解释
innerWidth 获取页面可视区域的宽度包括右侧的滚动条(如果有的话). 所谓的可视区域就是 HTML 页面的内容区域不包括浏览器自身的 ui 所占用的空间(地址栏和菜单栏等).
innerHeight 获取页面可视区域的高度包括底部的滚动条(如果有的话). 解释同上.
outerWidth 获取浏览器窗口宽度.
outerHeight 获取浏览器窗口高度.

友情出演 windows 画图:

注意: 单位均为 px.

注意 : 滚动条并不视为浏览器的 ui 中的内容, 而是视为内容区域的一部分, 右侧默认的滚动条的宽度包含在window.innerWidth 中, 但是不属于 html 元素和 html 下的任何元素.

元素部分

属性

属性名称 人类解释
element.clientWidth 元素内容区域宽度 +padding 的宽度, 如果宽度溢出且裁剪那么不包含被裁剪掉的部分.
element.scrollWidth 当子元素宽度溢出, 这里提供的是子元素的宽度包括溢出的部分, 大小计算和 clientWidth 一样.
element.offsetWidth 相当于计算边框宽度的 clientWidth, 宽度计算为 content+padding+border.
element.clientHeight 元素内容区域高度 +padding 的高度, 如果高度溢出且裁剪那么不包含被裁剪掉的部分.
element.scrollHeight 当子元素高度溢出, 这里提供的是子元素的高度包括溢出的部分, 大小计算和 clientHeight 一样.
element.offsetHeight 相当于计算边框高度的 clientHeight, 高度计算为 content+padding+border.
element.clientLeft 元素左边框的宽度
element.scrollLeft 计算较为复杂, 看后续详解
element.offsetLeft 计算比较复杂, 看下面详解
element.clientTop 元素上边框的宽度
element.scrollTop 计算较为复杂, 看后续详解
element.offsetTop 计算比较复杂, 看下面详解

滚动条的规律

无论是横向滚动条还是纵向滚动条, 对于测量 clientXXX 这个单位来说是不包括滚动条的宽 (高) 的.

例如在下面这张图中我们进行测量父元素 (黑色区域) 的clientWidth结果和子元素 (红色区域) 的clientWidth的大小是一样的.

不过需要注意的是, 一旦出现了滚动条对于 clientWidth 来说就意味着宽度减小(高度同理).

注意: 图片所指的宽度是clientWidth

API 名称 是否包含滚动条大小
offsetXXX 包含
clientXXX 不包含
scrollXXX 不包含

所以在 margin:0;padding:0;border-width:0; 情况下offsetWidth - clientWidth= 滚动条的宽度.

通过这种方式我求出了 chrome 浏览器滚动条大小是 17px 整, 但是不要忘记这些 API 只会返回整数.

注意 :scrollXXX 对于滚动条计算的规则和 clientXXX 表现一致.

含有 box-sizing:border-box 的计算

请记住, 对于 clientXXX 来说,元素的大小就是 padding+content.

而使用 border-box 后元素的表现就是padding 和 border 的修改就不会影响到元素的大小.

此时 width 是多少 clientWidth 就是多少,height 同理.

但是不要忘记了边框不参与 clientXXX 的计算, 所以 border 的修改并不会影响元素的宽高变化, 那么 那么当 border 变大, 对应的 clientXXX 就变小.

一个元素设置了border-box:

box{
  width:100px;
  padding:20px;
  border:20px solid;
  box-sizing:border-box;
}

此时clientWidth= 100px – 20px*2(左右边框的宽度) = 60px

由于 offsetXXX 的计算是包含 border 的大小的, 所以如果一个元素设置了 border-box 那么 offsetWidth 就等于元素的 width 大小, 因为 border 被限制到了 width 中.

offsetTopoffsetLeft

子元素的 offsetWidth|height 是相对于父元素内容区域 (padding+content) 左侧和顶部的偏移量.

这个两个是相对值, 是要求出向对于父级使用定位情况下来进行计算的, 这个父元素可以通过 HTMLElement.offsetParent 来获取到.

例如 : 父级使用absolute 或者relative.

注意:后文提出的父元素都指的是使用了相对定位的父元素.

注意: 以上都是对于块级元素所描述的, 对于行内元素或者 td 等元素相对的父元素不尽相同, 这里不考虑这些情况, 详情可以查看上方的链接.

情况 1

在子元素使用了绝对定位的情况下, 父元素无法干预子元素, 所以子元素的 scrollLeft 就是left+margin-left.

情况 2

第二种情况就是父元素和子元素都使用了相对定位, 而相对定位是不脱离文档流的, 那么父元素的 padding-left 就会影响到子元素的 scrollLeft 属性.

在线实例

注意 : 貌似offsetTopLeft在不同浏览器下有不同计算值, 会带来兼容性问题, 这里就不展开了, 有兴趣的读者可以去查阅相关资料.

scrollTopscrollLeft

首先 scrollTop 和 scrollLeft 是一对可读写的属性, 这意味着你可以获取他的值也可以设置它从而控制滚动的距离.

注意 :scrollTop|scrollLeft 是用在含有滚动区域的元素上 (图中黑色边框的元素), 而不是被滚动的元素上测量, 被滚动的元素scrollTop 永远是 0.

简单理解 : 在垂直方向上含有滚动条的元素的内容区域的顶部(padding+content) 相对于自身顶部边框的底部向上移动的距离(水平方向同理).

就是 scrollTop 的大小(图中超出去的部分).

元素方法

getBoundingClientRect

关于这个方法建议阅读 MDN 的指南. 当然你也可以选择听我瞎扯几句.

这个 api 是 ie 首先提出 (早在 ie4 时候就有了) 的这也是 ie 对 web 开发的贡献之一.

调用这个 api 会返回一个 DOMRect 对象, 这个对象多次易名, 不过没有变化过基本概念.

当你调用这个方法的时候他会返回一个对象, 该对象拥有如下几个属性:

  • left
  • top
  • right
  • bottom
  • width
  • height
  • x
  • y

注意:ie9+ 包括 edge 兼容 width 和 height 截止到 2019 年 4 月 5 日 ie 和 edge 不兼容 x 和 y.

和属性返回的值有两点不同:

  • 返回的值都是相对值, 相对于浏览器视口的左上角
  • 返回的值包含小数部分, 这意味着获取到的值更加精确

如何理解:

如果页面中只有一个 1000px 高 100% 宽的 div(没有 margin,padding,border), 那它的 bottom 和 height 应该 1000,left 和 top 是 0,right 和 width 是元素宽度:

  • 因为我们指定了 1000px 所以他的 height 是 1000,
    因为页面中只有这一个元素, 他的内容区域底部到可视区域顶部的距离就是 1000, 所以 bottom 是 1000.
  • 因为页面中只有这一个元素, 所以这个元素是贴着可视区域的顶端和左侧的, 所以 left 和 top 这两个值都是 0.
  • div 默认会铺满横向的空间, 横向空间中只有我们的 div 所以 right 到视口区域左侧的距离就是元素的宽度, 也是当前可视区域的宽度.

注意 :left,right,top,bottom 指的是内容区域的边缘到视口左上角的距离, 不包括 border 和 margin,padding, 如果指定了box-sizing:border-box; 则也包括 border 和 padding.

但是, 由于是该对象的值是相对值, 而视口是会移动的, 所以 top 和 left 的值在视口移动后会发生改变.

图解:

注意 : 该属性返回的值也将滚动条视为宽度(纵向) 和高度 (横向) 的一部分.

和 getBoundingClientRect 类似的还有另外一个 api 叫做 getClientRects. 这个 api 作用起来稍微复杂一些, 简单的作用于内联元素的时候两者有区别, 首先它返回的是数组, 包含这个内联元素所有行的 DOMRect 对象, 当内联元素只有一行的时候这两个 api 表现一样的.

思否上一篇关于 getClientRects 的讨论

https://segmentfault.com/q/10…

屏幕部分

这部分主要利用 window.screen 对象, 这个对象抽象的表示当前使用的物理显示设备.

没错这个属性是和硬件有关的, 一般的 Web 开发中很少会使用这个属性.

通过这个对象你可以获取到有关屏幕的分辨率等信息

这里就不多介绍了, 附上一篇介绍详细的文章:

https://www.cnblogs.com/ndos/…

此外在 windows 上还有几个关于屏幕的属性:

名称 描述
screenLeft 浏览器窗口左侧到屏幕左边缘的距离
screenTop 浏览器窗口顶部到屏幕上边缘的距离
screenX screenLeft
screenY screenTop

计算样式

HTMLelement.style

使用样式来获取元素的大小以及定位是一种常见的操作, 但是问题在于使用 HTMLelement.style 获取的内容:

  • 是带有 css 单位的字符串
  • 需要写在内联中才可以获取到

使用 style 获取元素的大小或者宽高无疑是方便的, 这里返回的值和 css 模型是一致的.

window.getComputedStyle

这个 API 和 HTMLelement.style 类型, 调用这个 API 返回 CSSStyleDeclaration 对象.

区别在于:

  • 只读
  • 可以使用 css 完整的名称来获取样式, 例如font-size
  • 不仅计算内联样式还包括样式表中的样式

注意 : 当元素的样式修改的时候对应的CSSStyleDeclaration 也会实时同步, 你不需要对一个元素进行连续获取.

滚动相关

属性

使用对象 API 名称 描述
window pageXoffset 视口相对于页面内容区域左侧的距离, 在现代浏览器上该值可能返回一个双精度浮点型
window pageYoffset 视口顶部于页面内容区域顶部的距离, 在现代浏览器上该值可能返回一个双精度浮点型
window scrollX 同 pageXoffset
window scrollY 同 pageYoffset

MDN 滚动参数兼容性参考

https://developer.mozilla.org…

方法

快速解释:

适用对象 API 名称 描述
window scrollBy 相对于当前的滚动位置进行滚动到指定的位置
window scrollByLines 相对于当前的滚动位置按照行数进行滚动(非标准)
window scrollByPages 相对于当前的滚动位置按照页数进行滚动(非标准)
window scroll 绝对滚动到某个位置上
window scrollTo 功能于 scroll 方法无异
window scrollMaxX 获取水平方向滚动距离的极限(非标准)
window scrollMaxY 获取垂直方向滚动距离的极限(非标准)
element scrollTo 用在元素上的 scrollTo 方法, 功能于 window 上的方法无异.
element scrollBy 用在元素上的 scrollBy 方法, 功能于 window 上的方法无异
element scroll 用在元素上的 scroll 方法, 功能于 window 上的方法无异.
退出移动版