目錄
client?家族與?offset?家族
一、client?家族:內容區域 + 內邊距
示例代碼
應用場景
二、offset?家族:內容區域 + 內邊距 + 邊框 + 滾動條
示例代碼
應用場景
三、綜合應用場景
1. 動態調整元素高度
2. 拖拽元素
3. 判斷元素是否在視口內
四、注意事項
五、總結
getBoundingClientRect()
一、DOMRect?對象屬性
二、關鍵特性
1.?相對視口坐標系
2.?包含邊框和內邊距
3.?兼容性與性能
三、與其他方法的對比
四、代碼示例
示例:電梯導航
client
?家族與?offset
?家族
在 JavaScript 中,元素的尺寸和位置信息可通過?client
?和?offset
?系列屬性獲取。這兩組屬性分別描述了元素不同維度的幾何特征,適用于動態布局、拖拽交互、滾動計算等場景。
一、client
?家族:內容區域 + 內邊距
屬性 | 說明 |
---|---|
clientWidth | 元素內容區域的寬度 + 左右內邊距(不包含滾動條、邊框、外邊距) |
clientHeight | 元素內容區域的高度 + 上下內邊距(不包含滾動條、邊框、外邊距) |
clientTop | 元素上邊框的寬度(等同于?border-top-width ) |
clientLeft | 元素左邊框的寬度(等同于?border-left-width ) |
示例代碼
const box = document.querySelector('.box');
console.log('內容寬度:', box.clientWidth); // 內容 + padding
console.log('上邊框寬度:', box.clientTop); // 上邊框寬度
應用場景
-
計算元素內部可用空間(如文本容器)。
-
動態調整內容區域大小(結合滾動條狀態)。
二、offset
?家族:內容區域 + 內邊距 + 邊框 + 滾動條
屬性 | 說明 |
---|---|
offsetWidth | 元素總寬度(內容 + 內邊距 + 邊框 + 垂直滾動條寬度,若存在) |
offsetHeight | 元素總高度(內容 + 內邊距 + 邊框 + 水平滾動條高度,若存在) |
offsetTop | 元素上外邊框距離最近定位父元素(或文檔)上內邊框的垂直距離 |
offsetLeft | 元素左外邊框距離最近定位父元素(或文檔)左內邊框的水平距離 |
offsetParent | 元素的最近定位祖先元素(position ?不為?static ,若沒有則指向?body ) |
示例代碼
const box = document.querySelector('.box');
console.log('元素總寬度:', box.offsetWidth); // 內容 + padding + border + 滾動條
console.log('距離父元素頂部:', box.offsetTop);
應用場景
-
獲取元素的完整尺寸(包含邊框和滾動條)。
-
計算元素在文檔中的絕對位置(結合?
offsetParent
)。
三、綜合應用場景
1. 動態調整元素高度
// 根據窗口高度調整容器高度
function adjustHeight() {const viewportHeight = window.innerHeight;const headerHeight = document.querySelector('header').offsetHeight;const container = document.querySelector('.container');container.style.height = `${viewportHeight - headerHeight}px`;
}
window.addEventListener('resize', adjustHeight);
2. 拖拽元素
let isDragging = false;
let startX, startY, initialX, initialY;document.querySelector('.draggable').addEventListener('mousedown', (e) => {isDragging = true;const rect = e.target.getBoundingClientRect();initialX = rect.left; // 元素當前文檔位置initialY = rect.top;startX = e.clientX; // 鼠標按下位置startY = e.clientY;
});document.addEventListener('mousemove', (e) => {if (!isDragging) return;const deltaX = e.clientX - startX;const deltaY = e.clientY - startY;e.target.style.left = `${initialX + deltaX}px`;e.target.style.top = `${initialY + deltaY}px`;
});document.addEventListener('mouseup', () => {isDragging = false;
});
3. 判斷元素是否在視口內
function isElementInViewport(el) {const rect = el.getBoundingClientRect();return (rect.top >= 0 &&rect.left >= 0 &&rect.bottom <= window.innerHeight &&rect.right <= window.innerWidth);
}
四、注意事項
-
布局抖動:頻繁讀取尺寸屬性(如?
offsetWidth
)會強制瀏覽器重排,應盡量減少讀取次數。 -
隱藏元素:若元素為?
display: none
,其尺寸屬性值為?0
。 -
小數精度:部分瀏覽器返回的尺寸值可能是小數,需按需取整。
-
邊框與滾動條:滾動條的存在會影響?
clientWidth
?和?offsetWidth
?的值。
五、總結
-
client
?系列:關注元素內容與內邊距,適合內部空間計算。 -
offset
?系列:包含邊框和滾動條,用于獲取元素整體占位或定位。 -
scroll
?系列:處理內容滾動與溢出部分。 -
組合使用:結合?
getBoundingClientRect()
?或?offsetParent
?實現精準布局控制。
??
getBoundingClientRect()
Element.getBoundingClientRect()
?是 JavaScript 中用于獲取元素精確尺寸和位置的核心方法。它返回一個?DOMRect
?對象,包含元素相對于瀏覽器視口(viewport)的幾何信息,適用于動態布局計算、滾動檢測、交互定位等場景。
一、DOMRect
?對象屬性
調用?getBoundingClientRect()
?返回的對象包含以下屬性(單位:像素):
屬性 | 說明 |
---|---|
x /left | 元素左邊界到視口左側的距離(x ?是?left ?的別名,兼容性稍差) |
y /top | 元素上邊界到視口頂部的距離(y ?是?top ?的別名,兼容性稍差) |
right | 元素右邊界到視口左側的距離(right = left + width ) |
bottom | 元素下邊界到視口頂部的距離(bottom = top + height ) |
width | 元素的寬度(包含?padding ?+?border ,不包含?margin ) |
height | 元素的高度(包含?padding ?+?border ,不包含?margin ) |
二、關鍵特性
1.?相對視口坐標系
-
所有值基于視口左上角計算,頁面滾動會改變結果(視口位置動態變化)。
-
轉換為文檔坐標(絕對位置):
const rect = element.getBoundingClientRect(); const absoluteLeft = rect.left + window.scrollX; const absoluteTop = rect.top + window.scrollY;
2.?包含邊框和內邊距
-
width
?和?height
?包含元素的?padding
?和?border
,與?offsetWidth
/offsetHeight
?一致。 -
對比其他尺寸屬性:
屬性 包含內容 clientWidth
內容 + padding(不含邊框/滾動條) offsetWidth
內容 + padding + border scrollWidth
實際內容寬度(含溢出部分)
3.?兼容性與性能
-
兼容性:所有現代瀏覽器均支持,包括 IE9+。
-
性能:頻繁調用可能觸發重排(reflow),建議緩存結果或使用優化策略(如防抖)。
三、與其他方法的對比
方法 | 特點 |
---|---|
offsetTop /offsetLeft | 返回相對于最近定位父元素的偏移,不提供完整尺寸信息 |
clientWidth /clientHeight | 僅包含內容 + padding,無位置信息 |
scrollIntoView() | 滾動元素到視口,但無法直接獲取位置數據 |
四、代碼示例
const element = document.getElementById('target');
const rect = element.getBoundingClientRect();console.log('元素尺寸:', rect.width, 'x', rect.height);
console.log('視口內位置:', rect.left, rect.top);
console.log('文檔絕對位置:', rect.left + window.scrollX, rect.top + window.scrollY);
示例:電梯導航
<!-- 電梯 --><div class="xtx-elevator"><ul class="xtx-elevator-list"><li><a href="javascript:;" data-name="new">新鮮好物</a></li><li><a href="javascript:;" data-name="popular">人氣推薦</a></li><li><a href="javascript:;" data-name="brand">熱門品牌</a></li><li><a href="javascript:;" data-name="topic">最新專題</a></li><li><a href="javascript:;" data-name="backTop"><i class="sprites"></i>頂部</a></li></ul></div><script>//導航隱藏與顯示const elevator = document.querySelector('.xtx-elevator')const entry = document.querySelector('.xtx_entry')window.addEventListener('scroll', function () {let scrolltop = document.documentElement.scrollTopelevator.style.opacity = scrolltop > entry.offsetTop ? 1 : 0})function clear() {if (document.querySelector('.xtx-elevator .active'))document.querySelector('.xtx-elevator .active').classList.remove('active')}//點擊導航跳轉const ul = document.querySelector('.xtx-elevator-list')ul.addEventListener('click', function (e) {clear()e.target.classList.add('active')if (e.target.dataset.name === 'backTop') {document.documentElement.scrollTop = 0}else {const distance = document.querySelector(`.xtx_goods_${e.target.dataset.name}`).offsetTopdocument.documentElement.scrollTop = distance}})//頁面滾動更新導航const newsTop = Math.floor(document.querySelector('.xtx_goods_new').offsetTop)const popularTop = Math.floor(document.querySelector('.xtx_goods_popular').offsetTop)const brandTop = Math.floor(document.querySelector('.xtx_goods_brand').offsetTop)const topicTop = Math.floor(document.querySelector('.xtx_goods_topic').offsetTop)window.addEventListener('scroll', function () {let scrolltop = document.documentElement.scrollTopclear()if (scrolltop >= newsTop && scrolltop < popularTop) {document.querySelector('[data-name = new]').classList.add('active')}else if (scrolltop >= popularTop && scrolltop < brandTop) {document.querySelector('[data-name = popular]').classList.add('active')}else if (scrolltop >= brandTop && scrolltop < topicTop) {document.querySelector('[data-name = brand]').classList.add('active')}else if (scrolltop >= topicTop) {document.querySelector('[data-name = topic]').classList.add('active')}})</script>