资讯专栏INFORMATION COLUMN

[譯 + 補充]理解 DOM 座標

Lionad-Morotar / 1340人阅读

摘要:相對於座標在可視區的最左上角。滑鼠座標通常透過事件取得。再次強調不幸的是沒有屬性可以直接取得元素對應的座標。觸發事件的元素相對於父容器定位元素的座標,從開始計算。上個座標與當前的座標移動距離。

座標系統

在瀏覽器中有兩種座標系統 & 滑鼠座標:

1. 相對於 `document` - 座標 (0, 0) 在整個頁面的最左上角。
2. 相對於 `window` - 座標 (0, 0) 在可視區 Viewport 的最左上角。
3. 滑鼠座標 - 通常透過事件取得。

當頁面還沒捲動時 window 和 document 的原點 (0, 0) 與其他座標是相同的。

開始捲動後,document 的座標等於 window(viewport) 的座標加上 scroll 的位置,大部分的情況下我們會使用 document 座標系統,因為它即便 scroll 了,還是會保持一致。

元素座標與 offsetParent

一個元素的座標位於該元素的左上角,不幸的是沒有屬性可以直接取得元素對應 document 的座標。不過可以透過計算 offsetTop/offsetLeft 和 offsetParent 來取得。

offsetParent 為元素的一個唯讀屬性,其值為上層最接近的有設定 position 的容器元素。更精確的說是 position 不為 static 的元素。當沒有任何 positioned 的元素時最接近的 table cell 或根元素為 offsetParent。HTML 兼容模式 body 為 offsetParent。display: none 時 offsetParent 為 null。

一個簡單的計算方式就是遍歷所有 offsetParent 計算 offsetTop/offsetLeft。簡單說就是不斷累加自己在父元素(positioned)的座標/距離,最終到達 document 則完成計算。

function getOffset (el) {
  var top = 0, left = 0
  while (el) {
    top += parentInt(el.offsetTop)
    left += parentInt(el.offsetLeft)
    el = el.offsetParent
  }

  return {
    top: top,
    left: left
  }
}

這種方式有 2 個缺點:

不同瀏覽器行為可能不同。因為 border 和 scroll 是否加入計算造成差異。

慢!因為要遍歷所有 offsetParent。

正確的方式 el.getBoundingClientRect()

這個方法時 w3c 標準,並且幾乎所有新版的瀏覽器都支援。它會回傳封閉該元素的一個矩形(CSS border-boxes),而這個矩形會透過物件的形式包含 top, left, right, bottom 等資料傳回。

再次強調 - 不幸的是沒有屬性可以直接取得元素對應 document 的座標。這個 getBoundingClientRect() 取得的資訊是相對於 window 不是 document。

根據 CSS 規範,任何內容都會被放置在 CSS Box 中。像是 div 這類的 Box 又稱 block box,這類的 Box 原本就是一個矩形。如果元素是 inline 的話會牽扯到 inline box,line box,containing box,contain area。就會有多個的矩形來組織。又稱 anonymous block box

/line-box.jpg

使用 getBoundingClientRect()

function getOffset (el) {
  // (1)
  var box = el.getBoundingClientRect()
  var body = document.body
  var docEl = document.documentElement

  // (2)
  var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop
  var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft

  // (3)
  var clientTop = docEl.clientTop || body.clientTop || 0
  var clientLeft = docEl.clientLeft || body.clientLeft || 0

  // (4)
  var top = box.top + scrollTop - clientTop
  var left = box.top + scrollLeft - clientLeft

  return {
    top: Math.round(top),
    left: Math.round(left)
  }
}

取得矩形與元素等相關座標資訊

計算 page scroll。所有瀏覽器器除了 IE < 9 外都支援 pageXOffset/pageYOffset。在 IE 當宣告了 DTD i.e DOCYPTE 則 scroll 位置可以透過 documentElement()取得,否則就從 body 取。

在 IE < 8 document 或 body 會具有特殊的偏移,所以我們需要取得偏移並扣掉。但要注意的是 clientTop/clientLeft 同時也表示 border 寬,一般來說我們不會替 document 或 body 設定 border,在這種情況下 IE 的document.documentElement.clientTop 不是 0 而是 2px。因此我們要減掉。

加入捲動的距離,然後扣掉位移即是該元素相對於 document 的座標。

針對 Firefox 我們需要而外使用四捨五入 Math.round()

scrollTop

取得已經捲動多少距離

document.body.scrollTop(Undefined DTD,兼容所有瀏覽器)

document.documentElement.scrollTop(DTD, Chrome, Safari 為 0)

window.pageYOffset(FF,Chrome,IE9+,Opera)

window.scrollY(不推薦使用)

DTD

DTD(Document Type Definition)文件類型定義,定義 XML 的元素,結構,屬性。
使用 DTD 可以讓不同的使用者在交換資料時明白其資料格式的標準,而程式可以使用 DTD 來驗證 XML 是否正確。

