一种基于匹配回朔的 css3 选择器引擎实现
介绍
CSS 选择器是一种应用于 DOM 节点查找场景的特定微型语法, 本质上和正则表达式一样都是一种模式匹配语言,灵活使用可以方便得获取指定位置的节点集合。
目前 W3C 推荐标准为 Selectors Level 3 , 在 ie9+ 以及 firefox,chrome,mobile 等浏览器上原生有基本一致的实现,而在 ie 下则需要 使用 javascript 模拟实现,本文介绍一种基于匹配回朔的 css3 选择器引擎实现,特定应用于 ie6,7,8 下。
语法
css 选择器是一种紧凑的语法,根据 css3 规范一个选择器字符串首先由 ',' 号分割的组组成,例如
s = g1,g2
表示匹配 g1 与 g2 的元素集合。组内又由以 ' ','+','>','~' 分割的简单选择器序列组成,例如
g1 = simple1 + simple2
g1 = simple1 simple2
g1 = simple1 > simple2
g1 = simple1 ~ simple2
+ 表示 simple1 匹配的元素与 simple2 的在同一层级,且 simple2 的元素紧跟在 simple1 后面。
> 表示 simple2 匹配的元素紧跟在 simple1 的下一层级。
' ' 表示 simple1 匹配的元素比 simple2 的层级更靠近根节点。
~ 表示 simple1 匹配的元素与 simple2 的在同一层级,且位置靠前。
简单选择器序列又可以由类型选择器以及后缀选择器组成,例如
simple1 = type_selector suffix_selector
其中 typeselector 表示标签的名称,例如 'h1','h2'。不指定时默认为 '*' 表示匹配任何标签。 suffixselector 则一般用来进一步过滤,例如类选择器(限定类名),属性选择器(限定属性),伪类、伪元素等。
例如 h1.x 匹配 <h1 class='x'> 而不匹配 <h1> 或 <span class='x'> 。
完整语法描述可以查看 w3c 标准页面 。
以下文章为了简单描述,将这种语法抽象为
a.b + c.d ~ e.f
其中 a c e 为类型选择器,b d f 为后缀选择器,+ 代表直接位置关系的 > +,~ 代表模糊位置关系的 ~ ' '.
实现
解析器生成
首先把 css 选择器语法用 LALR 解析程序生成器生成解析程序,从而可以把选择器的字符串格式转换成结构化的数据。 这里采用 kison 来生成。
对应 css 选择器语法的 kison 格式描述为: selector-grammar
生成的解析器代码如下: parser.js
流程图如下:

解析后的结构化数据为双向链表格式,例如
a.b + c.d
解析后的链表为:

引擎查找
接下来的工作就是引擎查找,查找过程比较复杂,下面根据以下流程图结合实例讲解:

举例选择器字符串为:
a.b + c.d ~ a + e.f
匹配节点串为:
e.f a.b c.d e c.d e a e.f
获取种子集合
和一般浏览器实现类似,采用自右向左的查找方法,首先要从最右端 的 type selector 获取到种子集合,根据本例为:
a e.f a.b c.d e c.d e a e.f
^ ^ ^ ^
选择器链表分组
将选择器根据直接位置进行分组,以直接位置相连的简单的选择器序列为一组,分组后
a.b + c.d ~ a + e.f
--------- -------
分组的意义在于,每次匹配都以直接位置相连的组为单元做匹配,回朔时也应当以组为单元回朔(直接位置处回朔无意义)。
初步过滤种子
根据最后的一组的选择器序列:
a + e.f
进一步过滤种子集合,过滤后为:
a e.f a.b c.d e c.d e a e.f
^ ^
1 2
进一步过滤种子
这一步会根据对种子进行进一步过滤,过滤过程中甚至会发生回朔。
例如对于第一个种子,在初步过滤后,节点串游标和选择器游标分别在
a e.f a.b c.d e c.d e a e.f
^
a.b + c.d ~ a + e.f
^
由于节点串游标已经越过节点串头,则表明该次匹配失败,该种子节点匹配失败。
对于第二个种子,在初步过滤后,节点串游标和选择器游标分别在
a e.f a.b c.d e c.d e a e.f
^
a.b + c.d ~ a + e.f
^
由于匹配失败,但选择器链接为 '~' ,则可不移动选择器游标,而只移动节点串游标:
a e.f a.b c.d e c.d e a e.f
^
a.b + c.d ~ a + e.f
^
可继续匹配到:
a e.f a.b c.d e c.d e a e.f
^
a.b + c.d ~ a + e.f
^
此时由于选择器链接为 '+' 因而移动节点串游标已经不可能再次匹配,此时应对选择器游标进行回朔到该分组前面:
a e.f a.b c.d e c.d e a e.f
^
a.b + c.d ~ a + e.f
^
此时仍然匹配不成功,但可以移动节点串游标为:
a e.f a.b c.d e c.d e a e.f
^
a.b + c.d ~ a + e.f
^
此时可以匹配选择器游标到头:
a e.f a.b c.d e c.d e a e.f
^
a.b + c.d ~ a + e.f
^
则表明该种子节点符合本次选择器串,最终匹配节点个数为 1
a e.f a.b c.d e c.d e a e.f
^
引擎代码
引擎代码可参见: selector.js
单元测试
单元测试直接拉取 sizzle 对应于 css3 的部分,经过少量调整,全部通过:
性能测试
欢迎提交新的例子。
一种基于匹配回朔的 css3 选择器引擎实现的更多相关文章
- CSS3 选择器——笔记+实战案例(基本选择器、组合选择器、属性选择器、伪类选择器)
使用CSS3 选择器——笔记 CSS通过选择器控制HTML元素,CSS选择器对网页对象可以实现一对一.一对多或者多对一的匹配. 一.CSS3选择器分类 CSS选择器在CSS2.1选择器的基础上新增了属 ...
- 16种基于 CSS3 & SVG 的创意的弹窗效果
在去年,我给大家分享了<基于 CSS3 的精美模态窗口效果>,而今天我要与大家分享一些新鲜的想法.风格和趋势变化,要求更加适合现代UI的不同的效果.这组新模态窗口效果包含了一些微妙的动画, ...
- Hive数据分析——Spark是一种基于rdd(弹性数据集)的内存分布式并行处理框架,比于Hadoop将大量的中间结果写入HDFS,Spark避免了中间结果的持久化
转自:http://blog.csdn.net/wh_springer/article/details/51842496 近十年来,随着Hadoop生态系统的不断完善,Hadoop早已成为大数据事实上 ...
- 一种基于uCos-II操作系统和lwIP协议栈的IEEE-1588主站以及基于该主站的报文处理方法
主站以及应用于电力系统的支持IEEE‐1588协议的主时钟(IEEE‐1588主站)的实现方法.该方法是在一个低成本的硬件平台上,借助uCos‐II操作系统和TCP/IP的协议栈,对以太网数据进行了分 ...
- [信安Presentation]一种基于GPU并行计算的MD5密码解密方法
-------------------paper--------------------- 一种基于GPU并行计算的MD5密码解密方法 0.abstract1.md5算法概述2.md5安全性分析3.基 ...
- <<一种基于δ函数的图象边缘检测算法>>一文算法的实现。
原始论文下载: 一种基于δ函数的图象边缘检测算法. 这篇论文读起来感觉不像现在的很多论文,废话一大堆,而是直入主题,反倒使人觉得文章的前后跳跃有点大,不过算法的原理已经讲的清晰了. 一.原理 ...
- tmpfs:一种基于内存的文件系统
tmpfs是一种基于内存的文件系统, tmpfs有时候使用rm(物理内存),有时候使用swap(磁盘一块区域).根据实际情况进行分配. rm:物理内存.real memery的简称? 真实内存就是电脑 ...
- 回朔法/KMP算法-查找字符串
回朔法:在字符串查找的时候最容易想到的是暴力查找,也就是回朔法.其思路是将要寻找的串的每个字符取出,然后按顺序在源串中查找,如果找到则返回true,否则源串索引向后移动一位,再重复查找,直到找到返回t ...
- 一种基于重载的高效c#上图片添加文字图形图片的方法
在做图片监控显示的时候,需要在图片上添加文字,如果用graphics类绘制图片上的字体,实现图像上添加自定义标记,这种方法经验证是可行的,并且在visual c#2005 编程技巧大全上有提到,但是, ...
随机推荐
- Android 实现透明效果的 Activity
Android系统提供了将Activity设置为透明的主题:@android:style/Theme.Translucent 该属性同一时候支持隐藏TitleBar和全屏显示. 仅仅须要在Androi ...
- 菜鸟调错(三)——Jboss与jdk版本号不兼容导致WebService调用出错
环境: jdk1.6 Jboss 5.1.0.GA 问题描写叙述: EJB公布webserivce已经成功,而且可以成功訪问wsdl文件: 使用axis1自带的sample/client下的类Dyna ...
- UNIX网络编程读书笔记:recv和send函数
这两个函数类似于标准的read和write函数,不过需要一个额外的参数. #include <sys/socket.h> ssize_t recv(int sockfd, void *bu ...
- vim中翻页的命令
整页翻页 ctrl-f ctrl-b f就是forword b就是backward 翻半页 ctrl-d ctlr-u d=down u=up 滚一行 ctrl-e ctrl-y zz 让光标所杂 ...
- 微信小程序弹窗组件
概述 自己封装的一个比较简单微信弹窗小组件,主要就是教会大家对微信小组件的用法和理解,因为微信小程序对组件介绍特别少,所以我就把自己的理解分享给大家 详细 代码下载:http://www.demoda ...
- Solr4.6删除数据的办法
Solr4.6的管理界面上,假设不配置数据导入的功能,将看不到清除数据的button.我表示非常遗憾,正好我们线上没有配置数据导入的功能. 网上搜到的各种清理solr数据的HTTP请求,拿到我的sol ...
- 每秒处理3百万请求的Web集群搭建-用 LVS 搭建一个负载均衡集群
这篇文章是<打造3百万次请求/秒的高性能服务器集群>系列的第3部分,有关于性能测试工具以及优化WEB服务器部分的内容请参看以前的文章. 本文基于你已经优化好服务器以及网络协议栈的基础之上, ...
- Ubuntu桌面操作快捷键
Ubuntu操作基本快捷键 * 打开主菜单 = Alt + F1 * 运行 = Alt + F2 * 打开终端 = Alt+F2 然后输入gnome-terminal回车 * 显示桌面 = Ctrl ...
- 如何使用Android MediaStore裁剪大图片
译者按:在外企工作的半年多中花了不少时间在国外的网站上搜寻资料,其中有一些相当有含金量的文章,我会陆陆续续翻译成中文,与大家共享之.初次翻译,“信达雅”三境界恐怕只到信的层次,望大家见谅! 这篇文章相 ...
- Java 字符串转为字符串数组
String strSalesStaff_init="李世民,朱元璋,刘秀,李自成"; String[] arrSalesStaff =strSalesStaff_value.sp ...