跟随标准与Webkit源码探究DOM -- 获取元素之getElementById
按照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"的元素也一并返回的错误实现增加的说明 - WHATWG 将
getElementById
放到了NonElementParentNode
里,因此实现了NonElementParentNode
的DocumentFragment
也拥有这个方法(而W3C的标准里,DocumentFragment
仅仅继承了Node
,不应该有此方法) - DOM 4 目前与 WHATWG 相同
注意点
- 注意
getElementById
的名字里没有全大写的ID
,而是id
。 - 目前浏览器中
getElementById
仅定义在Document
和DocumentFragment
上,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_elementsById
的getElementById
(WebCore/dom/TreeScope.h, WebCore/dom/TreeScope.cpp)。
DocumentOrderedMap
的getElementById
其实是get
的包装,最终实现参见 WebCore/dom/DocumentOrderedMap.cpp
迭代器的继承结构是 descendantsOfType
-> TypedElementDescendantIteratorAdapter
-> TypedElementDescendantIterator
,TypedElementDescendantIterator<ElementType>::operator++
调用 ElementIterator<ElementType>::traverseNext
,参见TypedElementDescendantIterator.h。
ElementIterator<ElementType>::traverseNext
再调用 Traversal<ElementType>::next
(WebCore/dom/ElementIterator.h),这里多态的ElementTraversal
(继承Traversal
)实际上又调用了NodeTraversal
,NodeTraversal
中 next
的重载利用traverseNextTemplate
实现。最后通过WebCore/dom/NodeTraversal.h可以看出访问顺序是 firstChild -> nextSibling -> nextAncestorSibling,也就是先序DFS,符合WHATWG里的描述。
跟随标准与Webkit源码探究DOM -- 获取元素之getElementById的更多相关文章
- 跟随标准与Webkit源码探究DOM -- 获取元素之querySelector,querySelectorAll
使用CSS选择器获取元素 -- querySelector,querySelectorAll(HTML5) 标准 W3C Selector API Level 1为Document,DocumentF ...
- 跟随标准与Webkit源码探究DOM -- 获取元素之getElementsByClassName
按照类名获取元素 -- getElementsByClassName(HTML5) 标准 WHATWG 在Document与Element上均有定义,原型 HTMLCollection getElem ...
- 跟随标准与Webkit源码探究DOM -- 获取元素之getElementsByTagName
按照标签名获取元素 -- getElementsByTagName 标准 DOM 1在Element和Document两个interface中均有定义,原型NodeList getElementsBy ...
- 跟随标准与Webkit源码探究DOM -- 获取元素之getElementsByName
按照name属性获取多元素 -- getElementsByName 标准 DOM 1 定义在HTMLDocument Interface 中,原型NodeList getElementsByName ...
- ConcurrentHashMap源码探究 (JDK 1.8)
很早就知道在多线程环境中,HashMap不安全,应该使用ConcurrentHashMap等并发安全的容器代替,对于ConcurrentHashMap也有一定的了解,但是由于没有深入到源码层面,很多理 ...
- Vue源码探究-虚拟DOM的渲染
Vue源码探究-虚拟DOM的渲染 在虚拟节点的实现一篇中,除了知道了 VNode 类的实现之外,还简要地整理了一下DOM渲染的路径.在这一篇中,主要来分析一下两条路径的具体实现代码. 按照创建 Vue ...
- Vue源码探究-全局API
Vue源码探究-全局API 本篇代码位于vue/src/core/global-api/ Vue暴露了一些全局API来强化功能开发,API的使用示例官网上都有说明,无需多言.这里主要来看一下全局API ...
- Vue源码探究-事件系统
Vue源码探究-事件系统 本篇代码位于vue/src/core/instance/events.js 紧跟着生命周期之后的就是继续初始化事件相关的属性和方法.整个事件系统的代码相对其他模块来说非常简短 ...
- Vue源码探究-状态初始化
Vue源码探究-状态初始化 Vue源码探究-源码文件组织 Vue源码探究-虚拟DOM的渲染 本篇代码位于vue/src/core/instance/state.js 继续随着核心类的初始化展开探索其他 ...
随机推荐
- [游戏模版1] MFC最小框架(base function including)
>_<:Here are the template of mini-MFC include: CPen,CBrush,Front,Paint Line and some other gra ...
- [备忘][转]rsync使用时的常见问题
sync使用时的常见问题: 错误1: rsync: read error: Connection reset by peer (104) rsync error: error in rsync pro ...
- 虚拟机锁定文件失败,开启模块snapshot失败解决办法
今天由于没有正常关闭虚拟机,导致出现打开虚拟机提示:锁定文件失败 虚拟机开启模块snapshot失败,后来从网上找打了资料解决了.解决办法:一:打开你存放虚拟机系统文件的文件夹,注意,是系统文件,不是 ...
- paip.多维理念 输入法的外码输入理论跟文字输出类型精髓
paip.多维理念 输入法的外码输入理论跟文字输出类型精髓 通常,我们的输入法使用的外码是拼音,但是,这个的用户体验很差.. 应该使用多个外码类型... ##按照词汇来源,有如下几个 固有词ati 来 ...
- 遵循amd规范的require.js(适合浏览器端)
1. 下载require.js 2. 引用 html <!DOCTYPE html> <html lang="en"> <head> <m ...
- Docker实践(1)—入门
tutorial centos6.5环境. # yum install docker-io -y 会依赖安装libcgroup,lxc,lxc-libs 启动docker # service dock ...
- windows server 2008 R2 FTP登陆错误。
建立了一个域用户ftp. 始终登陆不上winserver 2008 R2上的FTP. 错误如下: 530-User cannot log in. Win32 error: Logon failur ...
- oratop 各个指标项说明
Section 1- oratop and database/instance specifics spid :oratop's server SPID connected to inst ...
- iphone/ipad/ipod设置VPN(pptp连接方式)
一.点击桌面上的-设置-图标进入设置(如图) 二.点击-通用-进入通用设置 三.点击-VPN-进入VPN设置(如图) 四.点击添加VPN设置进行设置 五.选择并连接
- Oracle Essbase入门系列(二)
本篇开始会一个三口之家的家庭财务数据库为例,讲述Essbase的功能和开发.为了说明EPM应用程序的管理和开发过程,会绕一些弯路,不使用EAS,而尽量用EPMA. 创建应用程序 首先登陆到Worksp ...