跟隨標准與Webkit源碼探究DOM -- 獲取元素之getElementsByTagName


按照標簽名獲取元素 -- getElementsByTagName

標准

  • DOM 1在ElementDocument兩個interface中均有定義,原型NodeList getElementsByTagName(in DOMString tagname),指明按照先序遍歷遇到的順序排列,不會拋出任何異常,參數"*"返回對應document或者element下所有元素。注意這里指明返回的是一個live的僅含有ElementNodeList
  • DOM 2里定義仍在ElementDocument,增加了帶namespace的NodeList getElementsByTagNameNS(in DOMString namespaceURI, in DOMString localName)ElementDocument),引入了localName的概念(只有ELEMENT_NODEATTRIBUTE_NODE才能有)。
  • DOM 3(DocumentElement)特別聲明XML應當對標簽名的大小寫敏感,非XML則依照文檔類型自己對待大小寫的風格來決定是否敏感。實際上瀏覽器對HTML都會先將標簽轉統一換成成小寫后再去匹配,所以只能匹配到實際標簽名為小寫的元素。
  • WHATWG (Document ,Element)將返回類型修改為了HTMLCollection,並解釋了通過localName產生HTMLCollection的算法。注意算法的第二步實際上規定了在非HTML文檔里,標簽名大小寫敏感;而在HTML文檔里,任何大小寫的標簽都會被統一轉換成小寫后再去匹配,所以只能匹配到實際標簽名為小寫的元素。
  • DOM 4(DocumentElement) 目前與 WHATWG 基本一致

DOM Tree Accessors

DOM 1DOM 2HTMLDocument interface 里定義了一系列"DOM Tree Accessors"

  • readonly attribute HTMLCollection images
  • readonly attribute HTMLCollection applets
  • readonly attribute HTMLCollection links
  • readonly attribute HTMLCollection forms
  • readonly attribute HTMLCollection anchors
  • attribute HTMLElement body

這意味着在HTML文檔里可以用document.images獲得所有<img>元素,用document.links獲取所有帶有href屬性的<a>元素,用document.links獲取所有帶有name屬性的<a>元素,用document.forms獲取所有<form>元素。另外獲取<body>可以使用document.body

自 HTML5(W3CWHATWG)開始還定義了document.head,刪去了document.anchors,增加了獲取<embed>document.embeds和目前與之相同的document.plugins,以及獲取<script>document.scripts

DOM 1 便在Document 定義了document.documentElement來獲取根元素並保留至今,在HTML文檔里即<html>元素。

兼容性

  • IE 5.5 不支持*作為參數獲取所有元素。IE6以上的IE以及各大瀏覽器均按照標准實現了getElementsByTagName
  • 雖然document.scriptsdocument.embedsdocument.plugins直到HTML5才標准化,不過各版本IE和其他瀏覽器的現行版本都支持
  • document.head需要IE9+才支持。其他DOM Tree Accessor基本在各版本IE和現行的瀏覽器里都有支持。

Webkit 代碼分析

類似getElementsByNamegetElementsByTagNameContainerNode里實現。由於標准里對XML的特殊規定,這里會依據文檔類型,換用TagNodeList或者HTMLTagNodeList作為NodeListsNodeData::addCacheWithAtomicName<>的template specialization(參見WebCore/dom/ContainerNode.cpp)。

TagNodeList實現的elementMatches是:

if (m_localName != starAtom && m_localName != element.localName())
    return false;
return m_namespaceURI == starAtom || m_namespaceURI == element.namespaceURI();

這里starAtom就是標准里說的*。先比對localName是否相符或為*,然后比對namespaceURI是否相符或為*。由於沒有大小寫轉換步驟,所以遵循標准,是大小寫敏感的。注意這里比對namespaceURI的步驟相對於getElementsByTagName是多余的,之所以加上是因為getElementsByTagNameNS也用TagNodeList,這樣就可以偷懶不用再多寫一個比對namespaceURI的版本。不過getElementsByTagNameNS用的其實是addCacheWithQualifiedName而不是addCacheWithAtomicName,其實addCacheWithQualifiedNameaddCacheWithAtomicName的不同也就是它拿TagNodeList直接提前做好了template specification而已(參見WebCore/dom/NodeRareData.h

HTMLTagNodeList實現的elementMatches是:

if (m_localName == starAtom)
    return true;
const AtomicString& localName = element.isHTMLElement() ? m_loweredLocalName : m_localName;
    return localName == element.localName();

按照標准所說,如果被比對的元素是HTML namespace里的,轉換為小寫再比較。注意這里沒有比對namespaceURI,畢竟getElementsByTagNameNS不用它(標准里沒有指明getElementsByTagNameNS需要轉換大小寫,所以用TagNodeList那個大小寫敏感的過濾足矣)。


免責聲明!

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



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