元素位置、浏览器窗口、滚动条的种种问题整理

1. 元素位置与大小

1.1 Element.client[xxx]

获取元素不含边框的宽高,以及上方与左方边框的宽度。

在每个 Element 元素身上都挂载这一个 client[xxx] 的属性,其表示的意义为:

  • Element.clientWidth/clientHeight 表示元素的 Padding 与 Content 的宽度(高度)相加,如果使用了 CSS 的 box-sizing 属性改变了盒模型,那么所计算的仍然是 Padding 与 Content 的宽度(高度),只不过 Content 的宽高会由于盒模型的变更而减小。
  • Element.clientLeft/clientTop 表示元素左(上)边框的宽度。如果元素的文本方向是从右向左(RTL, right-to-left),并且由于内容溢出导致左边出现了一个垂直滚动条,则该属性包括滚动条的宽度。

1.2 HTMLElement.offset[xxx]

获取元素包含边框(以及滚动条)的宽高,以及获取元素相对于父级定位元素的位置。

每一个 Element 元素神挡都挂载着一个 offset[xxx] 属性,其表示的意义为:

  • HTMLElement.offsetHeight/offsetWidth 是一个只读属性,它返回该元素的像素高度(宽度),高度包含该元素的垂直内边距、边框、以及滚动条宽度(Content + Padding + Border + ScrollBar),且是一个整数。

    TIPS: 在这里要补充一点,当水平方向出现滚动条时,Content 的宽度会被缩减去滚动条的宽度,以保证元素整体的宽度不改变。

  • HTMLElement.offsetTop/offsetLeft 为只读属性,它返回当前元素从 offsetParent 元素的顶部(左边)内边距到元素边框的距离。

    TIPS: Element.offsetParent 即为获取当前元素的父级定位元素。当某一元素的父级元素没有经过定位时,offsetParent 指向 <body></body>。特殊情况下,定位为 fixed 的元素的 offsetParent 为 null,其 offsetTop/offsetLeft 属性在此时代指元素距离窗口顶部(左边)的距离。

    同时在 magrin 穿透的情况下,由于穿透部分仍然属于父元素,因此 offsetTop 所计算的高度还包含了 margin 穿透的部分,我们可以在

1.3 Element.getBoundingClientRect()

获取元素相对于视口的位置信息。

Element.getBoundingClientRect() 返回的是一个 DOMReact 对象,在 Chrome 中,其值有以下几个属性:

  • x: 元素相对于浏览器视口左上角 x 轴的距离(IE/Safari 不支持);
  • y: 元素相对于浏览器视口左上角 y 轴的距离(IE/Safari 不支持);
  • left: 元素左边相对于浏览器视口左边的距离;
  • right: 元素右边相对于浏览器视口左边的距离;
  • top: 元素顶部相对于浏览器视口顶部的距离;
  • bottom: 元素底部相对于浏览器视口顶部的距离。

当计算边界矩形时,会考虑视口区域(或其他可滚动元素)内的滚动操作,也就是说,当滚动位置发生了改变,top和left属性值就会随之立即发生变化(因此,它们的值是相对于视口的,而不是绝对的)。如果你需要获得相对于整个网页左上角定位的属性值,那么只要给top、left属性值加上当前的滚动位置(通过window.scrollX和window.scrollY),这样就可以获取与当前的滚动位置无关的值。同时为了跨浏览器兼容,请使用 window.pageXOffset 和 window.pageYOffset 代替 window.scrollX 和 window.scrollY。

还有一个 Element.getClientRects() 的接口,区别是 getClientRects() 获取行级元素是,如果元素折行,会将其按行分割成多个 Rect 对象。

1.4 window.innerWidth/innerHeight

如果想要计算浏览器窗口的宽度或者高度,window 上拥有一个独有的属性 innerHeightinnerWidth,分别用来获取窗口的高度与宽度。

在开发者模式下,我们拖动浏览器视口变化时,视口右上角的数字显示的就是 window.innerHeightwindow.innerWidth 的数值:

同时对于移动设备来说,使用 innerWidth 也可以获取到当前移动设备视口的宽度(注意是实际像素而并非物理像素)。这里要提一点,如果我们没有设置 mata 标签,视口以默认的 980px 去渲染页面时,那么当前的 innerWidth 的宽度输出结果是 980。当我们设置了 mata 标签,并将视口宽度设置为设备宽度(content="width=device-width, initial-scale=1.0"),那么所得到的 innerWidth 才是当前设备的设备宽度。

设置了 mata 标签的 width

没有设置 mata 标签的 width

2. Scroll

2.1 window 上 scroll 相关的属性与方法

window.scroll / scrollTo / scrollBy

window.scroll() 方法与 window.scrollTo() 方法相同,都是将页面滚动到某一坐标,可以不实用 window 直接在全局调用:

1
scrollTo(0, 100)

同时部分浏览器是原生支持平滑滚动的,通过配置的方式,可以添加平滑滚动的参数(Firefox、Chrom、Opera 以及除了 Safari Mobile 之外的其他主流移动端浏览器):

1
2
3
4
5
window.scroll({
top: 100,
left: 100,
behavior: 'smooth'
});

window.scrollX / scrollY

获取窗口滚动的 X 轴的距离或者 Y 轴的距离。

同时 pageX[Y]Offset 属性是 scrollX[Y] 属性的别名,为了跨浏览器兼容性,请使用 window.pageXOffset 代替 window.scrollX。另外,旧版本的 IE(<9)两个属性都不支持,必须通过其他的非标准属性来解决此问题。完整的兼容性代码如下:

1
2
var x = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
var y = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;

2.2 Element 上 scroll 相关的属性与方法

Element.scrollTop

如果一个元素内存在滚动条,使用 Element.scrollTop 可以过去到该元素已滚动的距离,如果该元素没有滚动条,该值为 0。

与之对应的还有 Element.scrollLeft

Element.scrollHeight

Element.scrollHeight 这个只读属性是一个元素内容高度的度量,包括由于溢出导致的视图中不可见内容。scrollHeight 的值等于该元素在不使用滚动条的情况下为了适应视口中所用内容所需的最小高度。

与之对应的还有 Element.scroolWidth

3. 例题

3.1 实现可拖拽 Div

通过事件对象 e 获取到鼠标从点击开始到实时位置的偏移量,然后与元素的位置相累加,得出元素的实时位置。

https://codepen.io/EsunR/pen/JjoyWap

还可以使用 HTML5 的拖拽事件实现

3.2 判断元素是否在浏览器显示范围内

通过 Element.getBoundingClientRect().top 获取元素位置到窗口顶部的距离,又可以通过 window.innerHeight 获取到窗口的高度,通过以下两个判断即可获取到元素的位置状态:

  • 如果元素到顶部的距离小于窗口的高度,说明元素进入了浏览器窗口
  • 如果元素到顶部的距离加上元素本身的高度为负值,那么说明元素已经离开了窗口的可视范围

https://codepen.io/EsunR/pen/dyPzvxQ