理解HTML5中Range物件

龍恩0707發表於2017-08-19

1.理解Range物件

   重新來學習下HTML5中的Range物件和Selection物件,最近在維護富文字編輯器,感覺這方面的知識點很有用,所以趁著週末多學習下~ 

什麼是Range物件?
在HTML5中,一個Range物件代表頁面上的一段連續區域。可以通過如下語句建立一個空的Range物件。如下程式碼:

var range = document.createRange();

什麼是Selection物件?
在HTML5中,每一個瀏覽器視窗都有一個Selection物件,代表使用者滑鼠在頁面中所選取的區域。可以通過如下程式碼得到一個Selection物件:

var selection = window.getSelection();
或 
var selection = document.getSelection();

Selection物件與Range物件的區別是?
每一個Selection物件都有一個或多個Range物件,每一個Range物件代表使用者用滑鼠所選取範圍內的一段連續區域。

Firefox 與 chrome,safari對Selection的區別?
   在Firefox瀏覽器中,使用者可以通過按住 ctrl鍵來選取頁面上的多個區域,因此一個Selection物件可能有多個Range物件。
   在chrome或safari瀏覽器中,使用者每次只能選取一段區域,所以一個Selection物件中只能有一個Range物件。

如何獲取Selection物件中的某個Range物件呢?
可以通過Selection物件的getRangeAt方法來獲取。程式碼如下:

var range = document.getSelection().getRangeAt(rangeIndex);

rangeIndex 代表Range物件的序號,在chrome或safari中,使用者每次只能選取一段區域,因此該值只能為0;

如何判斷使用者是否選取了內容?
可以通過Selection物件的 rangeCount 屬性來判斷;
  1. 使用者沒有按下滑鼠該屬性值為0;
  2. 使用者按下滑鼠之後該屬性值為1;
  3. 使用者用滑鼠加ctrl鍵選取了一個或多個區域時,該屬性值代表使用者通過滑鼠選取的區域的數量,當使用者取消該區域的選取之後,該屬性值為1.
下面是一個demo;頁面上顯示一段文字和一個按鈕,當使用者單擊按鈕時彈出的提示框中顯示使用者用滑鼠加ctrl鍵共選取了多少個區域及每一段區域中的內容。程式碼如下:

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8">
      <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
      <title>標題</title>
    </head>
    <body>
      <h3>Selection物件與Range物件使用demo</h3>
      <input type="button" value="選中我然後點選" onClick="rangeTest()" />
      <div id="showRange"></div>
      <script>
        function rangeTest() {
          var html,
            showRangeDiv=document.getElementById('showRange'),
            selection=document.getSelection();
          if(selection.rangeCount > 0) {
            html = "您選取了"+ selection.rangeCount + "段內容<br/>";
            for(var i = 0; i < selection.rangeCount; i++) {
              var range = selection.getRangeAt(i);
              html += ""+ (i+1) + "段內容為:" + range + "<br/>"; 
            }
            showRangeDiv.innerHTML = html;
          }
        }
      </script>
    </body>
  </html>

檢視效果

1-2 Range物件的屬性有哪些?
我們在頁面上建立的程式碼如下:

var rangeObj = document.createRange();
console.log(rangeObj);

列印後看到有如下屬性:
collapsed: (Boolean) 用於判斷Range物件所代表的區域的開始點和結束點是否位於相同的位置,如果相同該屬性值為true。
commonAncestorContainer: (node) 返回Range物件所代表的區域位於什麼節點之中。
endContainer: (node) 用於返回Range物件所代表的區域的終點位於什麼節點之中。
endOffset(int) 用於返回Range物件所代表區域的終點與包含該終點的節點起點之間的距離。
startContainer: (node) 用於返回Range物件所代表區域的起點位於什麼節點之中。
startOffset (int) 用於返回Range物件所代表的區域的起點與包含該起點節點的起點之間的距離。

2 Range物件的方法
   2-1 理解使用 selectNode, selectNodeContents, 與 deleteContents方法
selectNode: Range物件的selectNode 方法用於將Range物件的起點指定為某個節點的起點,將Range物件的終點指定為該節點的終點。Range物件所代表的區域包含該節點。
使用方法如下:
rangeObj.selectNode(node);
demo舉例理解
假如頁面上有一個div,該div包含內容,如果使用 rangeObj.selectNode("div"); 的含義是,選擇該div內的所有內容,包括該div標籤本身。

