W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
要移動頁面的元素,我們應(yīng)該先熟悉坐標(biāo)。
大多數(shù) JavaScript 方法處理的是以下兩種坐標(biāo)系中的一個:
position:fixed
,從窗口的頂部/左側(cè)邊緣計算得出。clientX/clientY
?,當(dāng)我們研究事件屬性時,就會明白為什么使用這種名稱來表示坐標(biāo)。position:absolute
類似,從文檔的頂部/左側(cè)邊緣計算得出。pageX/pageY
?。當(dāng)頁面滾動到最開始時,此時窗口的左上角恰好是文檔的左上角,它們的坐標(biāo)彼此相等。但是,在文檔移動之后,元素的窗口相對坐標(biāo)會發(fā)生變化,因為元素在窗口中移動,而元素在文檔中的相對坐標(biāo)保持不變。
在下圖中,我們在文檔中取一點,并演示了它滾動之前(左)和之后(右)的坐標(biāo):
當(dāng)文檔滾動了:
pageY
? —— 元素在文檔中的相對坐標(biāo)保持不變,從文檔頂部(現(xiàn)在已滾動出去)開始計算。clientY
? —— 窗口相對坐標(biāo)確實發(fā)生了變化(箭頭變短了),因為同一個點越來越靠近窗口頂部。方法 elem.getBoundingClientRect()
返回最小矩形的窗口坐標(biāo),該矩形將 elem
作為內(nèi)建 DOMRect 類的對象。
主要的 DOMRect
屬性:
x/y
? —— 矩形原點相對于窗口的 X/Y 坐標(biāo),width/height
? —— 矩形的 width/height(可以為負)。此外,還有派生(derived)屬性:
top/bottom
? —— 頂部/底部矩形邊緣的 Y 坐標(biāo),left/right
? —— 左/右矩形邊緣的 X 坐標(biāo)。如果你滾動此頁面并重復(fù)點擊上面那個按鈕,你會發(fā)現(xiàn)隨著窗口相對按鈕位置的改變,其窗口坐標(biāo)(如果你垂直滾動頁面,則為 y/top/bottom
)也隨之改變。
下面這張是 elem.getBoundingClientRect()
的輸出的示意圖:
正如你所看到的,x/y
和 width/height
對矩形進行了完整的描述??梢院苋菀椎貜乃鼈冇嬎愠雠缮╠erived)屬性:
left = x
?top = y
?right = x + width
?bottom = y + height
?請注意:
10.5
?。這是正常的,瀏覽器內(nèi)部使用小數(shù)進行計算。在設(shè)置 ?style.left/top
? 時,我們不是必須對它們進行舍入。elem
? 現(xiàn)在位于窗口的上方,則 ?elem.getBoundingClientRect().top
? 為負數(shù)。為什么需要派生(derived)屬性?如果有了 ?
x/y
?,為什么還要還會存在 ?top/left
??從數(shù)學(xué)上講,一個矩形是使用其起點
(x,y)
和方向向量(width,height)
唯一定義的。因此,其它派生屬性是為了方便起見。
從技術(shù)上講,
width/height
可能為負數(shù),從而允許“定向(directed)”矩形,例如代表帶有正確標(biāo)記的開始和結(jié)束的鼠標(biāo)選擇。
負的
width/height
值表示矩形從其右下角開始,然后向左上方“增長”。
這是一個矩形,其
width
和height
均為負數(shù)(例如width=-200
,height=-100
):
![]()
正如你所看到的,在這個例子中,
left/top
與x/y
不相等。
但是實際上,
elem.getBoundingClientRect()
總是返回正數(shù)的 width/height,這里我們提及負的width/height
只是為了幫助你理解,為什么這些看起來重復(fù)的屬性,實際上并不是重復(fù)的。
IE 瀏覽器不支持 ?
x/y
?由于歷史原因,IE 瀏覽器不支持
x/y
屬性。
因此,我們可以寫一個 polyfill(在
DomRect.prototype
中添加一個 getter),或者僅使用top/left
,因為對于正值的width/height
來說,它們和x/y
一直是一樣的,尤其是對于elem.getBoundingClientRect()
的結(jié)果。
坐標(biāo)的 right/bottom 與 CSS position 屬性不同
相對于窗口(window)的坐標(biāo)和 CSS
position:fixed
之間有明顯的相似之處。
但是在 CSS 定位中,
right
屬性表示距右邊緣的距離,而bottom
屬性表示距下邊緣的距離。
如果我們再看一下上面的圖片,我們可以看到在 JavaScript 中并非如此。窗口的所有坐標(biāo)都從左上角開始計數(shù),包括這些坐標(biāo)。
對 document.elementFromPoint(x, y)
的調(diào)用會返回在窗口坐標(biāo) (x, y)
處嵌套最多(the most nested)的元素。
語法如下:
let elem = document.elementFromPoint(x, y);
例如,下面的代碼會高亮顯示并輸出現(xiàn)在位于窗口中間的元素的標(biāo)簽:
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);
因為它使用的是窗口坐標(biāo),所以元素可能會因當(dāng)前滾動位置而有所不同。
對于在窗口之外的坐標(biāo),?
elementFromPoint
? 返回 ?null
?方法
document.elementFromPoint(x,y)
只對在可見區(qū)域內(nèi)的坐標(biāo)(x,y)
起作用。
如果任何坐標(biāo)為負或者超過了窗口的 width/height,那么該方法就會返回
null
。
在大多數(shù)情況下,這種行為并不是一個問題,但是我們應(yīng)該記住這一點。
如果我們沒有對其進行檢查,可能就會發(fā)生下面這個典型的錯誤:
let elem = document.elementFromPoint(x, y); // 如果坐標(biāo)恰好在窗口外,則 elem = null elem.style.background = ''; // Error!
大多數(shù)時候,我們需要使用坐標(biāo)來確定某些內(nèi)容的位置。
想要在某元素附近展示內(nèi)容,我們可以使用 getBoundingClientRect
來獲取這個元素的坐標(biāo),然后使用 CSS position
以及 left/top
(或 right/bottom
)。
例如,下面的函數(shù) createMessageUnder(elem, html)
在 elem
下顯示了消息:
let elem = document.getElementById("coords-show-mark");
function createMessageUnder(elem, html) {
// 創(chuàng)建 message 元素
let message = document.createElement('div');
// 在這里最好使用 CSS class 來定義樣式
message.style.cssText = "position:fixed; color: red";
// 分配坐標(biāo),不要忘記 "px"!
let coords = elem.getBoundingClientRect();
message.style.left = coords.left + "px";
message.style.top = coords.bottom + "px";
message.innerHTML = html;
return message;
}
// 用法:
// 在文檔中添加 message 保持 5 秒
let message = createMessageUnder(elem, 'Hello, world!');
document.body.append(message);
setTimeout(() => message.remove(), 5000);
我們可以修改代碼以在元素左側(cè),右側(cè)或下面顯示消息,也可以應(yīng)用 CSS 動畫來營造“淡入淡出”效果等。這很簡單,因為我們有該元素所有坐標(biāo)和大小。
但是請注意一個重要的細節(jié):滾動頁面時,消息就會從按鈕流出。
原因很顯然:message 元素依賴于 position:fixed
,因此當(dāng)頁面滾動時,它仍位于窗口的同一位置。
要改變這一點,我們需要使用基于文檔(document)的坐標(biāo)和 position:absolute
樣式。
文檔相對坐標(biāo)從文檔的左上角開始計算,而不是窗口。
在 CSS 中,窗口坐標(biāo)對應(yīng)于 position:fixed
,而文檔坐標(biāo)與頂部的 position:absolute
類似。
我們可以使用 position:absolute
和 top/left
來把某些內(nèi)容放到文檔中的某個位置,以便在頁面滾動時,元素仍能保留在該位置。但是我們首先需要正確的坐標(biāo)。
這里沒有標(biāo)準(zhǔn)方法來獲取元素的文檔坐標(biāo)。但是寫起來很容易。
這兩個坐標(biāo)系統(tǒng)通過以下公式相連接:
pageY
? = ?clientY
? + 文檔的垂直滾動出的部分的高度。pageX
? = ?clientX
? + 文檔的水平滾動出的部分的寬度。函數(shù) getCoords(elem)
將從 elem.getBoundingClientRect()
獲取窗口坐標(biāo),并向其中添加當(dāng)前滾動:
// 獲取元素的文檔坐標(biāo)
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
函數(shù):
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;
}
頁面上的任何點都有坐標(biāo):
elem.getBoundingClientRect()
?。elem.getBoundingClientRect()
? 加上當(dāng)前頁面滾動。窗口坐標(biāo)非常適合和 position:fixed
一起使用,文檔坐標(biāo)非常適合和 position:absolute
一起使用。
這兩個坐標(biāo)系統(tǒng)各有利弊。有時我們需要其中一個或另一個,就像 CSS position
的 absolute
和 fixed
一樣。
在下面的 iframe 中,你可以看到一個帶有綠色區(qū)域(field)的文檔。
使用 JavaScript 查找?guī)Ъ^指向的角的窗口坐標(biāo)。
你的代碼應(yīng)該使用 DOM 來獲取以下窗口坐標(biāo):
你計算得到的坐標(biāo),應(yīng)該與點擊鼠標(biāo)返回的坐標(biāo)相同。
P.S. 如果元素具有其他大?。╯ize)和邊框(border),且未綁定任何固定的值,你寫的代碼也應(yīng)該起作用。
外角就是我們從 elem.getBoundingClientRect() 獲取的。
answer1
為左上角的坐標(biāo),answer2
為右下角的坐標(biāo):
let coords = elem.getBoundingClientRect();
let answer1 = [coords.left, coords.top];
let answer2 = [coords.right, coords.bottom];
內(nèi)角與外角主要的不同在于邊框的寬度。一種獲取距離的可靠的方法是 clientLeft/clientTop
:
let answer3 = [coords.left + field.clientLeft, coords.top + field.clientTop];
在我們的例子中,我們需要把外部坐標(biāo)減去邊框(border)大小。
我們可以使用 CSS 的方式:
let answer4 = [
coords.right - parseInt(getComputedStyle(field).borderRightWidth),
coords.bottom - parseInt(getComputedStyle(field).borderBottomWidth)
];
另一種方式是把 clientWidth/clientHeight
和左上角的坐標(biāo)相加。這個方式相較于上一個或許更好:
let answer4 = [
coords.left + elem.clientLeft + elem.clientWidth,
coords.top + elem.clientTop + elem.clientHeight
];
創(chuàng)建一個函數(shù) positionAt(anchor, position, elem)
來定位 elem
,具體取決于 anchor
元素附近的 position
。
position
必須具有下列三個字符串中的一個:
"top"
? — 將 ?elem
? 定位在 ?anchor
? 上方"right"
? — 將 ?elem
? 定位在 ?anchor
? 右側(cè)"bottom"
? — 將 ?elem
? 定位在 ?anchor
? 下方position
被用在函數(shù) showNote(anchor, position, html)
內(nèi),該函數(shù)使用給定的 html
創(chuàng)建一個 “note” 元素,并將其顯示在 anchor
附近的 position
處。
這是一個演示示例:
在這個任務(wù)中,我們只需要準(zhǔn)確地計算坐標(biāo)即可。具體細節(jié),請參見代碼。
請注意:元素必須在文檔中才能讀取 offsetHeight
和其它屬性。 隱藏的(display:none
)或者不在文檔中的元素沒有大小。
修改 上一個任務(wù) 的解決方案,讓 note 元素使用 position:absolute
來替代 position:fixed
。
這可以防止頁面滾動時元素的“失控”。
以上一個任務(wù)的解決方案為基礎(chǔ)。為了測試頁面滾動,請?zhí)砑訕邮?nbsp;<body style="height: 2000px">
。
解決方案實際上很簡單:
.note
? 的 CSS 中,使用 ?position:absolute
? 代替 ?position:fixed
?。擴展上一個任務(wù) 在元素旁(absolute)顯示一個 note:教函數(shù) positionAt(anchor, position, elem)
把 elem
插入到 anchor
內(nèi)部。
position
的新值:
top-out
?,?right-out
?,?bottom-out
? — 和之前一樣工作,它們把 ?elem
? 插入到 ?anchor
? 的上方/右側(cè)/下方。top-in
?,?right-in
?,?bottom-in
? — 把 ?elem
? 插入到 ?anchor
? 內(nèi)部:將其粘貼到上/右/下邊緣。例如:
// 在 blockquote 上方顯示 note
positionAt(blockquote, "top-out", note);
// 在 blockquote 內(nèi)部的上邊緣顯示 note
positionAt(blockquote, "top-in", note);
結(jié)果:
可以以上一個任務(wù) 在元素旁(absolute)顯示一個 note 的解決方案為基礎(chǔ)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: