DOM擴展學習筆記


對DOM的兩個主要擴展是Selectors API(選擇符API)和HTML5,還有一個不太矚目的Element Traversal元素遍歷規范為DOM添加了一些屬性,另外還有一些專有擴展。

  • 選擇符API
  • 元素遍歷
  • HTML5
  • 專有擴展

選擇符API

讓瀏覽器原生支持css查詢,原理就是所有實現這一功能的JavaScript庫都會寫一個基礎的CSS解析器,然后再使用已有的DOM方法查詢文檔並找到匹配的節點。當把這個功能變成原生API后,解析和樹查詢操作可以在瀏覽器內部通過編譯后的代碼來完成,極大改善性能。
Selectors API Level1核心兩個方法:querySelector和querySelectorAll。在兼容的瀏覽器中可通過Document,Element,DocumentFragment類型實例(方法繼承自Document.prototype和Element.prototype和DocumentFragment.prototype)調用它們。兼容性如圖,反正大部分支持。

Selectors API Level 2規范為Element類型新增了一個方法matchesSelector()。各瀏覽器支持不統一。
querySelector()方法:接收一個CSS選擇符,返回與該模式匹配的第一個元素,如果沒有找到返回null。通過Document類型調用querySelector方法時,會在文檔元素范圍內查找匹配的元素,通過Element類型調用querySelector方法時只會在該元素后代元素范圍內查找匹配的元素。如果傳入了不被支持的選擇符,querySelector()會拋出錯誤。

//取得類為“button”的第一個圖像元素
var img= document.body.querySelector('img.button');

querySelectorAll()方法:返回的所有而不僅僅是一個元素。這個方法返回的是一個NodeList類型實例而且是靜態集合。querySelectorAll優點就是返回的值實際上是帶有所有屬性和方法的NodeList實例,而其底層實現則類似於一組元素的快照,而非不斷對文檔進行搜索的動態查詢,這樣實現可以避免使用NodeList對象通常會引起大多數性能上的問題。如果沒有找到匹配的元素,返回空的NodeList實例(類數組)。如果傳入了不被支持的選擇符,querySelectorAll()會拋錯。

//取得所有p元素中的所有strong元素
var strongs = document.querySelectorAll('p strong'); 


要取得返回的NodeList實例中的每一個元素,可使用item()方法,也可以使用方括號語法。

var i, len, strong;
for(i = 0,len = strongs.length; i<len; i++){
   strong = strongs[i];// 或strongs.item(i)
   strong.className = "test";
}

matchesSelector()方法:接受一個css選擇符參數,如果調用元素與該選擇符相匹配,返回true。

if(document.body.matchesSelector("body.page1")){
   //true
}

截至現在三大主流瀏覽器還沒有支持matchesSelector方法,但IE9+msMatchesSelector()支持該方法,FF3.6+通過mozMatchesSelector()支持該方法,Safari5+和Chrome通過webkitMatchesSelector()支持該方法。這些方法均在Element.prototype上。編寫一個包裝函數兼容:

function matchesSelector(ele, selector){
   if(ele.matchesSelector){
      return ele.matchesSelector(selector);
   }else if(ele.msMatchesSelector){
      return ele.msMatchesSelector(selector);
   }else if(ele.mozMatchesSelector){
      return ele.mozMatchesSelector(selector);
   }else if(ele.webkitMatchesSelector){
      return ele.webkitMatchesSelector(selector);
   }else{
      throw new Error("Not supported");
   }
}

if(matchesSelector(document.body, 'body.page1')){
   //true
}

 

元素遍歷

對於元素間空格,<=IE8會忽略HTML元素中文本節點,其他瀏覽器會返回這個文本節點。這樣就導致在使用childNodes和firstChild等屬性時行為不一致,Element Traversal元素遍歷規范定義了一組屬性。元素遍歷規范為DOM元素添加添加了以下5個屬性:均繼承自Element.prototype或Document.prototype或DocumentFragment.prototype。
Element.prototype

Document.prototype只繼承這三個方法

