若要移動元素,我們應該熟悉座標。
大多數 JavaScript 方法會處理兩個座標系統之一
- 相對於視窗 – 類似於
position:fixed
,從視窗頂端/左端計算。- 我們會將這些座標表示為
clientX/clientY
,這種名稱的理由將在我們研究事件屬性時變得清楚。
- 我們會將這些座標表示為
- 相對於文件 – 類似於文件根目錄中的
position:absolute
,從文件頂端/左端計算。- 我們會將它們表示為
pageX/pageY
。
- 我們會將它們表示為
當頁面捲動到最開始,使得視窗的左上角與文件左上角完全重合時,這些座標彼此相等。但文件移動後,元素的視窗相對座標會改變,因為元素在視窗中移動,而文件相對座標則保持不變。
在這個圖片中,我們取文件中的某一點,並展示它在捲動之前(左)和之後(右)的座標
當文件捲動時
pageY
– 文件相對座標保持不變,它從文件頂部(現在已捲動出去)開始計算。clientY
– 視窗相對座標確實改變了(箭頭變短了),因為同一點離視窗頂部更近了。
元素座標:getBoundingClientRect
方法 elem.getBoundingClientRect()
會傳回視窗座標,用於封裝 elem
的最小矩形,作為內建 DOMRect 類別的物件。
主要的 DOMRect
屬性
x/y
– 矩形原點相對於視窗的 X/Y 座標,width/height
– 矩形的寬度/高度(可以是負值)。
此外,還有衍生的屬性
top/bottom
– 矩形頂部/底部的 Y 座標,left/right
– 矩形左側/右側的 X 座標。
例如,按一下這個按鈕以查看其視窗座標
如果您捲動頁面並重複,您會注意到,隨著視窗相對按鈕位置的改變,其視窗座標(如果您垂直捲動,則為 y/top/bottom
)也會改變。
以下是 elem.getBoundingClientRect()
輸出的圖片
如您所見,x/y
和 width/height
完全描述了矩形。衍生的屬性可以輕鬆地從它們計算出來
left = x
top = y
right = x + width
bottom = y + height
請注意
- 座標可能是小數分數,例如
10.5
。這是正常的,瀏覽器內部在計算中使用分數。當設定為style.left/top
時,我們不必將它們四捨五入。 - 坐標可以是負值。例如,如果頁面捲動,使得
elem
現在位於視窗上方,則elem.getBoundingClientRect().top
為負值。
x/y
,為什麼會有 top/left
?在數學上,矩形由其起點 (x,y)
和方向向量 (width,height)
唯一定義。因此,額外的衍生屬性是為了方便。
技術上,width/height
可能為負值,這允許「定向」矩形,例如用於表示具有適當標記的開始和結束的滑鼠選取。
負 width/height
值表示矩形從其右下角開始,然後「向上」生長。
以下是一個具有負 width
和 height
的矩形(例如 width=-200
、height=-100
)
如你所見,在這種情況下,left/top
不等於 x/y
。
然而,在實務上,elem.getBoundingClientRect()
始終傳回正 width/height
,我們在此提到負 width/height
只是為了讓你了解為什麼這些看似重複的屬性實際上並非重複。
x/y
由於歷史原因,Internet Explorer 不支援 x/y
屬性。
因此,我們可以建立一個 polyfill(在 DomRect.prototype
中新增 getter),或僅使用 top/left
,因為對於正 width/height
,它們始終與 x/y
相同,特別是在 elem.getBoundingClientRect()
的結果中。
視窗相對坐標和 CSS position:fixed
之間有明顯的相似性。
但在 CSS 定位中,right
屬性表示距離右邊緣的距離,而 bottom
屬性表示距離底邊緣的距離。
如果我們只看上面的圖片,我們可以看到在 JavaScript 中並非如此。所有視窗坐標都是從左上角算起的,包括這些坐標。
elementFromPoint(x, y)
呼叫 document.elementFromPoint(x, y)
會傳回視窗坐標 (x, y)
中最巢狀的元素。
語法為
let elem = document.elementFromPoint(x, y);
例如,以下程式碼會高亮顯示並輸出目前位於視窗中央的元素的標籤
let centerX = document.documentElement.clientWidth / 2;
let centerY = document.documentElement.clientHeight / 2;
let elem = document.elementFromPoint(centerX, centerY);
elem.style.background = "red";
alert(elem.tagName);
由於它使用視窗坐標,因此元素可能會根據目前的捲動位置而有所不同。
elementFromPoint
會傳回 null
方法 document.elementFromPoint(x,y)
僅在 (x,y)
位於可見區域內時才有效。
如果任何坐標為負值或超過視窗寬度/高度,則會傳回 null
。
以下是一個我們若未檢查而可能發生的典型錯誤
let elem = document.elementFromPoint(x, y);
// if the coordinates happen to be out of the window, then elem = null
elem.style.background = ''; // Error!
使用「固定」定位
我們大多數時候需要座標才能定位某個東西。
若要在某個元素附近顯示某個東西,我們可以使用 `getBoundingClientRect` 取得其座標,然後將 CSS `position` 與 `left/top` (或 `right/bottom`) 合併使用。
例如,以下的函式 `createMessageUnder(elem, html)` 會在 `elem` 下方顯示訊息
let elem = document.getElementById("coords-show-mark");
function createMessageUnder(elem, html) {
// create message element
let message = document.createElement('div');
// better to use a css class for the style here
message.style.cssText = "position:fixed; color: red";
// assign coordinates, don't forget "px"!
let coords = elem.getBoundingClientRect();
message.style.left = coords.left + "px";
message.style.top = coords.bottom + "px";
message.innerHTML = html;
return message;
}
// Usage:
// add it for 5 seconds in the document
let message = createMessageUnder(elem, 'Hello, world!');
document.body.append(message);
setTimeout(() => message.remove(), 5000);
按一下按鈕來執行它
可以修改程式碼,讓訊息顯示在左側、右側、下方,套用 CSS 動畫讓它「淡入」等。這很簡單,因為我們有該元素的所有座標和大小。
但請注意一個重要的細節:當頁面捲動時,訊息會從按鈕流失。
原因很明顯:訊息元素依賴 `position:fixed`,因此當頁面捲動時,它會停留在視窗的同一個地方。
若要變更這一點,我們需要使用基於文件座標和 `position:absolute`。
文件座標
相對於文件的座標從文件的左上角開始,而不是視窗。
在 CSS 中,視窗座標對應於 `position:fixed`,而文件座標類似於頂部的 `position:absolute`。
我們可以使用 `position:absolute` 和 `top/left` 將某個東西放在文件的特定位置,讓它在頁面捲動時仍停留在該處。但我們首先需要正確的座標。
沒有標準的方法可以取得元素的文件座標。但撰寫它很簡單。
兩個座標系統由以下公式連接
pageY
=clientY
+ 文件捲動出的垂直部分的高度。pageX
=clientX
+ 文件捲動出的水平部分的寬度。
函式 `getCoords(elem)` 會從 `elem.getBoundingClientRect()` 取得視窗座標,並將目前的捲動量加到它們上面
// get document coordinates of the element
function getCoords(elem) {
let box = elem.getBoundingClientRect();
return {
top: box.top + window.pageYOffset,
right: box.right + window.pageXOffset,
bottom: box.bottom + window.pageYOffset,
left: box.left + window.pageXOffset
};
}
如果在上述範例中,我們將它與 `position:absolute` 一起使用,則訊息會在捲動時停留在元素附近。
修改後的 `createMessageUnder` 函式
function createMessageUnder(elem, html) {
let message = document.createElement('div');
message.style.cssText = "position:absolute; color: red";
let coords = getCoords(elem);
message.style.left = coords.left + "px";
message.style.top = coords.bottom + "px";
message.innerHTML = html;
return message;
}
摘要
頁面上的任何一點都有座標
- 相對於視窗 –
elem.getBoundingClientRect()
。 - 相對於文件 –
elem.getBoundingClientRect()
加上目前的頁面捲動。
視窗座標非常適合用於 position:fixed
,而文件座標則適合用於 position:absolute
。
這兩個座標系統各有優缺點;有時我們需要其中一個,就像 CSS position
absolute
和 fixed
一樣。
留言
<code>
標籤,若要插入多行,請將它們包覆在<pre>
標籤中,若要插入 10 行以上,請使用沙盒 (plnkr、jsbin、codepen…)