DOM 中的範圍

爲了讓開發人員更方便控制頁面,DOM2 級遍歷和範圍模塊定義了 範圍 (range) 接口。經過範圍能夠選擇文檔中的一個區域,而沒必要考慮節點的界限(選擇在後臺完成,對用戶是不可見的)。 FirefoxOperaSafariChrome 都支持 DOM 範圍。IE 以專有方式實現了本身的範圍特性。javascript

DOM 中的範圍

DOM2 級在 Document 類型中定義了 createRange() 方法。在兼容 DOM 的瀏覽器中,使用這個方法屬於 document 對象。使用 hasFeature() 或者直接檢測該方法,均可以肯定瀏覽器是否支持範圍。html

var supportsRange = document.implementation.hasFeature("Range","2.0");
var alsoSupportsRange = (typeof document.createRange == "function");
複製代碼

若是瀏覽器支持範圍,那麼就可使用 createRange() 來建立 DOM 範圍,例如:java

var range = document.createRange();
複製代碼

與節點相似,新建立的範圍與建立它的文檔關聯在一塊兒,不能用於其餘文檔。 每一個範圍由一個Range 類型的實例表示,這個實例擁有不少屬性和方法。下列屬性提供了當前範圍在文檔中的位置信息:node

一、startContainer:包含範圍起點的節點(即選區中第一個節點的父節點)。git

二、startOffset:範圍在 startContainer 中起點的偏移量。github

三、endContainer:包含範圍終點的節點(即選區中最後一個節點的父節點)。瀏覽器

四、endOffset:範圍在 endContainer 中終點的偏移量。app

五、commonAncestorContainer:startContainer 和 endContainer 共同的祖先節點。ui

下面簡單介紹一下範圍的使用方法:spa

一、用 DOM 範圍實現簡單選擇

要使用範圍來選擇文檔中的一部分,最簡單的方式是使用 selectNode()selectNodeContents(),例如:

<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <p id="p1"><b>Hello</b> world!</p>
        <script> var range1 = document.createRange(), range2 = document.createRange(), p1 = document.getElementById("p1"); range1.selectNode(p1); range2.selectNodeContents(p1); </script>
    </body>
</html>
複製代碼

兩個範圍包含文檔中不一樣部分,如圖:

1
在調用 selectNode() 時, startContainerendContainer 都等於傳入節點的父節點,也就是 document.body。而 startOffset 屬性等於給定節點在其父節點的 childNodes 集合中的索引(在這個例子中是 1 ,由於兼容 DOM 的瀏覽器將空格算做一個文本節點), endOffset 等於 startOffset1 (由於只選擇了一個節點)。 在調用 selectNodeContents() 時, startContainerendContainer 等於傳入的節點,即這個例子中的 <p> 元素。而 startOffset 屬性始終等於 0 ,由於範圍從給定節點的第一個子節點開始。最後, endOffset 等於子節點的數量(node.childNodes.length),在這個例子中是 2 。例如:

console.log(range1.startContainer); //<body>...</body>
console.log(range2.startContainer); //<p id="p1">...</p>
console.log(range1.endContainer); //<body>...</body>
console.log(range2.endContainer); //<p id="p1">...</p>
console.log(range1.startOffset); //1
console.log(range2.startOffset); //0
console.log(range1.endOffset); //2
console.log(range2.endOffset); //2
複製代碼

此外,爲了更精細地控制哪些節點包含在範圍內,還可使用下列方法:

一、setStartBefore(refNode):將範圍的起點設置在 refNode 以前。

二、setStartAfter(refNode):將範圍的起點設置在refNode 以後。

三、setEndBefore(refNode):將範圍的終點設置在 refNode 以前。

四、setEndAfter(refNode):將範圍的終點設置在 refNode 以後。

二、用 DOM 範圍實現複雜選擇

要建立複雜的範圍就得使用 setStart()setEnd() 方法。這兩個方法都接受兩個參數:一個參照節點和一個偏移量值。對 setStart() 來講,參照節點會變成 startContainer,而偏移量值會變 startOffset 。對於 setEnd() 來講,參照節點會變成 endContainer ,而偏移量值會變成 endOffset。 可使用這兩個方法來模擬 selectNode()selectNodeContents() 。例如:

var range1 = document.createRange(),
    range2 = document.createRange(),
    p1 = document.getElementById("p1"),
    p1Index = -1,i,len;
