按照类名获取元素 -- getElementsByClassName(HTML5)

标准

  • WHATWG 在DocumentElement上均有定义,原型 HTMLCollection getElementsByClassName(DOMString classNames),并定义了匹配算法类名的提取算法,注意这里是先从参数里提取出类名作为一个set,然后再开始匹配的。其中指明了在quirks mode下类名大小写不敏感,否则大小写敏感
  • DOM 4(DocumentElement )基本和WHATWG一致

注意点

按照标准里的说法,“Unless otherwise stated, a collection must be live. ”。既然标准没有指明getElementsByClassName的返回值说是static的,那么它返回的HTMLCollection就是live的。

WHATWG描述getElementsByClassName的实现是先从参数里用ASCII空白字符分割出类名,将得到的类名组成一个有序集合之后,将能够匹配集合内所有类名的元素返回。除非在quirks mode否则大小写敏感,DocumentElement上均有定义。

兼容性

  • 属于HTML5新出现的API,IE9+ 及其他浏览器的现行版本均支持

WebKit代码分析

和之前的几个方法一样,在ContainerNode里实现,使用ClassNodeList作为NodeListsNodeData::addCacheWithAtomicName<>的template specialization。之前的一次commitnodeMatches改为了elementMatches,不过github上的mirror里似乎ClassNodeList还没改掉。ClassNodeListnodeMatches过滤标准(参考WebCore/dom/ClassNodeList.h)为:

if (!element->hasClass())
return false;
if (!m_classNames.size())
return false;
// FIXME: DOM4 allows getElementsByClassName to return non StyledElement.
// https://bugs.webkit.org/show_bug.cgi?id=94718
if (!element->isStyledElement())
return false;
return element->classNames().containsAll(m_classNames);

可以看到里面目前有个多余的StyledElement判定……这里首先判断要过滤的元素有没有class或者传入的字符串是否不含有效的class,接着检查元素是否为StyledElement(找不到相关文档,不过Node.h里的enum ConstructionType可以看出哪些元素是StyledElement)。

类名的提取实现在SpaceSplitStringClassNodeListElementData有SpaceSplitStringm_classNames来维护类名集合。这些外部的类通过调用SpaceSplitString::set来更新和设置类名,而SpaceSplitString::set又调用SpaceSplitStringData::create来维护存有集合的私有成员SpaceSplitStringData m_data

SpaceSplitStringData::create有两个重载,外部调用的是SpaceSplitStringData::create (const AtomicString& keyString)。里面首先调用维护缓存的spaceSplitStringTable(实质是一个static的hashmap)来检查是否已经有缓存,如果没有则开始创建集合。 WebCore/dom/SpaceSplitString.cpp里的tokenizeSpaceSplitString实现了将类名字符串tokenize的算法,并且每分解出一个token,就会调用参数tokenProcessorprocessTokenSpaceSplitStringData::create (const AtomicString& keyString)首先以TokenCounter作为tokenProcessor调用tokenizeSpaceSplitString,将类名字符串先扫描一次统计token数目以方便分配内存,接着调用重载的SpaceSplitStringData::create(const AtomicString& keyString, unsigned tokenCount),里面使用TokenAtomicStringInitializer作为tokenProcessor再扫描一次字符串,将每个token的内容保存到分配好的内存里。

判断匹配的算法就比较粗暴了……参见WebCore/dom/SpaceSplitString.cpp的containsAll和WebCore/dom/SpaceSplitString.h的contains会发现,居然是线性查找套线性查找的O(mn)(m和n分别为要查找的类名数和被查找的元素的实际类名数)……不过考虑到应用场景下一般类名很少,多点也就五六个了,被查找的类名可能更少,乘起来mn可能最多也就二三十左右,一般大概不会超过十,此时用高级数据结构带来的overhead可能还大过简单的线性查找,所以这样实现也是合理的。

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

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

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

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

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

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

    按照ID获取元素 -- getElementById 标准 DOM 1,定义在HTMLDocument Interface 中,原型Element getElementById(in DOMStrin ...

  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. RequireJS源码初探

    前两天跟着叶小钗的博客,看了下RequireJS的源码,大体了解了其中的执行过程.不过在何时进行依赖项的加载,以及具体的代码在何处执行,还没有搞透彻,奈何能力不够,只能先记录一下了. RequireJ ...

  2. paip.提升性能---并行多核编程哈的数据结构list,set,map

    paip.提升性能---并行多核编程哈的数据结构list,set,map vector/copyonwritearraylist 都是线程安全的. 或者经过包装的list ::: collection ...

  3. 通过MSSQL连接服务器连接至Oracle数据库

    前言 有很多时候,我们需要MSSQL与Oracle进行跨库查询或数据交互.本篇随笔将阐述如何通过MSSQL的连接服务器连接至Oracle数据库,并且读取数据的示例. 具体步骤 首先需要到Oracle的 ...

  4. XCode升级导致的IAP失败的问题

    应用提交苹果测试之前,一般都会在沙盒环境里进行测试充值.用沙盒的测试帐号进行充值的时候,服务端拿到苹果返回的receipt-data后,向苹果服务端进行校验的时候,如果返回错误码21007则再去沙盒环 ...

  5. 21个高质量的Swift开源iOS App

    原文:21 Amazing Open Source iOS Apps Written in Swift 对Swift初学者来说,学习开源项目,阅读源码是个不错的方法.在这篇文章中,基于对代码质量和排名 ...

  6. Java中BigDecimal的8种舍入模式

    java.math.BigDecimal 不可变的.任意精度的有符号十进制数.BigDecimal 由任意精度的整数非标度值和32位的整数标度(scale)组成. 如果为零或正数,则标度是小数点后的位 ...

  7. [LeetCode] Find Median from Data Stream

    Find Median from Data Stream Median is the middle value in an ordered integer list. If the size of t ...

  8. 【LeetCode】268. Missing Number

    Missing Number Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one ...

  9. android开发常见编程错误总结

    1.设置TextView的文本颜色 1 2 3 TextView tv; ... tv.setTextColor(R.color.white); 其实这样设置的颜色是 R.color.white的资源 ...

  10. Django 源码小剖: URL 调度器(URL dispatcher)

    在刚开始接触 django 的时候, 我们尝试着从各种入门文档中创建一个自己的 django 项目, 需要在 mysite.urls.py 中配置 URL. 这是 django url 匹配处理机制的 ...