DocumentFragment.prototype也只繼承這三個方法

  • childElementCount:返回子元素(不包括文本節點和注釋)個數
  • firstElementChild:指向第一個子元素;firstChild的元素版
  • lastElementChild:指向最后一個子元素;lastChild元素版
  • previousElementSibling:指向前一個同輩元素;previousSibling元素版
  • nextElementSibling:指向后一個同輩元素;nextSibling元素版

跨瀏覽器遍歷某元素所有子元素:

var child = ele.firstChild;
while(child!=ele.lastChild){
   if(child.nodeType == 1){
      //元素節點
   }
   child = child.nextSibling;
}

使用Element Traversal代碼會更簡潔,支持元素遍歷方法的瀏覽器有IE9+,FF3.5+,Safari4+,Chrome,Opera10+

var child = ele.firstElementChild;
while(child != ele.lastElementChild){
  //元素節點
  child = child.nextElementSibling;
}

 

HTML5

HTML5規范圍繞如何使用新增標記定義大量JS API,其中一些API與DOM重疊,定義了瀏覽器應該支持的DOM擴展。

  • 與類相關的擴充:HTML4中class屬性用得越來越多,一方面可以通過它為元素添加樣式,另一方面可以用它表示元素語義。自然就有很多JS代碼操作CSS類,如動態修改類或搜索文檔中具有給定類或一組類的元素。HTML5新增了一些API簡化了css類的用法。
    (1).getElementsByClassName()方法:在Document.prototype和Element.prototype上。它是通過既有的DOM功能實現的,而原生的實現具有極大的性能優勢。
    該方法接收一個參數,即一個包含一或多個類名的字符串,返回HTMLCollection類型集合。傳入多個類名時類名的先后順序不重要。
    //取得所有類中包含“username”和“current”的元素,類名先后順序無所謂
    var eles = document.getElementsByClassName("username current");
    
    //取得ID為“myDiv”的元素中帶有類名“selected”的所有元素
    var eles = document.getElementById('myDiv').getElementsByClassName('selected');
    兼容性:

    (2).classList屬性:在操作類名時,需要通過className(繼承自Element.prototype)屬性添加刪除替換類名,但缺點是className是一個字符串,所以即使只修改字符串一部分,也必須每次都設置整個字符串的值。比如刪除某個類名:
    //刪除"user"類
    
    var classNames = ele.className.split(/\s+/);
    
    var idx = classNames.indexOf('user');
    
    classNames.splice(idx, 1);
    
    ele.className = classNames.join(' ');

    比如添加類名需要通過字符串拼接,而且后續要檢測確定不會多次添加相同類名。
    HTML5新增一種操作類名的方式,為所有元素添加了classList屬性(繼承自Element.prototype)。這個classList屬性是DOMTokenList的實例,來學習一下這個接口:原型鏈繼承關系為:ele.classList.__proto__->DOMTokenList.prototype->Object.prototype。

    add(value):將給定的字符串值添加到列表中,如果值已存在就不添加了。

    contains(value):表示列表中是否存在給定的值,如果存在則返回true,否則返回false。
    remove(value):從列表中刪除給定的字符串,並即時反映在文檔中。
    toggle(value):如果列表中已經存在給定的值,刪除它;如果列表中沒有給定的值,添加它。並且即時反映到文檔中。
    有了classList屬性,除非你需要全部刪除所有類名,或者完全重寫元素的class屬性,否則也用不到classList屬性。
    兼容性:不太好

  • 焦點管理:輔助管理DOM焦點的功能
    (1).document.activeElement:始終會引用DOM中當前獲得焦點的元素,繼承自Document.prototype。元素獲得焦點的方式有頁面加載,用戶輸入(通常是按Tab鍵,能響應tab鍵的有帶有超鏈接的a元素,表單元素等)和在代碼中調用focus()(focus能被調用成功的有帶有超鏈接的a元素,表單元素等,沒有document.body元素,p,div元素那些)。
    默認情況下文檔加載期間document.activeElement為null,文檔剛剛加載完成時document.activeElement中保存的是document.body元素引用。
    兼容性:

    (2).document.hasFocus():確定文檔是否獲得了焦點(可以是文檔內的某些元素),繼承自Document.prototype。這里要注意不要在頁面加載后在控制台輸入document.hasFocus()測試,會返回false的,因為此刻你的鼠標焦點在控制台並不是頁面文檔。所以在瀏覽器加載文檔代碼中測試:
    <script type="text/javascript">
      document.getElementById('a').focus();
      alert(document.activeElement);
      alert(document.hasFocus());
    </script>
    通過檢測文檔是否已經獲得了焦點可以知道用戶是不是正在與頁面交互。
  • HTMLDocument的變化:HTML5擴展了HTMLDocument,增加了新功能。原型鏈繼承關系為:document.__proto__->HTMLDocument.prototype->Document.prototype->
    (1).readyState屬性:IE4最早為document對象引入了readyState屬性(繼承自Document.prototype)。Document的readyState屬性有兩個可能值:
    loading:正在加載文檔
    complete:已經加載完文檔
    通過它實現一個指示文檔加載完成的指示器,在這個屬性得到廣泛支持之前要實現這樣一個指示器須借助onload事件處理設置一個標簽。
    if(document.readyState == "complete"){
      //執行操作
    }
    (2).兼容模式:自IE6開始區分渲染頁面的模式是標准的還是混雜的,IE為此給document添加一個compatMode屬性(繼承自Document.prototype)。標准模式下值為“CSS1Compat”,混雜模式下值為“BackCompat”。
    (3).head屬性:作為對document.body引用文檔的<body>元素的補充,HTML5引入document.head屬性(繼承自Document.prototype)。由於IE8不支持該屬性,所以兼容來寫
    var head = document.head || document.getElementsByTagName('head')[0];
  • 字符集屬性:HTML5新增了幾個與文檔字符集有關的屬性。均繼承自Document.prototype
    charset表示文檔中實際使用的字符集,也可用來指定新字符集。可以通過meta標簽,響應頭部或直接設置charset屬性修改這個值。
    defaultCharset:表示根據默認瀏覽器及操作系統的設置,當前文檔默認的字符集應該是什么
  • 自定義數據屬性:HTML5規定可以為元素添加非標准的屬性,但要加前綴“data-”,目的是為元素提供與渲染無關的信息,或者提供語義信息。可通過元素的dataset屬性訪問自定義屬性的值(繼承自HTMLElement.prototype),該值是一個DOMStringMap的實例即一個名值對的映射。原型鏈繼承關系為:ele.dataset.__proto__->DOMStringMap.prototype->Object.prototype

    可以設置ele.dataset.attr='str'並會即時反映到頁面中。當需要給元素添加一些不可見數據以便進行其他處理,就要用到自定義數據屬性。
  • 插入標記:在需要給文檔插入大量新的HTML標記情況下,通過DOM操作仍很麻煩因為要創建一系列節點還要按正確順序鏈接。相比而言使用插入標記的技術,直接插入HTML字符串不僅更簡單速度也更快。以下與插入標記相關的DOM擴展已納入H5規范:
    (1).innerHTML屬性:Chrome中繼承自Element.prototype,IE中繼承自HTMLElement.prototype
    讀模式:innerHTML返回與調用元素的子節點(包括元素,注釋,文本節點)對應的HTML標記
    寫模式:innerHTML會根據指定的值創建新的DOM樹,然后用這個DOM樹完全替換調用元素原先的所有子節點。
    注意:不同瀏覽器返回的文本格式會有所不同,早期IE和Opera將所有標簽轉換為大寫形式,Safari,Chrome和FF會按原先文檔中格式返回,包括空格和縮進。不要指望所有瀏覽器返回的innerHTML值完全相同。
    在寫模式下:innerHTML會被解析為DOM子樹,替換調用元素原來的子節點。因為它的值默認被認為是HTML,所以其中所有標簽都會按照瀏覽器處理HTML的標准方式轉換為元素(轉換結果也因瀏覽器而異)。如果設置的值僅是文本而沒有HTML標簽,那么結果就是設置純文本。
    為innerHTML設置的包含HTML字符串值與解析后innerHTML的值大不相同。例如:

    這是因為為innerHTML設置HTML字符串后瀏覽器會將這個字符串解析為相應的DOM樹,因此設置了之后再讀取HTML字符串會得到與設置時不一樣的結果,原因在於返回的字符串是根據原始HTML字符串創建的DOM樹經過序列化之后的結果。

    使用innerHTML存在一些限制,如下:
    1.<script>:多數瀏覽器中通過innerHTML插入<script>標簽並不會執行其中腳本。但<=IE9是唯一能在這種情況下執行腳本的瀏覽器但必須滿足以下兩點條件:
    必須為<script>元素指定defer屬性(繼承自HTMLScriptElement.prototype),這個屬性的用途表明腳本在執行時不會影響頁面的構造,即腳本會被延遲到整個頁面都解析完后再運行,相當於告訴瀏覽器立即下載但延遲執行。但總是先於DOMContentLoaded事件觸發前執行。HTML5明確規定defer屬性只適用於外部腳本文件,因此支持HTML5的實現會忽略給嵌入腳本設置的defer屬性。IE4~IE7還支持對嵌入腳本的defer屬性,但IE8之后版本完全支持H5規定的行為。在XHTML文檔中要把defer屬性設為defer="defer"。
    必須位於”有作用域“元素之后,因為<script>元素被認為是無作用域的元素,也就是在頁面中看不到的元素,與<style>元素或注釋類似。如果通過innerHTML插入的字符串開頭就是一個”無作用域的元素“,那么IE會在在解析這個字符串前先刪除該元素。
    div.innerHTML = "<script defer>console.log('hi')</script>";// 無效

    因為此時innerHTML字符串一開始就是一個”無作用域元素“,所以這個字符串會變成空字符串。
    解決方案:

    //在<script>前面添加一個有作用域的元素
    div.innerHTML = "<div>&nbsp;</div><script defer>alert('hi')<\/script>";
    
    //添加文本節點
    div.innerHTML = "_<script defer>alert('hi')<\/script>";
    
    //添加一個沒有結束標簽的元素
    div.innerHTML = "<input type=\”hidden\“><script defer>alert('hi')<\/script>"
    第一行在添加有作用域元素的時候,如果只插入一個空的<div>元素還是不行,必須要包含一點內容瀏覽器才會創建文本節點。第一行和第二行都會創建無用的文本節點你可能還得手動移除。第三行代碼使用的是一個隱藏的<input>域也能達到相同效果,由於隱藏的<input>域不影響頁面布局這種方式大多情況首選。
    IE9在innerHTML = "<script defer>...</script>"也能執行,不用修改作用域
    2.<style>:大多瀏覽器都支持以直觀方式通過innerHTML插入<style>元素,如:
    document.body.innerHTML = "<style type=\"text/css\">body{background:red;}</style>";

    但在<=IE8中<style>也是一個一個沒有作用域的元素,因此可以通過下面方式給它前置作用域

    document.body.innerHTML = "_<script type=\"text/css\">body{background:red;}</style>" 
    這樣會將body元素內容清空然后設置顏色。注意在<=IE9中給head元素用此innerHTML會報錯,會提示該操作的目標原件無效。
    3.並不是所有元素都支持設置innerHTML,可能有些老瀏覽器不支持,如上面的<=IE9無法設置,但可以取到innerHTML的值。
    4.FF對內容類型為application/xhtml+xml的XHTML文檔中設置innerHTML有嚴格限制,在XHTML文檔中使用innerHTML時XHTML代碼必須完全符合要求,如果代碼格式不正確,設置innerHTML將會靜默失敗。

    無論何時,使用innerHTML從外部插入HTML都應首先以可靠方式處理HTML,IE為此提供了window.toStaticHTML()方法,該方法接收一個字符串參數,返回一個經無害處理后的版本(從源HTML中刪除所有腳本節點和事件處理程序屬性)

    (2).outerHTML屬性:
    讀模式下:返回調用它的元素及所有子節點的HTML標簽
    寫模式:根據指定HTML字符串創建新的DOM子樹,然后用這個DOM子樹完全替換調用元素。不過由於瀏覽器解析和解釋HTL標記不同,結果也可能不同。
    div.outerHTML = "<p>this is a paragraph</p>";
    
    //等價於
    var p = document.createElement('p');
    p.appendChild(document.createTextNode('this is a paragraph'));
    div.parentNode.replaceChild(p, div);

    <=FF7不支持outerHTML屬性。

    (3).insertAdjacentHTML()方法:Chrome繼承自Element.prototype,IE繼承自HTMLELement.prototype。兩個參數:插入位置和要插入的HTML文本。
    第一個參數必須是下列值之一:CHrome下必須為駝峰式大小寫,IE無所謂都行
    beforeBegin:在當前元素之前插一個緊鄰的同輩元素。
    afterEnd:在當前元素之后插一個緊鄰的同輩元素。
    afterBegin:在當前元素之下插入一個新的子元素或在第一個子元素之前插入新的子元素。
    beforeEnd:在當前元素之下插入一個新的子元素或在最后一個子元素之后再插入新的子元素。
    第二參數是HTML字符串(與innerHTML和outerHTML的值相同)如果瀏覽器無法解析該字符串會拋錯
    //作為前一個同輩元素插入
    ele.insertAdjacentHTML('beforeBegin', "<p>Hello</p>");
    
    //作為后一個同輩元素插入
    ele.insertAdjacentHTML('afterEnd', "<p>Hello</p>");
    
    //作為第一個子元素插入
    ele.insertAdjacentHTML('afterBegin', "<p>Hello</p>");
    
    //作為最后一個子元素插入
    ele.insertAdjacentHTML('beforeBegin', "<p>Hello</p>");

    兼容性: 

    (4).內存與性能問題:innerHTML或outerHTML替換節點會導致瀏覽器內存占用問題,在IE中問題更明顯。
    在刪除帶有事件處理程序或引用了其他JavaScript對象子樹時就有可能導致內存占用問題。假設某個元素有一個事件處理程序(或引用了一個JavaScript對象作為屬性),在使用前述某個屬性將該元素從文檔中刪除后,元素與事件處理程序(或JavaScript對象)之間的綁定關系在內存中並沒有一並刪除。如果這種情況頻繁出現頁面占用內存熟練會明顯增加。因此在使用innerHTML,outerHTML,insertAdjacentHTML方法時最好先手工刪除要被替換的元素所有事件處理程序和JavaScript對象屬性。
    不過,innerHTML還是有優點的,在插入大量新HTML標記使用innerHTML屬性與通過多次DOM操作先創建節點再指定它們之間的關系相比效率要高很多。這是因為在設置innerHTML或outerHTML時會創建一個HTML解析器。這個解析器是在瀏覽器級別的代碼(通常是C++編寫)基礎上運行的,因此比執行JavaScript快的多。但不可避免,創建和銷毀HTML解析器也會帶來性能損失,所以最好能將設置innerHTML或outerHTML的次數控制在合理范圍。例如:
    for(var i=0;i<len;i++){
      ul.innerHTML +='<li>'+i+'</li>'; // 要避免這種頻繁操作
    }

    這種每次循環都設置一次innerHTML做法很低效,而且每次循環還要從innerHTML中讀取一次信息,意味着每次循環要訪問兩次innerHTML。最好做法是單獨構建字符串后再一次性將結果字符串賦給innerHTML

    var itemHtml ='';
    for(var i=0;i<len;i++){
      itemHtml += '<li>'+i+'</li>';
    }
    ul.innerHTML = itemHtml;
  • scrollIntoView()方法:HTML5采用scrollIntoView()作為標准方法,Chrome繼承自Element.prototype,IE繼承自HTMLElement.prototype。該方法可以在所有HTML元素上調用,通過滾動瀏覽器窗口或某個容器元素,調用元素就可以出現在視口中。如果給這個方法傳入true作為參數或不傳參數,那么該方法調用后會讓調用元素頂部與視口頂部盡可能平齊。如果傳入false,調用元素會盡可能全部出現在視口(可能的話調用元素底部會與視口頂部平齊)。實際上為某個元素設置焦點也會導致瀏覽器滾動並顯示出獲得焦點的元素。

 

