国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

詳解Javascript中DOM的范圍

2019-11-19 17:37:24
字體:
來源:轉載
供稿:網友

前言

為了讓開發人員更方便地控制頁面,DOM定義了“范圍”(range)接口。通過范圍可以選擇文檔中的一個區域,而不必考慮節點的界限(選擇在后臺完成,對用戶是不可見的)。在常規的DOM操作不能更有效地修改文擋時,使用范圍往往可以達到目的。本文將詳細介紹DOM范圍,下面來一起看看吧。

創建范圍

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

[注意]IE8-瀏覽器不支持

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

如果瀏覽器支持范圍,那么就可以使用createRange()來創建DOM范圍,如下所示

var range = document.createRange();

與節點類似,新創建的范圍也直接與創建它的文檔關聯在一起,不能用于其他文檔。創建了范圍之后,接下來就可以使用它在后臺選擇文檔中的特定部分。而創建范圍并設置了其位置之后,還可以針對范圍的內容執行很多種操作,從而實現對底層DOM樹的更精細的控制

每個范圍由一個Range類型的實例表示,這個實例擁有很多屬性和方法。

下列屬性提供了當前范圍在文檔中的位置信息

  • startContainer:包含范圍起點的節點(即選區中第一個節點的父節點)
  • startoffset:范圍在startContainer中起點的偏移量。如果startContainer是文本節點、注釋節點或CDATA節點,那么startoffset就是范圍起點之前跳過的字符數量。否則,startoffset就是范圍中第一個子節點的索引
  • endContainer:包含范圍終點的節點(即選區中最后一個節點的父節點)
  • endOffset:范圍在endContainer中終點的偏移量(與startoffset遵循相同的取值規則)
  • commonAncestorContainer:startContainer和endContainer共同的祖先節點在文檔樹中位置最深的那個

在把范圍放到文檔中特定的位置時,這些屬性都會被賦值

簡單選擇

要使用范圍來選擇文檔中的一部分,最簡單的方式就是使用selectNode()selectNodeContents() 。這兩個方法都接受一個參數,即一個DOM節點,然后使用該節點中的信息來填充范圍。其中,selectNode()方法選擇整個節點,包括其子節點;而selectNodeContents()方法則只選擇節點的子節點

<!DOCTYPE html><html> <body> <p id="p1"><b>Hello</b> world!</p> </body></html>

我們可以使用下列代碼來創建范圍

var range1 = document.createRange();var range2 = document.createRange();var p1 = document.getElementById("p1");//Range {startContainer: body, startOffset: 1, endContainer: body, endOffset: 2, collapsed: false…}range1.selectNode(p1);//Range {startContainer: p#p1, startOffset: 0, endContainer: p#p1, endOffset: 2, collapsed: false…}range2.selectNodeContents(p1);

這里創建的兩個范圍包含文檔中不同的部分:rangl包含<p>元素及其所有子元素,而rang2包含<b>元素、文本節點"Hello"和文本節點"world!"

在調用selectNode()時,startContainer、endContainer和commonAncestorContainer都等于傳入節點的父節點,也就是這個例子中的document.body。而startoffset屬性等于給定節點在其父節點的childNodes集合中的索引(在這個例子中是1――因為兼容DOM的瀏覽器將空格算作一個文本節點),endOffset等于startoffset加1(因為只選擇了一個節點)

在調用selectNodeContents()時,startContainer、endContainer和commonAncestorContainer等于傳入的節點,即這個例子中的<p>元素。而startoffset屬性始終等于0,因為范圍從給定節點的第一個子節點開始。最后,endOffset等于子節點的數量(node.childNodes.length),在這個例子中是2

此外,為了更精細地控制將哪些節點包含在范圍中,還可以使用下列方法

  1. setStartBefore(refNode) :將范圍的起點設置在refNode之前,因此refNode也就是范圍選區中的第一個子節點。同時會將startContainer屬性設置為refNode.parentNode,將startoffset屬性設置為refNode在其父節點的childNodes集合中的索引
  2. setStartAfter(refNode) :將范圍的起點設置在refNode之后,因此refNode也就不在范圍之內了,其下一個同輩節點才是范圍選區中的第一個子節點。同時會將startContainer屬性設置為refNode.parentNode,將startoffset屬性設置為refNode在其父節點的childNodes集合中的索引加1
  3. setEndBefore(refNode) :將范圍的終點設置在refNode之前,因此refNode也就不在范圍之內了,其上一個同輩節點才是范圍選區中的最后一個子節點。同時會將endContainer屬性設置為refNode.parentNode,將endOffset屬性設置為refNode在其父節點的childNodes集合中的索引
  4. setEndAfter(refNode) :將范圍的終點設置在refNode之后,因此refNode也就是范圍選區中的最后一個子節點。同時會將endContainer屬性設置為refNode.parentNode,將endOffset屬性設置為refNode在其父節點的childNodes集合中的索引加1

