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