專有擴展

  • 文檔模式:IE8引入“文檔模式”的概念,頁面的文檔模式決定了可以使用什么功能,即文檔模式決定你可以使用哪個級別css,可以在JS中使用哪些API以及如何對待文檔類型(doctype)。到了IE9,公有以下四種文檔模式:
    IE5:以混雜模式渲染頁面(IE5的默認模式就是混雜模式),IE8及更高版本中的新功能都無法使用。
    IE7:以IE7標准模式渲染頁面。IE8及更高版本中的新功能都無法使用。
    IE8:以IE8標准模式渲染頁面。IE8中的新功能都可使用,因此可以使用Selectors API,更多CSS2選擇符合某些CSS3功能,還有一些HTML5功能。不過IE9中新功能無法使用。
    IE9:以IE9標准模式渲染頁面。IE9中新功能可用,比如ECMAScript5,完整的CSS3以及更多HTML5,這個文檔模式是最高級模式。
    要強制瀏覽器以某種模式渲染頁面,可以使用HTTP頭部信息X-UA-Compatible,或通過等價的<meta>標簽來設置:
    <meta http-equiv="X-UA-Compatible" content="IE=IEVersion">

    這里IE的版本有以下不同的值而且這些值不一定與上述4種文檔模式對應:
    忽略文檔類型聲明的版本:
    Edge:始終以最新文檔模式渲染頁面,對於IE8始終保持以IE8標准模式渲染頁面,對於IE9始終保持以IE9標准模式渲染頁面。
    9:強制以IE9標准模式渲染頁面。
    8:強制以IE8標准模式渲染頁面。
    7:強制以IE7標准模式渲染頁面。
    5:強制將文檔模式設置為IE5。
    不忽略文檔類型聲明的版本:
    EmulateIE9:如果有文檔類型聲明,以IE9標准模式渲染頁面,否則將文檔模式設置為IE5。
    EmulateIE8:如果有文檔類型聲明,以IE8標准模式渲染頁面,否則將文檔模式設置為IE5。
    EmulateIE7:如果有文檔類型聲明,以IE7標准模式渲染頁面,否則將文檔模式設置為IE5。

    <!--想讓文檔模式像在IE7中一樣 -->
    <meat http-equiv="X-UA-Compatible" content="IE=EmulateIE7">
    
    <!--不打算考慮文檔類型聲明,直接使用IE7標准模式-->
    <meat http-equiv="X-UA-Compatible" content="IE=7">

    沒有規定說必須在頁面中設置X-UA-Compatible,默認情況下瀏覽器會通過文檔類型聲明確定是使用最佳的可用文檔模式還是混雜模式。通過document.documentMode屬性可知給定頁面使用的是什么文檔模式,這個屬性只在IE中有(繼承自Document.prototype)它返回文檔模式的版本號。

  • children屬性:由於<=IE8與其他瀏覽器再處理文本節點中空白符有差異,因此出現children屬性(Chrome繼承自Document.prototype或Element.prototype,IE繼承自HTMLElement.prototype),這個屬性是HTMLCollection實例,只包含是元素的子節點。IE不支持document.children,但可以ele.children。在<=IE8children屬性中會包含注釋節點,但IE9之后版本則只返回元素節點。
  • contains()方法:某個節點是不是另一個節點的后代,IE為了率先引入contains方法,一遍不通過在DOM文檔樹中查找即可獲得這個信息。使用:祖先node.contains(后代node),如果被檢測節點是后代節點返回true。該方法在Chrome中繼承自Node.prototype(document和ele均可用),在IE中繼承自HTMLELement.prototype(只能ele用)。
    DOM Level3 compareDocumentPosition()也能確定節點間關系,繼承自Node.prototype。支持這個方法的有IE9+和其他瀏覽器。這個方法返回表示關系的掩碼
    掩碼 節點關系
    1 無關(給定節點不在當前文檔中)
    2 居前(給定節點在DOM樹中位於參考節點之前)
    4 居后(給定節點在DOM樹中位於參考節點之后)
    8 包含(給定節點是參考節點祖先)
    16 被包含(給定節點是參考節點后代)

    為模仿contains()方法,關注掩碼16,我們可以對compareDocumentPosition的結果進行按位與,以確定參考節點是否包含給定節點
    var result = document.documentElement.compareDocumentPosition(document.body);
    console.log(result);// 20
    console.log(!!(result&16));// true

    result結果並不是16而是20是因為居后4和被包含16之和。20化為二進制位10100,16化為二進制10000,按位與結果為10000(16),!!兩個邏輯非操作會將該數值轉化為布爾值。這里說一下為什么要和16按位與,我覺得是因為按位與是都是1&1才是1,其余是0,所以當節點關系不是包含關系而是其他關系(掩碼總是<16)所以小於16的數與10000怎樣與都是返回0的。
    寫一個通用的contains函數:

    /*
    判斷某節點是否是另外一個節點的后代的兼容性代碼
    refNode:參照節點
    targetNode:要檢查的節點
    */
    function contains(refNode, targetNode){
      if(typeof refNode.contains == 'function'){
        return refNode.contains(targetNode);
      }else if(typeof refNode.compareDocumentPosition == 'function'){
        return !!(refNode.compareDocumentPosition(targetNode)&16);
      }else{
        var node = targetNode.parentNode;
        do{
          if(node == refNode){
            return true;
          }else{
            node = node.parentNode;
          }
        }while(node!== null);
        return false;
      }
    }
  • 插入文本:IE原來專有的插入標記的屬性innerHTML和outerHTML已經被H5納入規范,但innerText和outerText卻沒有。
    (1).innerText屬性:操作元素中包含的所有文本內容,包括子文檔樹中的文本。繼承自HTMLElement.prototype。在通過innerText讀取值時它會按由淺入深順序將子文檔樹中所有文本拼接起來。寫入值時結果會刪除元素的所有子節點插入包含相應文本值的文本節點,由於不同瀏覽器處理空白符的方式不同因此輸出的文本可能會也可能不會包含原始HTML代碼中的縮進。

    可以通過innerText屬性過濾掉它原本的HTML標簽
    ele.innerText = ele.innerText;

    FF低版本不支持不支持innerText,但支持作用類似的textContent屬性(繼承自Node.prototype),textContent是DOM Level3規定的一個屬性,其他支持textContent屬性的瀏覽器還有IE9+和其他主流瀏覽器。
    兼容性代碼:

    function getInnerText(ele){
       return (typeof ele.textContent == 'string')?ele.textContent:ele.innerText;
    }
    
    function setInnerText(ele, str){
      if(typeof ele.textContent == 'string'){
         ele.textContent = str;
      }else{
         ele.innerText = str;
      }
    }

    <=IE8的innerText會忽略行內樣式和腳本。因此避免從包含行內樣式或行內腳本的DOM子樹副本或片段中讀取文本。 

    (2).outerText屬性:繼承自HTMLElement.prototype,作用范圍擴大到包含調用它的節點,寫模式下它會替換整個元素(包括子節點)
    div.outerText ="xxx";
    
    //等價於
    var text = document.createTextNode('xxx');
    div.parentNode.replaceChild(text, div);
    由於該屬性會導致調用它的原始不存在,所以不建議使用。
  • 滾動:scrollIntoViewIfNeeded(aligncenter):Chrome繼承Element.prototype。只在當前元素在視口中不可見的情況下才滾動瀏覽器窗口或容器元素最終讓它可見。如果元素在當前視口中本身可見這個方法什么也不做。如果將可選的alignCenter參數設置為true則表示盡量將元素顯示在視口中部(垂直方向),只有Chrome和Safari實現了該方法。
    需要注意的是scrollIntoView()和scrollIntoViewIfNeeded()的作用對象是元素的容器。

 

參考

《JavaScript高級程序設計》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM