瀏覽器 API 公開數種座標系統,因為螢幕、瀏覽器視窗、視埠、文件與元素回答不同的測量問題。因此,僅依名稱選擇 API 可能造成捲軸、捨入或捲動錯誤。
視窗、螢幕與視埠測量 #
| API | 測量 |
|---|---|
window.innerWidth、innerHeight | 版面配置視埠,包括已繪製的傳統捲軸 |
window.outerWidth、outerHeight | 瀏覽器視窗的外部尺寸 |
screen.width、screen.height | 以 CSS 像素表示的螢幕尺寸 |
screen.availWidth、screen.availHeight | 排除保留作業系統區域的螢幕空間 |
document.documentElement.clientWidth、clientHeight | 排除傳統捲軸的版面配置視埠 |
CSS 像素是邏輯單位。在高密度顯示器上,一個 CSS 像素可能涵蓋多個裝置像素,因此 screen.width 不一定等於面板的實體像素數。
outerWidth 與 outerHeight 包含瀏覽器外框。它們與內部尺寸的差異取決於視窗裝飾、分頁、工具列與顯示縮放;因此不應用來驅動頁面版面配置。
const viewport = {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
};當計算必須排除傳統捲軸時,請使用根元素的 client 尺寸。覆蓋式捲軸不會消耗版面寬度,因此 innerWidth 與 clientWidth 的差異取決於平台。
版面配置與視覺視埠 #
版面配置視埠建立頁面版面配置使用的包含區塊。視覺視埠是在縮放手勢或螢幕鍵盤改變未遮蔽區域後,目前可見的部分。在支援的瀏覽器上,window.visualViewport 會公開 width、height、scale、offsetLeft 與 offsetTop:
const viewport = window.visualViewport;
viewport?.addEventListener("resize", positionOverlay);
viewport?.addEventListener("scroll", positionOverlay);對於必須跟隨可見區域的覆蓋層,請使用這些事件。一般文件版面配置應繼續使用 CSS 與版面配置視埠;因應視覺視埠事件移動整個頁面可能與瀏覽器捲動衝突。
元素盒測量 #
| API | 包含區域 | 精確度 |
|---|---|---|
clientWidth、clientHeight | 內容加內距,不含邊框與捲軸 | 整數 |
scrollWidth、scrollHeight | 不捲動時所需的最小內容加內距範圍 | 整數 |
offsetWidth、offsetHeight | 邊框盒,包括已繪製的捲軸 | 整數 |
getBoundingClientRect() | 視埠座標中的邊框盒幾何資訊 | 分數 |
不建立 CSS 盒的行內元素,其 clientWidth 與 clientHeight 為零。對根元素而言,它們表示視埠。歷史上的 quirks mode 可能使 body 元素參與視埠測量,但 standards mode 文件應使用 document.documentElement。
可捲動溢位會使 scrollHeight 或 scrollWidth 超過相符的 client 尺寸:
function hasVerticalOverflow(element) {
return element.scrollHeight > element.clientHeight;
}偽元素內容可能貢獻於捲動尺寸。整數 API 會捨入子像素版面配置,因此分數幾何資訊重要時請使用 getBoundingClientRect()。
座標系統與捲動 #
getBoundingClientRect() 回傳相對於視埠的座標:
const rect = element.getBoundingClientRect();
console.log(rect.left, rect.top, rect.width, rect.height);將視埠座標轉換為文件座標時,請加上目前的捲動偏移:
const documentTop = rect.top + window.scrollY;
const documentLeft = rect.left + window.scrollX;轉換會影響回傳的矩形。結果是包含轉換後邊框盒的最小軸對齊視埠矩形,而不是元素未轉換的版面配置尺寸。
在正確時間測量 #
字型、影像、樣式表或應用程式資料載入後,幾何資訊可能改變。影響版面配置的相依項可用後再測量。程式碼必須回應後續尺寸變更時,請使用 ResizeObserver:
const observer = new ResizeObserver(([entry]) => {
const { inlineSize, blockSize } = entry.contentBoxSize[0];
console.log({ inlineSize, blockSize });
});
observer.observe(element);讀取幾何資訊可能迫使瀏覽器完成待處理的樣式與版面配置工作。在動畫程式碼中,請先批次讀取,再批次寫入,因為交替進行版面配置讀取與樣式變更可能造成重複的同步版面配置。
requestAnimationFrame 會選取繪製階段,但不會使版面配置讀取變得免費。先讀取所有需要的幾何資訊,計算下一個狀態,再以另一個批次套用樣式或 DOM 寫入。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)