調用這些方法時,所有屬性會自動設置好。不過,要想創建復雜的范圍選區,也可以直接指定這些屬性的值

復雜選擇

要創建復雜的范圍就得使用setStart()setEnd()方法。這兩個方法都接受兩個參數:一個參照節點和一個偏移量值。對setStart()來說,參照節點會變成startContainer。而偏移量值會變成startoffset。對于setEnd()來說,參照節點會變成endContainer,而偏移量值會變成endOffset。可以使用這兩個方法來模仿selectNode()selectNodeContents()

來看下面的例子

var range1 = document.createRange();var range2 = document.createRange();var p1 = document.getElementById("p1"); var p1Index = -1;var 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);

顯然,要選擇這個節點(使用range1),就必須確定當前節點(p1)在其父節點的childNodes集合中的索引。而要選擇這個節點的內容(使用range2),也不必計算什么;只要通過setStart()和setEnd()設置默認值即可。模仿selectNode()selectNodeContents()并不是setStart()setEnd()的主要用途,它們更勝一籌的地方在于能夠選擇節點的一部分

假設只想選擇前面HTML示例代碼中從“Hello"的"llo"到"world!"的"o"――很容易做到

第一步是取得所有節點的引用,如下所示:

var p1 = document.getElementById("p1");var helloNode = p1.firstChild.firstChild;var worldNode = p1.lastChild;

實際上,"Hello”文本節點是<p>元素的孫子節點,因為它本身是<b>元素的一個子節點。因此,p1.firstchild取得的是<b>,而p1.firstchild.firstchild取得的才是這個文本節點。"world!"文本節點是<p>元素的第二個子節點(也是最后一個子節點),因此可以使用p1.lastChild取得該節點。

然后,必須在創建范圍時指定相應的起點和終點,如下所示

var range = document.createRange();range.setStart(helloNode, 2);range.setEnd(worldNode, 3);

因為這個范圍的選區應該從"Hello"中"e"的后面開始,所以在setStart()中傳入helloNode的同時,傳入了偏移量2(即"e"的下一個位置;"H"的位置是0)。設置選區的終點時,在setEnd()中傳入worldNode的同時傳入了偏移量3,表示選區之外的第一個字符的位置,這個字符是”r",它的位置是3(位置0上還有一個空格)。

如下所示

由于helloNode和worldNode都是文本節點,因此它們分別變成了新建范圍的startContainer和endContainer。此時startoffset和endOffset分別用以確定兩個節點所包含的文本中的位置,而不是用以確定子節點的位置(就像傳入的參數為元素節點時那樣)。此時的commonAncestorContainer是<p>元素,也就是同時包含這兩個節點的第一個祖先元素

當然,僅僅是選擇了文檔中的某一部分用處并不大。但重要的是,選擇之后才可以對選區進行操作

操作范圍內容

在創建范圍時,內部會為這個范圍創建一個文檔片段,范圍所屬的全部節點都被添加到了這個文檔片段中。為了創建這個文檔片段,范圍內容的格式必須正確有效。在前面的例子中,創建的選區分別開始和結束于兩個文本節點的內部,因此不能算是格式良好的DOM結構,也就無法通過DOM來表示。但是,范圍知道自身缺少哪些開標簽和閉標簽,它能夠重新構建有效的DOM結構以便對其進行操作

對于前面的例子而言,范圍經過計算知道選區中缺少一個開始的<b>標簽,因此就會在后臺動態加入一個該標簽,同時還會在前面加入一個表示結束的</b>標簽以結束"He"。于是,修改后的DOM就變成了如下所示

<p><b>He</b><b>llo</b> world!</p>

另外,文本節點"world!"也被拆分為兩個文本節點,一個包含"wo",另一個包含"rid!"。最終的DOM樹下圖所示,右側是表示范圍的文檔片段的內容

像這樣創建了范圍之后,就可以使用各種方法對范圍的內容進行操作了

[注意]表示范圍的內部文檔片段中的所有節點,都只是指向文檔中相應節點的指針

【deleteContents()】

操作范圍內容的第一個方法是deleteContents() ,這個方法能夠從文檔中刪除范圍所包含的內容

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

執行以上代碼后,頁面中會顯示如下HTML代碼

<p><b>He</b>rld!</p>

由于范圍選區在修改底層DOM結構時能夠保證格式良好,因此即使內容被刪除了,最終的DOM結構依舊是格式良好的

【extractContents()】

deleteContents()方法相似,extractContents()方法也會從文檔中移除范圍選區。但區別在于,extractContents()會返回范圍的文檔片段。利用這個返回的值,可以將范圍的內容插入到文檔中的其他地方。如下所示:

var p1 = document.getElementById("p1");var helloNode = p1.firstChild.firstChild;var worldNode = p1.lastChild;var range = document.createRange();range.setStart(helloNode, 2);range.setEnd(worldNode, 3);var fragment = range.extractContents();p1.parentNode.appendChild(fragment);

