zoukankan      html  css  js  c++  java
  • 跟随标准与Webkit源码探究DOM -- 获取元素之getElementsByClassName

    按照类名获取元素 -- 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可能还大过简单的线性查找,所以这样实现也是合理的。

  • 相关阅读:
    PE文件简介
    hook键盘驱动中的分发函数实现键盘输入数据的拦截
    遍历系统中加载的驱动程序以及通过设备对象指针获取设备对象名称
    如何利用git shell提交代码到github
    驱动开发中的常用操作
    3.1_栈_顺序存储结构(数组形式)
    2.6_链表深入
    2.5_线性表的链式存储结构_双向链表
    2.4_线性表的链式存储结构_单链表具体实现
    2.3_线性表的链式存储结构_单链表
  • 原文地址:https://www.cnblogs.com/joyeecheung/p/4122953.html
Copyright © 2011-2022 走看看