selectNodeContents: 該方法用於將Range物件的起點指定為某個節點中的所有內容的起點,將Range物件的終點指定為該節點所有內容的終點,也就是說使Range物件所代表的區域包含該節點的所有內容,但是不包括該節點標籤本身。

使用方法如下:
rangeObj.selectNodeContents(node);
demo舉例理解:
還是上面的div元素,該元素包含內容,如果使用 rangeObj.selectNodeContents('div');的話,的含義是,選擇該div內的所有內容,但是不包括該div本身。

deleteContents: 該方法用於將Range物件中所包含的內容從頁面中刪除。
使用方法如下:
rangeObj.deleteContents();

下面是使用一個demo來理解上面的三個方法的具體含義,頁面中有一個div元素,一個刪除內容的按鈕,和一個刪除元素的按鈕,div元素中顯示一些文字,當使用者單擊 "刪除內容"按鈕時,會將div元素中的文字從頁面中刪除,單擊 “刪除元素” 按鈕時,會將整個div元素從頁面中刪除。

程式碼如下:

<!DOCTYPE html>
     <html>
        <head>
          <meta charset="utf-8">
          <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
          <title>標題</title>
        </head>
        <body>
          <h3>Selection物件與Range物件使用demo</h3>
          <div id="div" style="background-color: #e0a0b0; width:300px; height: 50px;">aaaaadsadsdasadsbbgg</div>
          <button onclick="deleteRangeContents(true);">刪除內容</button>
          <button onclick="deleteRangeContents(false);">刪除元素</button>

          <script>
              function deleteRangeContents(flag) {
                var div = document.getElementById("div");
                var rangeObj = document.createRange();
                if (flag) {
                  rangeObj.selectNodeContents(div);
                } else {
                  rangeObj.selectNode(div);
                }
                rangeObj.deleteContents();
              }
          </script>
        </body>
    </html>

檢視效果

 2-2 理解使用 setStart方法,setEnd方法,setStartBefore方法,setStartAfter方法,setEndBefore方法與setEndAfter方法; 

  setStart: 該方法用於將某個節點中的某個位置指定為Range物件所代表區域的起點位置。使用方法如下:
  rangeObj.setStart(node, num);
  num的含義是:首先它是一個整型數值,有兩種含義取決於node節點;
    1. 當第一個引數node所代表的節點是一個內容為一段文字的時候,那麼該引數值用於指定將第幾個文字結束位置作為Range物件所代表區域的起點位置(num是從0開始)
    2. 當第一個引數node所代表的節點包括其他子節點時,該引數用於指定將第幾個子節點的結束位置作為Range物件所代表區域的起點位置。

   setEnd: 該方法用於將某個節點中的某處位置指定為Range物件所代表區域的結束位置。使用方法如下:
   rangeObj.setEnd(node, num);
   num的含義: 首先是一個整型數值;
     1. 當第一個引數node所代表的節點是一個內容為一段文字的時候,該引數指定將第幾個文字結束位置作為Range物件所代表區域的結束位置。
     2. 當第一個引數node所代表的節點包括其他子節點時,該引數值用於指定將第幾個子節點的結束位置作為Range物件所代表區域的結束位置。

   下面是一個簡單的demo來理解上面的 setStart和setEnd方法的使用,頁面上有一個div元素和一個刪除文字的按鈕,div元素中有一些文字,當使用者點選 刪除文字按鈕時,div元素中的第三個文字到第十個文字將被刪除。
程式碼如下:

<!DOCTYPE html>
     <html>
        <head>
          <meta charset="utf-8">
          <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
          <title>標題</title>
        </head>
        <body>
          <div id="myDiv" style="color: red">這段文字中第三個文字到第十個文字將被刪除</div>
          <button onclick="deleteChar()">刪除文字</button>
          <script>
            function deleteChar() {
              var div = document.getElementById("myDiv");
              var textNode = div.firstChild;
              var rangeObj = document.createRange();
              rangeObj.setStart(textNode, 2);
              rangeObj.setEnd(textNode, 10);
              rangeObj.deleteContents();
            }
          </script>
        </body>
    </html>

檢視效果

setStartBefore: 該方法用於將某個節點的起始位置指定為Range物件所代表區域的起點位置。使用方法如下:
rangeObj.setStartBefore(node);

setStartAfter: 該方法用於將某個節點的終點位置指定為Range物件所代表區域的起點位置。使用方法如下:
rangeObj.setStartAfter(node);

setEndBefore: 該方法用於將某個節點的起始位置指定為Range物件所代表區域的終點位置,使用方法如下:
rangeObj.setEndBefore(node);

setEndAfter: 該方法用於將某個節點的終點位置指定為Range物件所代表區域的終點位置。使用方法如下:
rangeObj.setEndAfter(node);

看上面的四個方法容易混淆,我們來做一個demo,使用一下 setStartBefore 和 setEndAfter方法。 頁面上有一個表格和一個按鈕,使用者單擊按鈕時,通過Range物件的setStartBefore 和 setEndAfter方法 使Range物件代表的區域包含表格的第一行,然後刪除該行。

<!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
        <title>標題</title>
      </head>
      <body>
        <table id="myTable" border="1" cellspacing="0" cellpadding="0">
          <tr>
            <td>第一行第一列</td>
            <td>第一行第二列</td>
          </tr>
          <tr>
            <td>第二行第一列</td>
            <td>第二行第二列</td>
          </tr>
        </table>
        <button onclick="deleteFirstRow()">刪除第一行</button>
        <script>
          function deleteFirstRow() {
            var myTable = document.getElementById('myTable');
            if (myTable.rows.length > 0) {
              var row = myTable.rows[0];
              var rangeObj = document.createRange();
              rangeObj.setStartBefore(row);
              rangeObj.setEndAfter(row);
              rangeObj.deleteContents();
            }
          }
        </script>
      </body>
    </html>

檢視效果

2-3 理解使用 cloneRange方法,cloneContents方法,extractContents方法

cloneRange: 該方法用於對當前的Range物件進行復制,該方法返回複製的Range物件。使用方法如下:
   var rangeClone = rangeObj.cloneRange();
下面可以看一個demo來理解一下,頁面上顯示一個 "克隆Range物件" 按鈕,使用者單擊該按鈕時,建立一個Range物件,該物件包含頁面中的所有內容,然後使用cloneRange方法複製Range物件,然後在彈窗顯示該Range物件中的內容。
程式碼如下:

<!DOCTYPE html>
   <html>
      <head>
        <meta charset="utf-8">
        <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
        <title>標題</title>
      </head>
      <body>
        <button onclick="cloneRange()">克隆Range物件</button>
        <script>
          function cloneRange() {
            var rangeObj = document.createRange();
            rangeObj.selectNodeContents(document.body);
            var rangeClone = rangeObj.cloneRange();
            console.log(rangeClone);
            alert(rangeClone.toString());
          }
        </script>
      </body>
  </html>

檢視效果

cloneContents: 該方法用於在頁面上追加一段HTML程式碼,使用方法如下:
var docFragment = rangeObj.cloneContents();
該方法返回的是 一個 DocumentFragment物件,該物件為一個容器元素,當需要追加,修改,刪除或查詢頁面上的元素時,該方法非常有用。
下面是一個demo,頁面上顯示一個div元素,div元素中包含一些文字和一個按鈕,使用者點選按鈕時將在該div元素底部克隆出相同的文字和按鈕。

<!DOCTYPE html>
   <html>
      <head>
        <meta charset="utf-8">
        <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
        <title>標題</title>
      </head>
      <body>
        <div id="div">
          <span>aaaaaa</span>
          <button onclick="cloneDivContent()">克隆</button>
        </div>
        <script>
          function cloneDivContent() {
            var div = document.getElementById('div');
            var rangeObj = document.createRange();
            rangeObj.selectNodeContents(div);
            var documentFragment = rangeObj.cloneContents();
            div.appendChild(documentFragment);
          }
        </script>
      </body>
  </html>

檢視效果

extraContents: 該方法用於將Range物件所代表區域的HTML程式碼克隆到一個 DocumentFragment中,然後從頁面中刪除這段HTML程式碼;