在這個例子中,將提取出來的文檔片段添加到了文檔<body>元素的末尾

[注意]在將文檔片段傳入appendChild()方法中時,添加到文檔中的只是片段的子節點,而非片段本身

<p><b>He</b>rld!</p><b>llo</b> wo

【cloneContents】

還有一種做法是使用cloneContents()創建范圍對象的一個副本,然后在文檔其他地方插入該副本

var p1 = document.getElementById("p1");var helloNode = p1.firstChild.firstChild;var worldNode = p1.lastChild;var range = document.createRange();range.setStart(helloNode, 2);range.setEnd(worldNode, 3);var fragment = range.cloneContents();p1.parentNode.appendChild(fragment);

這個方法與extractContents()非常類似,因為它們都返回文檔片段。它們的主要區別在于,cloneContents()返回的文檔片段包含的是范圍中節點的副本,而不是實際的節點。執行上面的操作后,頁面中的HTML代碼如下所示:

<p><b>Hello</b> world!</p><b>llo</b> wo

[注意]在調用cloneContents()方法之前,拆分的節點并不會產生格式良好的文檔片段。換句話說,原始的HTML在DOM被修改之前會始終保持不變

插入范圍內容

利用范圍,可以刪除或復制內容,還可以像前面介紹的那樣操作范圍中的內容。使用insertNode()方法可以向范圍選區的開始處插入一個節點。假設在前面例子中的HTML前面插入以下HTML代碼

<span style="color: red">Inserted text</span>

那么可以使用下列代碼:

var p1 = document.getElementById("p1");var helloNode = p1.firstChild.firstChild;var worldNode = p1.lastChild;var range = document.createRange();var span = document.createElement("span");range.setStart(helloNode, 2);range.setEnd(worldNode, 3);span.style.color = "red";span.appendChild(document.createTextNode("Inserted text"));range.insertNode(span);

運行以上javascript代碼,就會得到如下HTML代碼

<p id="p1"><b>He<span styie="color:red">Inserted text</span>llo</b> world</p>

[注意]<span>正好被插入到了"Hello"中的"llo"前面,而該位置就是范圍選區的開始位置。使用這種技術可以插入一些幫助提示信息,例如在打開新窗口的鏈接旁邊插入一幅圖像

【surroundContents()】

除了向范圍內部插入內容之外,還可以環繞范圍插入內容,此時就要使用surroundContents()方法。這個方法接受一個參數,即環繞范圍內容的節點。在環繞范圍插入內容時,后臺會執行下列步驟

  1、提取出范圍中的內容(類似執行extractContents() )

  2、將給定節點插入到文檔中原來范圍所在的位置上

  3、將文檔片段的內容添加到給定節點中

可以使用這種技術來突出顯示網頁中的某些詞句,例如下列代碼

var p1 = document.getElementById("p1");var helloNode = p1.firstChild.firstChild;var worldNode = p1.lastChild;var range = document.createRange();range.selectNode(helloNode);var span = document.createElement("span");span.style.backgroundColor = "yellow";range.surroundContents(span);

以上代碼會給范圍選區加上一個黃色的背景。得到的HTML代碼如下所示

<p><b><span style="background-color:yellow">Hello</b> world!</p>

為了插入<span>,范圍必須包含整個DOM選區,而不能僅僅包含選中的DOM節點

折疊范圍

所謂折疊范圍,就是指范圍中未選擇文檔的任何部分。可以用文本框來描述折疊范圍的過程。假設文本框中有一行文本,用鼠標選擇了其中一個完整的單詞。然后,單擊鼠標左鍵,選區消失,而光標則落在了其中兩個字母之間。同樣,在折疊范圍時,其位置會落在文檔中的兩個部分之間,可能是范圍選區的開始位置,也可能是結束位置。下圖展示了折疊范圍時發生的情形

【collapse()】

使用collapse()方法來折疊范圍,這個方法接受一個參數,該參數是一個布爾值,表示要折疊到范圍的哪一端。參數true表示折疊到范圍的起點,參數false表示折疊到范圍的終點。要確定范圍已經折疊完畢,可以檢

主站蜘蛛池模板: 阜康市| 永清县| 沙雅县| 会昌县| 桂林市| 丰宁| 嘉荫县| 广安市| 嵩明县| 灵山县| 土默特右旗| 兰坪| 洞口县| 渭南市| 甘泉县| 揭东县| 高台县| 曲阜市| 邯郸市| 庆安县| 辛集市| 青神县| 梅州市| 宁德市| 醴陵市| 德清县| 松阳县| 富阳市| 柘荣县| 静海县| 宁南县| 厦门市| 娱乐| 天镇县| 景德镇市| 景洪市| 化州市| 揭阳市| 安龙县| 通化县| 昭通市|