按照標簽名獲取元素 -- getElementsByTagName
標准
- DOM 1在
Element和Document兩個interface中均有定義,原型NodeList getElementsByTagName(in DOMString tagname),指明按照先序遍歷遇到的順序排列,不會拋出任何異常,參數"*"返回對應document或者element下所有元素。注意這里指明返回的是一個live的僅含有Element的NodeList。 - DOM 2里定義仍在
Element和Document,增加了帶namespace的NodeList getElementsByTagNameNS(in DOMString namespaceURI, in DOMString localName)(Element,Document),引入了localName的概念(只有ELEMENT_NODE和ATTRIBUTE_NODE才能有)。 - DOM 3(
Document,Element)特別聲明XML應當對標簽名的大小寫敏感,非XML則依照文檔類型自己對待大小寫的風格來決定是否敏感。實際上瀏覽器對HTML都會先將標簽轉統一換成成小寫后再去匹配,所以只能匹配到實際標簽名為小寫的元素。 - WHATWG (
Document,Element)將返回類型修改為了HTMLCollection,並解釋了通過localName產生HTMLCollection的算法。注意算法的第二步實際上規定了在非HTML文檔里,標簽名大小寫敏感;而在HTML文檔里,任何大小寫的標簽都會被統一轉換成小寫后再去匹配,所以只能匹配到實際標簽名為小寫的元素。 - DOM 4(Document,Element) 目前與 WHATWG 基本一致
DOM Tree Accessors
DOM 1 與 DOM 2 的 HTMLDocument interface 里定義了一系列"DOM Tree Accessors"
readonly attribute HTMLCollection imagesreadonly attribute HTMLCollection appletsreadonly attribute HTMLCollection linksreadonly attribute HTMLCollection formsreadonly attribute HTMLCollection anchorsattribute HTMLElement body
這意味着在HTML文檔里可以用document.images獲得所有<img>元素,用document.links獲取所有帶有href屬性的<a>元素,用document.links獲取所有帶有name屬性的<a>元素,用document.forms獲取所有<form>元素。另外獲取<body>可以使用document.body。
自 HTML5(W3C,WHATWG)開始還定義了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.scripts,document.embeds和document.plugins直到HTML5才標准化,不過各版本IE和其他瀏覽器的現行版本都支持 document.head需要IE9+才支持。其他DOM Tree Accessor基本在各版本IE和現行的瀏覽器里都有支持。
Webkit 代碼分析
類似getElementsByName,getElementsByTagName在ContainerNode里實現。由於標准里對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,其實addCacheWithQualifiedName和addCacheWithAtomicName的不同也就是它拿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那個大小寫敏感的過濾足矣)。