在 HTML 中 document.compatMode 可以得知是否宣告了 DTD,值為 BackCompat 為未宣告,CSS1Compat 為宣告 DTD。

MouseEvent 座標

screenX/Y - 相對於螢幕左上角的座標

pageX/Y - 相對於頁面左上角的座標(IE9 不支援)包含 scroll

clientX/Y - 相對於瀏覽器左上角的座標

layerX/Y - 當元素的 position 不是 static 我們稱為定位元素(positioned)。觸發事件的元素相對於父容器定位元素的座標,從 border 開始計算。

offsetX/Y - 跟 layerX/Y 不同的是 layerX/Y 是從 border 外邊界開始計算,offsetX/Y 從內邊界計算 i.e padding area,也就是說當點擊在 border 上時會是負值。

movementX/Y - 上個 mousemove 座標與當前的 mousemove 座標移動距離。currentEvent.screenX - previousEvent.screenX

window 尺寸

window.innerWidth

window.innerHeight

window.outerWidth

window.outerHeight

window.pageYOffset

window.pageXOffset

window.scrollX

window.scrollY

window.screenX

window.screenY

DOM 尺寸

clientWidth / clientHeight

scrollWidth / scrollHeight

offsetWidth / offsetHeight

scrollTop / scrollLeft

offsetTop / offsetLeft

clientTop / clientLeft

offsetParent

getBoundingClientRect()

video.videoWidth

video.videoHeight

小結

client 前綴的概略為相對於瀏覽器 viewport 或指內部 padding area。
offset 前綴的概略為相對於父定位元素。
scroll 前綴的概略為相對於完整頁面。
screen 相對於螢幕。

資源

Javascript coordinates

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88121.html

相关文章

  • [] 學習 CSS clip-path 屬性

    摘要:整體來說網頁主要是由矩形所構成的,另一方面印刷品則具備相對多樣性。即便我們設定的元素不再是矩形,但周圍的元素排列方式仍然維持原本矩形的佈局。為了達成周圍的元素跟著裁切的形狀,我們可以使用屬性。周圍的元素仍需要靠來修正。 整體來說網頁主要是由矩形所構成的,另一方面印刷品則具備相對多樣性。造成這樣差異的原因有很多,不過其中一個即是缺少合適的工具。 這篇文章主要會介紹 clip-path 這...

    yuanxin 评论0 收藏0
  • [技術分享] 理解 SVG 中的 Viewport 和 ViewBox-拖曳與縮放功能實做(上)

    摘要:註在這篇文章中我們只考慮和為等比例的情況。最後實做出來的功能會像這樣子實做拖曳與縮放瞭解中的和在的世界中,空間的概念可以分成和兩個部分。實際感受的效果在她所撰寫文章中提供了非常好的實做案例。這個座標系統是相對固定的。 理解 SVG 中的 Viewport 和 ViewBox - 實做縮放(zoom)和拖曳(drag)效果 本文章同步刊載於 PJCHENder 前端網頁資源站 不同於以往...

    Leo_chen 评论0 收藏0
  • [技術分享] 理解 SVG 中的 Viewport 和 ViewBox-拖曳與縮放功能實做(上)

    摘要:註在這篇文章中我們只考慮和為等比例的情況。最後實做出來的功能會像這樣子實做拖曳與縮放瞭解中的和在的世界中,空間的概念可以分成和兩個部分。實際感受的效果在她所撰寫文章中提供了非常好的實做案例。這個座標系統是相對固定的。 理解 SVG 中的 Viewport 和 ViewBox - 實做縮放(zoom)和拖曳(drag)效果 本文章同步刊載於 PJCHENder 前端網頁資源站 不同於以往...

    AJie 评论0 收藏0
  • [ + 補充] Webpack 2 入門

    摘要:目錄許多開發者會把的目錄命名為但這並不強迫。所有的檔案都會使用從被編譯成。同時有個小小的重點那就是我們可已觀察編譯後的檔案大小。在專案目錄下執行可以觀察截至目前為止的結果。我們的目標是要把編譯封裝到我們的中。 在今時今日,webpack 已經成為前端開發非常重要的工具之一。本質上它是一個 Javascript 模組封裝工具,但透過 loaders 和 plugins 它也可以轉換封裝其...

    betacat 评论0 收藏0
  • [ + 更新] 參透 Node 中 exports 的 7 種設計模式

    摘要:現在,我們可以開始探討介面的設計模式了。匯出命名空間一個簡單且常用的設計模式就是匯出一個包含數個屬性的物件,這些屬性具體的內容主要是函式,但並不限於函式。如此,我們就能夠透過匯入該模組來取得這個命名空間下一系列相關的功能。 前言 這篇文章試著要整理,翻譯Export This: Interface Design Patterns for Node.js Modules這篇非常值得一讀的...

    wmui 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<