前言:
在業務當中,我們經常要計算元素的大小和元素在頁面的位置資訊。比如說,在一個滾動區域内,我要知道元素A是在可視區内,還是在隐藏内容區(滾動到外邊看不到了)。有時還要進一步知道,元素是全部都顯示在可視區,還是有部分在可視區部分在隐藏内容區。有時還要進一步知道,在隐藏内容區的那一部分是占多大的大小。so,來聊聊如何擷取元素的大小和位置資訊。
計算元素距離頂部的高度:偏移量
在二維的世界裡,可以想象成一個二維坐标系。每一個元素在坐标系内都有兩個基本的屬性:大小和位置。
大小:
dom元素在頁面的大小有兩個屬性:offsetWidth 、offsetHeight,
offsetHeight:元素在垂直方向上的占用空間大小,以像素計。包括元素的高度、(可見的)水準滾動條的高度、上邊框高度和下邊框高度。(我的了解:盒模型包括border内的高度總和)
offsetWidth: (同理)
位置:
offsetParent 是一個隻讀屬性,傳回一個指向最近的(closest,指包含層級上的最近)包含該元素的定位元素。如果沒有定位的元素,則 offsetParent 為最近的 table, table cell 或根元素
offsetTop:元素相對于其 offsetParent 元素的頂部的距離
offsetLeft:(同理)

元素大小和位置資訊圖解
原理:
計算元素距離頂部的高度:将元素的offsetTop與其offsetParent的相同屬性相加,如此循環直至根元素,就可以得到一個基本準确的值。封裝如下函數(開箱即用,函數傳回值即為元素距離頂部高度)
function getElementTop (el) {
var actualTop = el.offsetTop
var current = el.offsetParent
while (current !== null) {
actualTop += current.offsetTop
current = current.offsetParent
}
return actuanlTop
}
分析一下代碼:
計算元素距離左部的高度:(同理可得)
元素是否在可視區判斷:結合scrollTop
假如這樣的業務場景:有一個wrap滾動容器,wrap的内容區content的高度超過wrap的高度,則出現縱向滾動條。随意拖動滾動條到某個位置,要判斷content裡面的子元素input輸入框是否在可視區内,若不在可視區内,自動拖動滾動條,使其進入可視區。這樣的業務場景其實經常有遇到。
首先,根據以上getElementTop(domInput)函數,得到元素input距離頂部的高度elementTop。再結合滾動容器domWrap的scrollTop屬性得到滾動條高度scrollTop(被隐藏在内容區域上方的像素數),進行比較,即可判斷
scrollTop > elementTop: 滾動條高度大于元素離頂部高度,說明元素進入了隐藏内容區,進入的量為 scrollTop - elementTop
scrollTop < elementTop:滾動條高度小于元素的離頂部高度,說明元素還沒進入上方的隐藏内容區,如要保證元素在可視區内,則必須同時滿足條件,元素不在下方的隐藏内容區: elementTop - scrollTop < document.documentElemnt.clientHeight
結論:
是以,元素在可視區的初步判斷條件為:scrollTop < elementTop && elementTop - scrollTop < document.documentElemnt.clientHeight
以上判斷還不太嚴謹,如果wrap還同時存在橫向滾動條,還得再判斷是否元素在橫向的可視區内,如果要判斷元素是否完全在可視區,還得加上自身的高度值,即為:scrollTop < elementTop && elementTop + input.offsetHeight - scrollTop < document.documentElemnt.clientHeight。
通過設定scrollTop屬性的值domWrap.scrollTop = elementTop 可以讓滾動條自動滾動,且input元素剛好在可視區的上方。
ps:書上是這麼說的,對于簡單的CSS布局的頁面,getElementTop函數可以得到非常精确的結果。對于使用表格和内嵌架構布局的頁面,由于不同浏覽器實作這些元素的方式不同,是以得到的結果就不太精确。在我的業務中,我沒有用到表格和内嵌架構的布局,計算結果确實是精确的,可放心使用。如果使用表格和内嵌架構布局的頁面,Keeping a eye on it。
參考文獻: Javascript進階程式設計(第三版) MDN web docs
Q&A:有疑問和指正的地方歡迎在評論區留言