JS中獲取 DOM 元素的絕對位置實例詳解

原文:https://www.jb51.net/article/138770.htm

這篇文章主要介紹了JS中獲取 DOM 元素的絕對位置,詳細介紹了各種獲取dom元素絕對位置的方法及對應的兼容性,需要的朋友參考下吧

在操作頁面滾動和動畫時經常會獲取 DOM 元素的絕對位置,例如 本文 左側的懸浮導航,當頁面滾動到它以前會正常地渲染到文檔流中,當頁面滾動超過了它的位置,就會始終懸浮在左側。

本文會詳述各種獲取 DOM 元素絕對位置 的方法以及對應的兼容性。關於如何獲取 DOM 元素高度和滾動高度,請參考視口的寬高與滾動高度 一文。

概述

這些是本文涉及的 API 對應的文檔和標準,供查閱:

 

API 用途 文檔 標準
offsetTop 相對定位容器的位置 MDN CSSOM View Module
clientTop 上邊框寬度 MDN CSSOM View Module
.getBoundingClientRect() 元素大小和相對視口的位置 MDN CSSOM View Module
.getClientRects() 所有子 CSS 盒子的大小和位置 MDN CSSOM View Module
.getComputedStyle() 應用所有樣式表和計算之後的 CSS 屬性 MDN DOM Level 2 Style CSSOM

 

offsetTop/offsetLeft

HTMLElement.offsetTop 用來獲取當前元素(不包括上邊框)相對於定位容器(positioning container)的位置。也就是說,

如果所有祖先元素都是靜態定位 position:static;(這是默認的情況),offsetTop 表示與文檔最上方的高度差(文檔最上方可能已經滾出視口,這個高度可能大於視口高度)。

如果存在絕對定位的祖先元素 position:absolute/fixedoffsetTop 就會相對於這個元素。因此爲了獲取相對於文檔最上方的高度差,需要遞歸地調用:

function getOffsetTop(el){
 return el.offsetParent
  ? el.offsetTop + getOffsetTop(el.offsetParent)
  : el.offsetTop
}

el.offsetParent 是當前元素的定位容器(positioning container),如果當前元素沒有絕對定位的祖先節點,這個屬性的值就是 null

兼容性和限制:幾乎所有瀏覽器都支持該屬性。如果元素被隱藏它的值就是 0,但在 IE9 下沒有影響。

clientTop/clientLeft

不要被名字誤導,Element.clientTop 是指當前元素的 上邊框的寬度 的整數值。總是等於 getComputedStyle() 返回的 border-top-width 屬性的四捨五入爲整數後的值。

爲什麼呢?在 DOM 術語中,client 總是指除邊框(border)外的渲染盒子(內邊距+內容大小)。offset 總是指包含邊框的渲染盒子(邊框+內邊距+內容大小),clientTop 即爲這兩者的 Top 之差,即邊框寬度。盒子的概念請參考:CSS Display 屬性與盒模型

兼容性和限制:同 offsetTop/offsetLeft

.getBoundingClientRect()

Element.getBoundingClientRect() 用於獲取元素的大小,以及相對於視口(viewport)的位置,返回一個 DOMRect 對象。

> document.querySelector('span').getBoundingClientRect()
DOMRect {x: 2.890625, y: 218.890625, width: 1264, height: 110, top: 218.890625, …}
bottom: 328.890625
height: 110
left: 2.890625
right: 1266.890625
top: 218.890625
width: 1264
x: 2.890625
y: 218.890625

如果要獲取相對於文檔左上角的位置,需要在上述 top 和 left 的基礎上再加滾動位置。如下代碼來自 MDN,可兼容幾乎所有瀏覽器:

// For scrollX
(((t = document.documentElement) || (t = document.body.parentNode))
 && typeof t.scrollLeft == 'number' ? t : document.body).scrollLeft
// For scrollY
(((t = document.documentElement) || (t = document.body.parentNode))
 && typeof t.scrollTop == 'number' ? t : document.body).scrollTop

兼容性和限制:同樣是 CSSOM View Module 的特性,但幾乎兼容所有瀏覽器,可參考

https://caniuse.com/#feat=getboundingclientrectIE 下窗口的左上角可能不是 0,0,在 IE9 可以這樣把它設置爲 0,0:

<meta http-equiv="x-ua-compatible" content="ie=edge"/>

.getClientRects()

Element.getClientRects() 用來獲得 DOM 元素中的所有CSS 盒模型 對應的 DOMRect 組成的集合。

如果是一個塊級元素,返回的集合中應該只有一個元素,即這個塊的大小和位置。但如果是一個行內元素(或者 SVG 內的元素),則會返回其中每個 CSS 盒子。比如一個普通的被默認折行的 <span>

> document.querySelector('span').getClientRects()
DOMRectList {0: DOMRect, 1: DOMRect, 2: DOMRect, length: 5}
0: DOMRect {x: 2.890625, y: 262.890625, width: 1264, height: 22, top: 262.890625, …}
1: DOMRect {x: 2.890625, y: 284.890625, width: 1264, height: 22, top: 284.890625, …}
2: DOMRect {x: 2.890625, y: 306.890625, width: 768, height: 22, top: 306.890625, …}

這個 <span> 有三行,其中第三行的長度不足一行,每次折行都形成了一個新的 CSS 盒子。

兼容性和限制:在 IE8 及以下會返回 IE 獨有的 TextRectangle 對象(而不是 ClientRect),這個對象不具有 width 和 height 屬性,而且無法給它設置屬性。參考:https://webplatform.github.io/docs/dom/HTMLElement/getClientRects/

.getComputedStyle()

Window.getComputedStyle() 可以得到一個元素的所有計算後的 CSS 屬性。對於簡單的絕對定位元素,可以通過這個 API 返回的 topleft 等屬性值獲取元素的位置。例如:

let btn = document.querySelector('#btn-scroll-up')
let {top, left} = getComputedStyle(btn)
console.log('top:', top, 'left:', left)

.getComputedStyle() 還有一個有用的用法,獲取僞元素的大小和位置等樣式信息:

// 以下代碼來自: https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
var h3 = document.querySelector('h3'); 
var result = getComputedStyle(h3, ':after').content;
console.log('the generated content is: ', result); // returns ' rocks!'

兼容性和限制:.getComputedStyle() 幾乎兼容所有瀏覽器,可參考 https://caniuse.com/#search=getComputedStyle。但它返回的值是 CSS 屬性,用它獲取絕對位置時要注意值的類型。例如 left 可能是 13px 這樣的絕對值,也可能是 auto 這樣的 CSS 關鍵字。

總結 獲取 DOM 元素相對於文檔的位置,可以直接使用 offsetTop; 獲取 DOM 元素相對於視口的位置,可以使用 .getBoundingClientRect(); 獲取 SVG 元素或行內元素的 CSS 盒子(比如用來做文本高亮時),可以使用 .getClientRects(); 獲取絕對定位元素、僞元素的渲染後 CSS 屬性,可以使用 .getComputedStyle()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章