读Zepto源码之Selector模块
Selector
模块是对 Zepto
选择器的扩展,使得 Zepto
选择器也可以支持部分 CSS3
选择器和 eq
等 Zepto
定义的选择器。
在阅读本篇文章之前,最好先阅读《读Zepto源码之神奇的$》。
读 Zepto 源码系列文章已经放到了github上,欢迎star: reading-zepto
源码版本
本文阅读的源码为 zepto1.2.0
GitBook
辅助方法
visible
function visible(elem){
elem = $(elem)
return !!(elem.width() || elem.height()) && elem.css("display") !== "none"
}
判断元素是否可见。
可见的标准是元素有宽或者高,并且 display
值不为 none
。
filters
var filters = $.expr[':'] = {
visible: function(){ if (visible(this)) return this },
hidden: function(){ if (!visible(this)) return this },
selected: function(){ if (this.selected) return this },
checked: function(){ if (this.checked) return this },
parent: function(){ return this.parentNode },
first: function(idx){ if (idx === 0) return this },
last: function(idx, nodes){ if (idx === nodes.length - 1) return this },
eq: function(idx, _, value){ if (idx === value) return this },
contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this },
has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this }
}
定义了一系列的过滤函数,返回符合条件的元素。这些过滤函数会将集合中符合条件的元素过滤出来,是实现相关选择器的核心。
- visible: 过滤可见元素,匹配
el:visible
选择器 - hidden: 过滤不可见元素, 匹配
el:hidden
选择器 - selected: 过滤选中的元素,匹配
el:selected
选择器 - checked: 过滤勾选中的元素,匹配
el:checked
选择器 - parent: 返回至少包含一个子元素的元素,匹配
el:parent
选择器 - first: 返回第一个元素,匹配
el:first
选择器 - last: 返回最后一个元素,匹配
el:last
选择器 - eq: 返回指定索引的元素,匹配
el:eq(index)
选择器 - contains: 返回包含指定文本的元素,匹配
el:contains(text)
- has: 返回匹配指定选择器的元素,匹配
el:has(sel)
process
var filterRe = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*'),
function process(sel, fn) {
sel = sel.replace(/=#\]/g, '="#"]')
var filter, arg, match = filterRe.exec(sel)
if (match && match[2] in filters) {
filter = filters[match[2]], arg = match[3]
sel = match[1]
if (arg) {
var num = Number(arg)
if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '')
else arg = num
}
}
return fn(sel, filter, arg)
}
process
方法是根据参数 sel
,分解出选择器、伪类名和伪类参数(如 eq
、 has
的参数),根据伪类来选择对应的 filter
,传递给回调函数 fn
。
分解参数最主要靠的是 filterRe
这条正则,正则太过复杂,很难解释,不过用正则可视化网站 regexper.com,可以很清晰地看到,正则分成三大组,第一组匹配的是 :
前面的选择器,第二组匹配的是伪类名,第三组匹配的是伪类参数。
sel = sel.replace(/=#\]/g, '="#"]')
这段是处理 a[href^=#]
的情况,其实就是将 #
包在 ""
里面,以符合标准的属性选择器,这是 Zepto
的容错能力。 这个选择器不会匹配到 filters
上的过滤函数,最后调用的是 querySelectorAll
方法,具体见《读Zepto源码之神奇的$》对 qsa
函数的分析。
if (match && match[2] in filters) {
filter = filters[match[2]], arg = match[3]
sel = match[1]
...
}
match[2]
也即第二组匹配的是伪类名,也是对应 filters
中的 key
值,伪类名存在于 filters
中时,则将选择器,伪类名和伪类参数存入对应的变量。
if (arg) {
var num = Number(arg)
if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '')
else arg = num
}
如果伪类的参数不可以用 Number
转换,则参数为字符串,用正则将字符串前后的 "
或 '
去掉,再赋值给 arg
.
return fn(sel, filter, arg)
最后执行回调,将解释出来的参数传入回调函数中,将执行结果返回。
重写的方法
qsa
var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches,
childRe = /^\s*>/,
classTag = 'Zepto' + (+new Date())
zepto.qsa = function(node, selector) {
return process(selector, function(sel, filter, arg){
try {
var taggedParent
if (!sel && filter) sel = '*'
else if (childRe.test(sel))
taggedParent = $(node).addClass(classTag), sel = '.'+classTag+' '+sel
var nodes = oldQsa(node, sel)
} catch(e) {
console.error('error performing selector: %o', selector)
throw e
} finally {
if (taggedParent) taggedParent.removeClass(classTag)
}
return !filter ? nodes :
zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) }))
})
}
改过的 qsa
调用的是 process
方法,在回调函数中处理大部分逻辑。
思路是通过选择器获取到所有节点,然后再调用对应伪类的对应方法来过滤出符合条件的节点。
处理选择器,根据选择器获取节点
var taggedParent
if (!sel && filter) sel = '*'
else if (childRe.test(sel))
taggedParent = $(node).addClass(classTag), sel = '.'+classTag+' '+sel
var nodes = oldQsa(node, sel)
如果选择器和过滤器都不存在,则将 sel
设置 *
,即获取所有元素。
如果 >sel
形式的选择器,查找所有子元素。
这里的做法是,向元素 node
中添加唯一的样式名 classTag
,然后用唯一样式名和选择器拼接成子元素选择器。
最后调用原有的 qsa
函数 oldQsa
来获取符合选择器的所有元素。
清理所添加的样式
if (taggedParent) taggedParent.removeClass(classTag)
如果存在 taggedParent
,则将元素上的 classTag
清理掉。
调用对应的过滤器,过滤元素
return !filter ? nodes :
zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) }))
如果没有过滤器,则将所有元素返回,如果存在过滤器,则遍历集合,调用对应的过滤器获取元素,并将新集合的元素去重。
matches
zepto.matches = function(node, selector){
return process(selector, function(sel, filter, arg){
return (!sel || oldMatches(node, sel)) &&
(!filter || filter.call(node, null, arg) === node)
})
}
matches
也是调用 process
方法,这里很巧妙地用了 ||
和 &&
的短路操作。
其实要做的事情就是,如果可以用 oldMatches
匹配,则使用 oldMatches
匹配的结果,否则使用过滤器过滤出来的结果。
系列文章
- 读Zepto源码之代码结构
- 读Zepto源码之内部方法
- 读Zepto源码之工具函数
- 读Zepto源码之神奇的$
- 读Zepto源码之集合操作
- 读Zepto源码之集合元素查找
- 读Zepto源码之操作DOM
- 读Zepto源码之样式操作
- 读Zepto源码之属性操作
- 读Zepto源码之Event模块
- 读Zepto源码之IE模块
- 读Zepto源码之Callbacks模块
- 读Zepto源码之Deferred模块
- 读Zepto源码之Ajax模块
- 读Zepto源码之assets模块
参考
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
作者:对角另一面
读Zepto源码之Selector模块的更多相关文章
- 读Zepto源码之Touch模块
大家都知道,因为历史原因,移动端上的点击事件会有 300ms 左右的延迟,Zepto 的 touch 模块解决的就是移动端点击延迟的问题,同时也提供了滑动的 swipe 事件. 读 Zepto 源码系 ...
- 读Zepto源码之Gesture模块
Gesture 模块基于 IOS 上的 Gesture 事件的封装,利用 scale 属性,封装出 pinch 系列事件. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...
- 读Zepto源码之IOS3模块
IOS3 模块是针对 IOS 的兼容模块,实现了两个常用方法的兼容,这两个方法分别是 trim 和 reduce . 读 Zepto 源码系列文章已经放到了github上,欢迎star: readin ...
- 读Zepto源码之Fx模块
fx 模块为利用 CSS3 的过渡和动画的属性为 Zepto 提供了动画的功能,在 fx 模块中,只做了事件和样式浏览器前缀的补全,没有做太多的兼容.对于不支持 CSS3 过渡和动画的, Zepto ...
- 读Zepto源码之fx_methods模块
fx 模块提供了 animate 动画方法,fx_methods 利用 animate 方法,提供一些常用的动画方法.所以 fx_methods 模块依赖于 fx 模块,在引入 fx_methods ...
- 读Zepto源码之Stack模块
Stack 模块为 Zepto 添加了 addSelf 和 end 方法. 读 Zepto 源码系列文章已经放到了github上,欢迎star: reading-zepto 源码版本 本文阅读的源码为 ...
- 读Zepto源码之Form模块
Form 模块处理的是表单提交.表单提交包含两部分,一部分是格式化表单数据,另一部分是触发 submit 事件,提交表单. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...
- 读Zepto源码之Data模块
Zepto 的 Data 模块用来获取 DOM 节点中的 data-* 属性的数据,和储存跟 DOM 相关的数据. 读 Zepto 源码系列文章已经放到了github上,欢迎star: reading ...
- 读Zepto源码之Ajax模块
Ajax 模块也是经常会用到的模块,Ajax 模块中包含了 jsonp 的现实,和 XMLHttpRequest 的封装. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...
随机推荐
- C++算法接口使用参考
C++算法接口参考 算法参考:[algorithm] 编译:g++ -std=c++11 xxx_algorithm.cpp 运行:./a.out 1.保持原序列运算 all_of template ...
- Window检测网络连接情况
Window检测网络连接情况 #include "Wininet.h" #pragma comment(lib,"Wininet.lib") DWORD fla ...
- Zepto源码分析(二)奇淫技巧总结
Zepto源码分析(一)核心代码分析 Zepto源码分析(二)奇淫技巧总结 目录 * 前言 * 短路操作符 * 参数重载(参数个数重载) * 参数重载(参数类型重载) * CSS操作 * 获取属性值的 ...
- SQL 创建存储过程,让主键自增
1. 首先创建存储过程: 2. 然后分别创建序列,生成基金公司编号.基金代码.活期账号.理财账号.基金账户.合同号.要求如下: 基金公司编号,字母K+5位数字. 基金代码,字母V+6位数字. 活期 ...
- SSM简明教程:简单的十步教你搭建人生第一个SSM框架[ SSM框架整合教程(Spring+SpringMVC+MyBatis) ]
SSM_BookSystem SSM框架基础 SSM_BookSystem ---> Hello CRUD 说明:本项目目前包含基础的CRUD 日期:2017-05-01 22:25:37 作者 ...
- Spring Date Jpa on update current_timestamp 自动维护创建时间和更新时间
在数据库里设置默认值current_timestamp可以维护创建时间,设置on update current_timestamp 可以维护更新时间.在JPA中应该如何去做呢?这里还是以上篇Topic ...
- virtualbox下正确虚拟机修改设备名称
在学习大数据管理过程中,想要修改虚拟机的设备名称(因为名称太长),所以就直接在右上角的设置中找到详细设置,直接修改设备名称,结果启动Hadoop失败!!!! 后来参考网上问题解决弄好了,现在给出修改设 ...
- Java架构师系统培训高并发分布式电商实战activemq,netty,nginx,redis dubbo shiro jvm虚拟机视频教程下载
15套java架构师.集群.高可用.高可扩 展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布 式项目实战视频教程 ...
- 常用Git操作
--------------------git-------------------- 1.简介 1.Git是一款免费.开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目.[1] ...
- Spark Mllib框架1
1. 概述 1.1 功能 MLlib是Spark的机器学习(machine learing)库,其目标是使得机器学习的使用更加方便和简单,其具有如下功能: ML算法:常用的学习算法,包括分类.回归.聚 ...