for(i = 0, len = p1.parentNode.childNodes.length; i < len; i++){
    if(p1.parentNode.childNodes[i] == p1){
        p1Index = i;
        break;
    }
}
range1.setStart(p1.parentNode,p1Index);
range1.setEnd(p1.parentNode,p1Index+1);
range2.setStart(p1,0);
range2.setEnd(p1,p1.childNodes.length);
複製代碼

假設你只想選擇前面 HTML 示例代碼中從 hellolloworld!o ,能夠這樣作:

var p1 = document.getElementById("p1"),
    helloNode = p1.firstChild.firstChild,
    worldNode = p1.lastChild;
var range = document.createRange();
range.setStart(helloNode,2);
range.setEnd(worldNode,3);
複製代碼

因爲 helloNodeworldNode 都是文本節點,所以它們分別變成了新建範圍的 startContainerendContainer 。此時 startOffsetendOffset 分別用以肯定兩個節點所包含的文本中的位置,而不是用以肯定子節點的位置。如圖:

2

三、操做 DOM 範圍中的內容

對前面的例子而言,範圍通過計算知道選取中缺乏一個開始的 <b> 標籤,所以就會在後臺動態添加一個該標籤,同時還會在前面加入一個表示結束的 <b> 標籤。因而修改後的 DOM 就變成以下所示: <p><b>He</b><b>llo</b> world!</p>

像這樣建立了範圍以後,就可使用各類方法對範圍的內容進行操做了。 第一個方法:deleteContents(),從文檔中刪除範圍所包含的內容。例如:

range.deleteContents();
複製代碼

執行這句代碼以後,頁面會顯示以下 HTML 代碼:

<p id="p1"><b>He</b>rld!</p>
複製代碼

第二個方法:extractContents() ,也會從文檔中移除範圍選區,但會返回文檔片斷。例如:

var fragment = range.extractContents();
p1.parentNode.appendChild(fragment);
複製代碼

執行這句代碼以後,頁面會顯示以下 HTML 代碼:

<p id="p1"><b>He</b>rld!</p><b>llo</b> wo
複製代碼

第三個方法:cloneContents() ,會建立範圍對象的一個副本,而後插入到其餘地方。例如:

var fragment = range.cloneContents();
p1.parentNode.appendChild(fragment);
複製代碼

執行這句代碼以後,頁面會顯示以下 HTML 代碼:

<p id="p1"><b>Hello</b> world!</p><b>llo</b> wo
複製代碼

四、插入 DOM 範圍中的內容

利用範圍,能夠刪除或複製內容,也可使用 insertNode() 方法像範圍選區的開始處插入一個節點。假設咱們在前面例子中的 HTML 前面插入如下 HTML 代碼:<span style="color:red;">Inserted text</span> ,那麼可使用如下代碼:

var span = document.createElement("span");
span.style.color = "red";
span.appendChild(document.createTextNode("Inserted text"));
range.insertNode(span);
複製代碼

執行這句代碼以後,頁面會顯示以下 HTML 代碼:

<p id="p1"><b>He<span style="color: red;">Inserted text</span>llo</b> world!</p>
複製代碼

注意:<span> 正好被插入到了 hello 中的 llo 前面,而該位置就是範圍選區的開始位置。還要注意的是,這裏並無添加或刪除 <b> 元素,使用這種技術能夠插入一些幫助提示信息,例如在打開新窗口的連接旁插入一幅圖像。

除了像範圍內部插入內容以外,還能夠圍繞範圍插入內容,此時就要使用 surroundContents() 方法。該方法接受一個參數,即環繞範圍內容的節點。例如:

range.selectNode(helloNode);
var span = document.createElement("span");
span.style.backgroundColor = "yellow";  
range.surroundContents(span);
複製代碼

執行這句代碼以後,會給範圍選區加上一個黃色背景。獲得的 HTML 代碼以下所示:

<p id="p1"><b><span style="background-color: yellow;">Hello</span></b> world!</p>
複製代碼

注意:爲了插入 <span> ,範圍必須包含整個 DOM 選區(不能僅僅包含選中的 DOM 節點)。

五、摺疊 DOM 範圍

所謂 摺疊範圍 ,就是指範圍中未選擇文檔的任何部分。使用 collapse() 方法來摺疊範圍,該方法接受一個參數,一個布爾值,表示要摺疊到範圍的哪一端。參數 true 表示要摺疊到範圍的起點,參數 false 表示要摺疊到範圍的終點。要肯定範圍已經摺疊完畢,能夠檢查 collapsed 屬性,以下所示:

range.collapse(true); //摺疊到起點
alert(range.collapsed); //輸出 true
複製代碼

檢測某個範圍是否處於摺疊狀態,能夠幫助咱們肯定範圍中的兩個節點是否緊密相鄰。例如,對於下面的 HTML 代碼:

