按照ID获取元素 -- getElementById

标准

  • DOM 1,定义在HTMLDocument Interface 中,原型Element getElementById(in DOMString elementId),当不存在拥有对应ID的元素时返回null,该方法不会抛出任何异常。
  • DOM 2,移动到了Document(原HTMLDocument的Parent Interface),原型不变。
  • DOM 3 特别声明浏览器应当使用Attr.isId判断 Attr 是否为 ID,同时加了一句“Attributes with the name "ID" or "id" are not of type ID unless so defined.”,这是针对IE7-会将name为"id"的元素也一并返回的错误实现增加的说明
  • WHATWGgetElementById 放到了 NonElementParentNode里,因此实现了NonElementParentNodeDocumentFragment也拥有这个方法(而W3C的标准里,DocumentFragment仅仅继承了Node,不应该有此方法)
  • DOM 4 目前与 WHATWG 相同

注意点

  • 注意 getElementById 的名字里没有全大写的 ID,而是id
  • 目前浏览器中 getElementById 仅定义在DocumentDocumentFragment上,WHATWG的文档里提到没有添加到Element是为这个特性会挂掉未使用sizzle前的jQuery(<=1.2.6)的单元测试(旧版jQuery使用了elem.getElementById来判断元素是否为Document),参见邮件列表上的讨论
  • 没有插入 DOM (如用appendChild)的元素是无法用该方法搜索到的。由于前面提到的WHATWG与W3C标准的不同,现实中浏览器里的DocumentFragment也可以用此方法搜索元素。
  • 一些浏览器会将带有id的元素创建成全局变量(比如id="foo"的元素会以window.foo出现在javascript runtime),并且为了向后兼容所以一直保留这个特性,但是它不在标准里,而且全局变量很容易被覆盖,应该尽量避免使用。
  • 标准里写明了当存在多个拥有对应 id 的元素时,浏览器的行为是未定义的,但是大多数浏览器都选择返回第一个拥有该 id 的元素。至于什么是“第一个”,就要看浏览器实现中的DOM树是怎么遍历的了。 WHATWG 里描述的是tree-order,即先序的DFS。

    检查方法:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Document</title>
    </head>
    <body>
    <div>
    <div id="foo" class="a"></div>
    </div>
    <div id="foo" class="b"></div>
    </body>
    </html>

    浏览器 console 运行

    document.getElementById('foo')

    Chrome,FireFox及IE均返回class="a"的div,即 WHATWG 中规定的先序DFS。

兼容性

IE7- 会将有与查询id相同的name的元素也算入,因此如果需要兼容 IE7-,可能需要做elem.id === id的判定。

其他

DOM 2 中,id 定义在 HTMLElement上(DOM 3 没有 DOM HTML 标准),WHATWG 将 id 放在了 Element,定义都是attribute DOMString id,并说明了 id 属于global attribute。总之,在 HTML 里只要是元素就可以有id,并且可以通过 elem.id 的方式直接获取。

Webkit 相关代码分析

Document 继承自 TreeScope,即WHATWG里提到的NonElementParentNode (参见WebCore/dom/Document.h),getElementById其实实现在TreeScope里,调用私有变量DocumentOrderedMap指针m_elementsByIdgetElementByIdWebCore/dom/TreeScope.hWebCore/dom/TreeScope.cpp)。

DocumentOrderedMapgetElementById其实是get的包装,最终实现参见 WebCore/dom/DocumentOrderedMap.cpp

迭代器的继承结构是 descendantsOfType -> TypedElementDescendantIteratorAdapter -> TypedElementDescendantIteratorTypedElementDescendantIterator<ElementType>::operator++ 调用 ElementIterator<ElementType>::traverseNext ,参见TypedElementDescendantIterator.h

ElementIterator<ElementType>::traverseNext 再调用 Traversal<ElementType>::nextWebCore/dom/ElementIterator.h),这里多态的ElementTraversal(继承Traversal)实际上又调用了NodeTraversalNodeTraversalnext的重载利用traverseNextTemplate实现。最后通过WebCore/dom/NodeTraversal.h可以看出访问顺序是 firstChild -> nextSibling -> nextAncestorSibling,也就是先序DFS,符合WHATWG里的描述。

