AbstractRange
AbstractRange 抽象介面是定義所有 DOM 範圍型別的基礎類。範圍(range)是一個物件,用於指示文件中內容部分的起始點和結束點。
注意: 作為一個抽象介面,你不能直接例項化 AbstractRange 型別的物件。你應該使用 Range 或 StaticRange 介面。要了解這兩個介面的區別,以及如何根據你的需求選擇合適的介面,請查閱每個介面的文件。
例項屬性
collapsed只讀-
一個布林值,如果範圍是摺疊的(collapsed),則為
true。摺疊的範圍是指其開始位置和結束位置相同的範圍,從而得到一個零字元長度的範圍。 endContainer只讀-
endOffset屬性指定的範圍結束點所在的Node物件。 endOffset只讀-
一個整數值,表示範圍物件所表示的範圍的最後一個字元,相對於節點內容的開始位置的偏移量(以字元為單位)。此值必須小於
endContainer節點 的長度。 startContainer只讀-
startOffset屬性指定的範圍開始點所在的 DOMNode。 startOffset只讀-
一個整數值,表示範圍物件所引用的內容的第一個字元,相對於節點內容的開始位置的偏移量(以字元為單位)。此值必須小於
startContainer中指示的節點 的長度。
例項方法
AbstractRange 介面不提供任何方法。
用法說明
範圍型別
document 中所有內容範圍都使用基於 AbstractRange 的介面例項來描述。有以下兩種介面:
Range-
Range介面存在已久,最近才被重新定義為基於AbstractRange,以滿足定義其他形式範圍資料的需求。Range提供了允許你修改範圍端點的方法,以及用於比較範圍、檢測範圍交叉等的方法。 StaticRange-
StaticRange是一個基礎範圍,一旦建立就無法更改。具體來說,隨著節點樹的變異和改變,該範圍保持不變。這在你只需要一次性使用某個範圍時非常有用,因為它避免了更復雜的Range介面帶來的效能和資源影響。
元素的內容
在嘗試訪問元素的內容時,請記住,元素本身是一個節點,它內部的任何文字也是一個節點。為了在元素的文字內設定範圍端點,請確保找到元素內部的文字節點。
const startElem = document.querySelector("p");
const endElem = startElem.querySelector("span");
const range = document.createRange();
range.setStart(startElem, 0);
range.setEnd(endElem, endElem.childNodes[0].length / 2);
const contents = range.cloneContents();
document.body.appendChild(contents);
此示例建立一個新的範圍 range,並將其起始點設定為第一個元素的第三個子節點。結束點被設定為 span 的第一個子節點的中間位置,然後使用該範圍複製範圍的內容。
範圍與 DOM 的層級結構
為了能夠跨越零個或多個節點邊界來定義文件中的字元範圍,並且儘可能地對 DOM 更改具有彈性,你不能在 HTML 中直接指定第一個和最後一個字元的偏移量。這有幾個很好的原因。
首先,在頁面載入完成後,瀏覽器不再以 HTML 的形式思考。一旦載入完成,頁面就是一個 DOM Node 物件樹,因此你需要使用節點和節點內的位置來指定範圍的開始和結束位置。
其次,為了儘可能地支援 DOM 樹的可變性,你需要一種方式來表示相對於樹中節點的位置,而不是相對於整個文件的全域性位置。透過將文件中的點定義為給定節點內的偏移量,即使節點被新增到、從 DOM 樹中移除或在 DOM 樹中移動(在合理範圍內),這些位置也能與內容保持一致。當然存在一些明顯的限制(例如,如果一個節點被移動到範圍的結束點之後,或者節點的內容被嚴重修改),但這比什麼都沒有要好得多。
第三,使用節點相對位置來定義開始和結束位置通常更容易獲得高效能。使用者代理(瀏覽器)不需要遍歷 DOM 來確定全域性偏移量指的是什麼,而是可以直接定位到起始位置指示的節點,然後從那裡開始,向前移動直到到達結束節點中給定的偏移量。
為了說明這一點,請考慮下面的 HTML
<div class="container">
<div class="header">
<img src="" class="sitelogo" />
<h1>The Ultimate Website</h1>
</div>
<article>
<section class="entry" id="entry1">
<h2>Section 1: An interesting thing…</h2>
<p>A <em>very</em> interesting thing happened on the way to the forum…</p>
<aside class="callout">
<h2>Aside</h2>
<p>An interesting aside to share with you…</p>
</aside>
</section>
</article>
<pre id="log"></pre>
</div>
載入 HTML 並構建文件的 DOM 表示後,生成的 DOM 樹如下所示:
在此圖中,代表 HTML 元素的節點顯示為綠色。它們下方的每一行顯示了 DOM 樹中下一層級的深度。藍色節點是文字節點,包含螢幕上顯示的文字。每個元素的子節點都連結在其下方,可能透過元素包含其他元素和文字節點,從而在下方形成一系列分支。
如果你想建立一個包含 <p> 元素(其內容是 "A <em>very</em> interesting thing happened on the way to the forum…")的範圍,可以這樣做:
const pRange = document.createRange();
pRange.selectNodeContents(document.querySelector("#entry1 p"));
由於我們希望選擇 <p> 元素及其子元素的全部內容,所以這樣做是完美的。
如果我們希望複製 <section> 的標題(一個 h2 元素)中的文字“An interesting thing…”一直到下面段落中 <em> 中的字母“ve”結束,則以下程式碼會起作用:
const range = document.createRange();
const startNode = document.querySelector("section h2").childNodes[0];
range.setStart(startNode, 11);
const endNode = document.querySelector("#entry1 p em").childNodes[0];
range.setEnd(endNode, 2);
const fragment = range.cloneContents();
這裡出現了一個有趣的問題——我們正在捕獲來自 DOM 層級中不同位置的多個節點的內容,並且只捕獲其中一部分。結果應該是什麼樣的?
事實證明,DOM 規範恰好解決了這個問題。例如,在這種情況下,我們正在呼叫範圍的 cloneContents() 方法來建立一個新的 DocumentFragment 物件,該物件提供了一個 DOM 子樹,複製了指定範圍的內容。為了做到這一點,cloneContents() 會構建所有必要節點來保留指定範圍的結構,但不會多餘。
在此示例中,指定範圍的開始點位於 section 標題下方的文字節點中,這意味著新的 DocumentFragment 需要包含一個 h2,並在其下方包含一個文字節點。
範圍的結束點位於 <p> 元素下方,因此它需要包含在新片段中。包含“A”的文字節點也需要,因為它包含在範圍內。最後,一個 <em> 元素及其下方的文字節點也將被新增到 <p> 元素下方。
文字節點的具體內容由呼叫 setStart() 和 setEnd() 時給定的文字節點內的偏移量決定。由於標題文字的偏移量為 11,因此該節點將包含“An interesting thing…”。同樣,根據對結束節點前兩個字元的要求,最後一個文字節點將包含“ve”。
生成的文件片段如下所示:
特別要注意的是,此片段的內容都位於其內頂層節點共享公共父節點的下方。父節點 <section> 不需要複製克隆的內容,因此未包含在內。
示例
考慮以下簡單的 HTML 片段:
<p><strong>This</strong> is a paragraph.</p>
假設使用 Range 來提取其中的單詞“paragraph”。實現此目的的程式碼如下所示:
const paraNode = document.querySelector("p");
const paraTextNode = paraNode.childNodes[1];
const range = document.createRange();
range.setStart(paraTextNode, 6);
range.setEnd(paraTextNode, paraTextNode.length - 1);
const fragment = range.cloneContents();
document.body.appendChild(fragment);
首先,我們獲取對段落節點本身以及段落內第二個子節點的引用。第一個子節點是 <strong> 元素。第二個子節點是文字節點“ is a paragraph.”。
有了文字節點的引用後,我們透過在 Document 本身上呼叫 createRange() 來建立一個新的 Range 物件。我們將範圍的起始位置設定為文字節點字串的第六個字元,結束位置設定為文字節點字串的長度減一。這會將範圍設定為包含單詞“paragraph”。
然後,我們透過呼叫 Range 上的 cloneContents() 來完成操作,建立一個新的 DocumentFragment 物件,該物件包含範圍所覆蓋的文件部分。之後,我們使用 appendChild() 將該片段新增到文件的 body 末尾,該 body 透過 document.body 獲取。
結果如下所示
規範
| 規範 |
|---|
| DOM # interface-abstractrange |
瀏覽器相容性
載入中…