<p id="p1">Paragraph 1</p><p id="p2">Paragraph 2</p>
複製代碼

咱們假設不知其實際構成(好比動態生成的),那麼能夠像下面這樣建立一個範圍:

var p1 = document.getElementById('p1'),
    p2 = document.getElementById('p2'),
    range = document.createRange();
range.setStartAfter(p1);
range.setEndBefore(p2);
alert(range.collapsed); //true
複製代碼

在這個例子中,建立的範圍是摺疊的,因此 p1p1 是相鄰的。

六、比較 DOM 範圍

在有多個範圍的狀況下,可使用 compareBoundaryPoints() 方法來肯定這些範圍是否有公共的邊界。這個方法接受兩個參數:表示比較方式的常量值和要比較的範圍。表示比較方式的常量以下:

一、Range.START_TO_START:比較第一個範圍和第二個範圍的起點;

二、Range.START_TO_END:比較第一個範圍的起點和第二個範圍的終點;

三、Range.END_TO_END:比較第一個範圍和第二個範圍的終點;

四、Range.END_TO_START:比較第一個範圍的終點和第二個範圍的起點;

compareBoundaryPoints()方法返回值以下:若是第一個範圍中的點位於第二個範圍中的點以前,返回 -1 ;若是兩個點相等,返回 0 ;若是第一個範圍中的點位於第二個範圍中的點以後,返回 1 。例如:

var range1 = document.createRange(),
    range2 = document.createRange(),
    p1 = document.getElementById("p1");
range1.selectNodeContents(p1);
range2.selectNodeContents(p1);
range2.setEndBefore(p1.lastChild);
alert(range1.compareBoundaryPoints(Range.START_TO_START,range2)); //0
alert(range1.compareBoundaryPoints(Range.END_TO_END,range2)); //1
複製代碼

這個例子中,兩個範圍的起點相同,第一個範圍的終點位於第二個範圍的終點後面,如圖:

1

七、複製 DOM 範圍

可使用 cloneRange() 方法複製範圍,這個方法會建立調用它的範圍的一個副本。

var newRange = range.cloneRange();
複製代碼

新建立的範圍與原來的範圍包含相同的屬性,而修改它的端點不會影響原來的範圍。

八、清理 DOM 範圍

在使用完範圍後,最好是調用 detach() 方法,以便從建立範圍的文檔中分離出該範圍。調用 detach() 以後,就能夠放心地解除對範圍的引用,從而讓垃圾回收機制回收其內存了。例如:

range.detach(); //從文檔中分離
range = null; //解除引用
複製代碼

在使用範圍的最後在執行這兩個步驟是咱們推薦的方式,一旦分離範圍,就不能在恢復使用了。

IE8及更早版本中的範圍

IE9 支持範圍,但 IE8 及以前版本不支持範圍。不過,IE8 及早起版本支持一種相似的概念,即 文本範圍(text range) 。文本範圍是 IE 專有特性,主要處理文本(不必定是 DOM 節點)。經過 <body><button><input><textarea>等幾個元素,能夠調用 createTextRange() 方法來建立文本範圍。例如:

var range = document.body.createTextRange();
複製代碼

DOM 範圍相似,使用 IE 文本範圍的方式也有不少種。

一、用 IE 範圍實現簡單的選擇

選擇頁面中某一區域最簡單的方式,就是使用範圍的 findText() 方法。這個方法會找到第一次出現的給定文本,並將範圍移過來以環繞該文本。若是沒找到文本,這個方法返回 false ;不然,返回 true 。一樣,仍然如下面的 HTML 代碼爲例:

<p id="p1"><b>Hello</b> world!</p>
複製代碼

要選擇 Hello ,可使用如下代碼:

var range = document.body.createTextRange();
var found = range.findText("Hello");
alert(found); //true
alert(range.text); //"Hello"
複製代碼

IE 中與 DOM 中的 selectNode() 方法最接近的方法是 moveToElementText() ,這個方法接受一個 DOM 元素,並選擇該元素的全部文本,包含 HTML 標籤,例如:

var range = document.body.createTextRange(),
    p1 = document.getElementById("p1");
range.moveToElementText(p1);
複製代碼

在文本範圍中包含 HTML 的狀況下,可使用 htmlText 屬性取得範圍的所有內容,例如:

alert(range.htmlText);
複製代碼

IE 的範圍沒有任何屬性能夠隨着範圍選區的變化而動態更新。不過,其 parentElement() 方法卻是與DOMcommonAncestorContainer 屬性相似。例如:

var ancestor = range.parentElement();
複製代碼

這樣就獲得了範圍選區的父節點。

二、使用 IE 範圍實現複雜的選擇

IE 中建立複雜範圍的方法,就是以增量向四周移動範圍。爲此,IE 提供了 4 個方法: move()moveStart()moveEnd()expand()。 這些方法都接受兩個參數:移動單位和移動單位的數量。其中,移動單位是下列一種字符串值:

一、character:這個字符地移動;

二、word:逐個單詞(一系列非空格字符)地移動;

三、sentence:逐個句子(一系列以句號、問號或歎號結尾的字符)地移動;

四、textedit:移動到當前範圍選區的開始或結束位置。

經過 moveStart() 方法能夠移動到範圍的起點,經過 moveEnd() 方法能夠移動到範圍的終點,移動的幅度由單位數量指定,例如:

range.moveStart("word",2); //起點移動 2 個單詞
range.moveEnd("character",1); //終點移動 1 個字符
複製代碼

使用 expand() 方法能夠將範圍規範化。換句話說,能夠將任何部分選擇的文本所有選中。例如:

range.expand("word"); //能夠將整個單詞選中
複製代碼

move() 方法則先會摺疊當前範圍,而後再將範圍移動到指定的單位數量,例如:

range.move("character",5); //移動 5 個字符
複製代碼

三、操做 IE 範圍中的內容

IE 中操做範圍中的內容可使用 text 屬性或 pasteHTML() 方法。如前所述,經過 text 屬性能夠取得範圍中的內容文本;可是,也能夠經過這個屬性設置範圍中的內容文本。例如:

var range = document.body.createTextRange();
range.findText("Hello");
range.text = "Hi";
複製代碼

執行以上代碼後的 HTML 代碼以下:

<p id=p1><b>Hi</b> world!</p>
複製代碼

這時,HTML 標籤保持不變,若要像範圍中插入 HTML 代碼,就得使用 pasteHTMl() 方法,例如:

var range = document.body.createTextRange();
range.findText("Hello");
range.pasteHTML("<em>Hi</em>");
複製代碼

執行以上代碼後的 HTML 代碼以下:

<p id=p1><b><em>Hi</em></b> world!</p>
複製代碼

注意:在範圍中包含 HTML 代碼時,不該該使用 pasteHTML() ,由於這樣很容易致使不可預料的結果(極可能是格式不正確的 HTML)。

四、摺疊 IE 範圍

IE 爲範圍提供的 collapse() 方法與 DOM 方法用法同樣,惋惜,沒有對應的 collapsed 屬性讓咱們知道範圍是否已經摺疊完畢。爲此,必須使用 boundingWidth 屬性,該屬性返回範圍的寬度(以像素爲單位)。若是等於 0 說明範圍已經摺疊。例如:

range.collapse(true);
alert(range.boundingWidth); //0
複製代碼

此外,還有 boundingHeightboundingLeftboundingTop 等屬性,能夠提供一些位置信息。

五、比較 IE 範圍

IE 中的 compareEndPoints() 方法與 DOM 範圍的 compareBoundaryPoints() 方法相似。這個方法接受兩個參數:比較的類型和要比較的範圍。比較類型的取值範圍是下列幾個字符串值:StartToStartStartToEndEndToEndEndToStartcompareEndPoints() 方法返回值同 compareBoundaryPoints() 方法。例如:

var range1 = document.body.createTextRange(),
    range2 = document.body.createTextRange();
range1.findText("Hello world!");
range2.findText("Hello");
alert(range1.compareEndPoints("StartToStart",range2)); //0
alert(range1.compareEndPoints("EndToEnd",range2)); //1
複製代碼

IE 中還有兩個方法,也用於比較範圍:isEqual() 方法用於肯定兩個範圍是否相等,inRange() 方法用於肯定一個範圍是否包含另外一個範圍。例如:

var range1 = document.body.createTextRange(),
    range2 = document.body.createTextRange();
range1.findText("Hello world!");
range2.findText("Hello");
alert("range1.isEqual(range2):"+range1.isEqual(range2)); //false
alert("range1.inRange(range2):"+range1.inRange(range2)); //true
複製代碼

六、複製 IE 範圍

IE 中使用 duplicate() 方法能夠複製文本範圍,結果會建立原範圍的一個副本,例如:

var newRange = range.duplicate();
複製代碼

新建立的範圍會帶有與原範圍徹底相同的屬性。
😋😋😋 好了,基本知識就是這些,在這裏和你們說拜拜啦~

查看demo

相關文章
相關標籤/搜索