跟随标准与Webkit源码探究DOM -- 获取元素之getElementById的更多相关文章

  1. 跟随标准与Webkit源码探究DOM -- 获取元素之querySelector,querySelectorAll

    使用CSS选择器获取元素 -- querySelector,querySelectorAll(HTML5) 标准 W3C Selector API Level 1为Document,DocumentF ...

  2. 跟随标准与Webkit源码探究DOM -- 获取元素之getElementsByClassName

    按照类名获取元素 -- getElementsByClassName(HTML5) 标准 WHATWG 在Document与Element上均有定义,原型 HTMLCollection getElem ...

  3. 跟随标准与Webkit源码探究DOM -- 获取元素之getElementsByTagName

    按照标签名获取元素 -- getElementsByTagName 标准 DOM 1在Element和Document两个interface中均有定义,原型NodeList getElementsBy ...

  4. 跟随标准与Webkit源码探究DOM -- 获取元素之getElementsByName

    按照name属性获取多元素 -- getElementsByName 标准 DOM 1 定义在HTMLDocument Interface 中,原型NodeList getElementsByName ...

  5. ConcurrentHashMap源码探究 (JDK 1.8)

    很早就知道在多线程环境中,HashMap不安全,应该使用ConcurrentHashMap等并发安全的容器代替,对于ConcurrentHashMap也有一定的了解,但是由于没有深入到源码层面,很多理 ...

  6. Vue源码探究-虚拟DOM的渲染

    Vue源码探究-虚拟DOM的渲染 在虚拟节点的实现一篇中,除了知道了 VNode 类的实现之外,还简要地整理了一下DOM渲染的路径.在这一篇中,主要来分析一下两条路径的具体实现代码. 按照创建 Vue ...

  7. Vue源码探究-全局API

    Vue源码探究-全局API 本篇代码位于vue/src/core/global-api/ Vue暴露了一些全局API来强化功能开发,API的使用示例官网上都有说明,无需多言.这里主要来看一下全局API ...

  8. Vue源码探究-事件系统

    Vue源码探究-事件系统 本篇代码位于vue/src/core/instance/events.js 紧跟着生命周期之后的就是继续初始化事件相关的属性和方法.整个事件系统的代码相对其他模块来说非常简短 ...

  9. Vue源码探究-状态初始化

    Vue源码探究-状态初始化 Vue源码探究-源码文件组织 Vue源码探究-虚拟DOM的渲染 本篇代码位于vue/src/core/instance/state.js 继续随着核心类的初始化展开探索其他 ...

随机推荐

  1. ViEmuVS2013-3.2.1 破解

    VS升级到2013后,作为一个Vimer,自然需要更新最新的ViEmu插件,因为现在离了Vim,写代码已经寸步难行了. ViEmu 3.2.1的破解其实和Viemu 3.0.13的破解方法一样.安装前 ...

  2. nginx upstream模块--负载均衡

    Module ngx_http_upstream_module英文文档 upstream模块相关说明1.upstream模块应放于nginx.conf配置的http{}标签内2.upstream模块默 ...

  3. VirtualBox安装Ubuntu教程

    1.VirtualBox虚拟机安装,及VirtualBox安装Ubuntu教程VirtualBox版本为VirtualBox-4.3.12-93733-Win.exe,Ubuntu版本为ubuntu- ...

  4. Atitit.实现继承的原理and方法java javascript .net c# php ...

    Atitit.实现继承的原理and方法java javascript .net c# php ... 1. 实现继承的问题 1 2. 如何拷贝基类方法?采用prototype原型方式,通过冒充对象 1 ...

  5. 配置nginx 高并发 php

    user www www; // nginx在运行时使用哪个账号的权限,每一个服务都以一个普通的账号的权限来运行,不要以root来运行 worker_processes 2; // 开启进程数,CPU ...

  6. web前端基础——初识HTML DOM编程

    1 HTML DOM编程概述 文件对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理HTML的标准编程接口.由于HTML文档被浏览器解析后就是一棵DOM树,要改 ...

  7. 理解与模拟一个简单servlet容器

    servlet接口 使用servlet编程需要实现或者继承实现了javax.servlet.Servlet接口的类,其中定义了5个签名方法: public void init(ServletConfi ...

  8. C#:WebBrowser控件设置代理IP访问网站【附源码】

    软件截图 源码下载 http://download.csdn.net/detail/php_fly/8041731  

  9. IrregularGridCollectionView处理不定宽度的标签cell

    IrregularGridCollectionView处理不定宽度的标签cell 效果 源码 https://github.com/YouXianMing/UI-Component-Collectio ...

  10. Spring3 整合Hibernate3.5 动态切换SessionFactory (切换数据库方言)

    一.缘由 上一篇文章Spring3.3 整合 Hibernate3.MyBatis3.2 配置多数据源/动态切换数据源 方法介绍到了怎么样在Sping.MyBatis.Hibernate整合的应用中动 ...