使用方法如下:
var documentFragment = rangeObj.extraContents();

下面是一個demo, 頁面上有2個div元素和一個按鈕,其中第一個div元素包含一些文字,使用者單擊該按鈕時,把文字移動到第二個div中。

<!DOCTYPE html>
   <html>
      <head>
        <meta charset="utf-8">
        <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
        <title>標題</title>
      </head>
      <body>
        <div id="srcDiv" style="background-color: red; width: 300px; height:50px">adsddasdssdsdsdsads</div>
        <div id="distDiv" style="background-color: blue; width: 300px; height: 50px;"></div>
        <button onclick="moveContent()">移動元素內容</button>

        <script>
          function moveContent() {
            var srcDiv = document.getElementById('srcDiv');
            var distDiv = document.getElementById("distDiv");
            var rangeObj = document.createRange();
            rangeObj.selectNodeContents(srcDiv);
            var documentFragment = rangeObj.extractContents();
            distDiv.appendChild(documentFragment);
          }
        </script>
      </body>
  </html>

檢視效果

2-4 insertNode方法
該方法用於將指定的節點插入到某個Range物件所代表的區域中,插入位置為Range物件所代表區域的起點位置,如果該節點已經存在於頁面之中,那麼該節點
將被移動到Range物件所代表區域的起點處。
使用方法如下:
rangeObj.insertNode(node)

下面是一個demo,頁面中有一個div元素和一個按鈕,div元素有一些文字,在div元素中按下滑鼠左鍵時,該按鈕將被移動到按下滑鼠左鍵的位置。

<!DOCTYPE html>
   <html>
      <head>
        <meta charset="utf-8">
        <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
        <title>標題</title>
      </head>
      <body>
        <div onmouseup="moveButton()" style="width: 400px; background-color: red;">adssdasddsdasszczxccxzcx</div>
        <button id="button">按鈕</button>

        <script>
          function moveButton() {
            var button = document.getElementById("button");
            var selection = document.getSelection();
            if (selection.rangeCount > 0) {
              var range = selection.getRangeAt(0);
              range.insertNode(button);
            }
          }
        </script>
      </body>
  </html>

檢視效果

2-5 理解collapse方法 和 detach方法
該方法用於將Range物件所代表的區域的終點移動到該區域的起點處,或將Range物件所代表的區域的起點移動到終點處,使Range物件所代表的區域內不包含任何內容。
使用方法如下:
RangeObj.collapse(toStart);
引數toStart,是一個Boolean型,如果為false的話,表示將Range物件所代表的區域的起點移動到終點處,當為true的話,表示將Range物件所代表的區域的終點移動到該區域的起點處。
下面是一個demo,可以來理解下 collapse方法的使用;
頁面上有一個div元素,裡面包含一些文字,一個 選擇元素的 按鈕,一個 取消選擇元素的按鈕,和一個 顯示Range內容的按鈕,使用者單擊 選擇元素 按鈕時該Range物件中將包含頁面中的div元素,然後單擊 顯示Range內容按鈕 就彈出 該div元素的內容,再單擊 取消選擇按鈕 將使用Range物件的collapse方法清空Range物件的內容。再次單擊 顯示Range內容,將顯示空字串。
程式碼如下:

<!DOCTYPE html>
       <html>
          <head>
            <meta charset="utf-8">
            <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
            <title>標題</title>
          </head>
          <body>
            <div id="div" style="background-color: red; width:300px; height: 50px;">元素中的內容</div>
            <button onclick="selectRangeContents()">選擇元素</button>
            <button onclick="unselect()">取消選擇</button>
            <button onclick="showRange()">顯示Range內容</button>
            <script>
              var rangeObj = document.createRange();
              function selectRangeContents() {
                var div = document.getElementById('div');
                rangeObj.selectNode(div);
              }
              function unselect() {
                rangeObj.collapse(false);
              } 
              function showRange() {
                alert(rangeObj.toString());
              }
            </script>
          </body>
      </html>

檢視效果

detach方法: 該方法用於從瀏覽器中釋放Range物件,釋放之後將不能再訪問該Range物件,否則將丟擲指令碼錯誤,使用方法如下:
RangeObj.detach();

相關文章