本来是打算参考zepto.js,然后将里面想要的部分抽出来做函数,随调随用。

但后面发现这种写法重复代码太多,代码不整洁,于是就打算模仿下zepto的写法,挑出些比较实用的方法,造一下轮子。

起名叫“iSelector”,已经放到了github上面

简单的做了封装,本来也想使用“$”相关的符号,但看来看去不是很合适,就用大写的“S”替代。

在造轮子的过程中,了解到了以前不知道的Element、Array等相关的方法或属性,这也是种收获。

同时引进了jasmine单元测试工具,通过加载其中的一个组件,就可以测试DOM了。

一、基础概念

1)Element与Node

Node(节点)是DOM层次结构中的任何类型的对象的通用名称,Node有很多类型,常用的如下:

Element继承了Node类,也就是说Element是Node多种类型中的一种,nodeType=1的Node就是Element,并且Element还有很多自己的属性和方法。

2)children与childNodes

Element类中的children返回nodeType为1的子元素集合,该集合为一个即时更新的(live)HTMLCollection

Node类中childNodes返回nodeType为1或3的子元素的集合,该集合为一个即时更新的(live)NodeList

<div id="outter">
<p id="inner">inner</p>
</div>

分别将outter中的两个值打印出,可在线调试,如下所示:

 

3)Element.matches()

matches表示如果当前元素能被指定的css选择器查找到,则返回true,否则返回false。这个方法在不同浏览器中需要使用前缀。


selectorString 是个css选择器字符串,语法与querySelector相同,同样可以在线调试

function matches(element, selector) {
var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.matchesSelector;
return matchesSelector.call(element, selector)
}
var ismatch = matches(inner, "#inner");

4)Array相关方法

Array有众多方法,平时会用slice做数组转换、forEach做迭代,在zepto源码中用到了好几个我平时没怎么用的方法。

concat:将传入的数组或非数组值与原数组合并,组成一个新的数组并返回。

filter:使用指定的函数测试所有元素,并创建一个包含所有通过测试的元素的新数组。

reduce:接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始合并,最终为一个值。

some:测试数组中的某些元素是否通过了指定函数的测试。

every:测试数组的所有元素是否都通过了指定函数的测试。

map:返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组。

二、通用方法

zepto是将普通的元素包装了起来,在原型链中添加了很多方法,而通过“$”查找到的是个数组。

我自己封装的就用“S”来代表“$”符号。

例如$("div")调用上面的html代码,返回是将是下面一个伪数组,如果要变成数组就要手动的做“slice”操作。

1)S.matches

前面讲到过,用来做选择器匹配,对于不支持matches的浏览器,zepto中做了些兼容操作,源码在zepto中的51行左右。

在zepto经常引用的find、filter、is、closest等操作中就会使用这个方法。

2)S.qsa

根据输入的选择器,做匹配查询,使用getElementByIdgetElementsByClassNamegetElementsByTagNamequerySelectorAll

其实通过这个方法,可以选择最合适的匹配方法,选择器的语法也能做到与jQuery类似,源码在zepto中的249行左右。

3)S.extend

通过源对象扩展目标对象属性,源对象属性将覆盖目标对象属性,源码在zepto中的255行左右。

在zepto中的extend中,如果参数是个对象,那么浅复制仅仅是复制一个引用,深复制是复制内容。

var source = {0:1, 3:{4:'a', 5:'b'}};
var target = {};
extend(target, source);//第三个值为true,是深复制
source[3][4] = 'c';
console.log(target);
console.log(source);

上面是浅复制,修改source,target也会改变,左边是target,右边source。

  

如果是深复制,就不会改变。

    

我想简单点使用,就直接修改为浅复制吧,但我会过个hasOwnProperty的判断,过滤原型链上的属性或方法。

function extend(target, source) {
for (key in source) {
if (source[key] !== undefined && source.hasOwnProperty(key))
target[key] = source[key];
}
}

4)S.contains

containsNode类中的方法,返回一个boolean表示传入的节点是否是子节点。

zepto的代码中,还做了兼容性处理,可以直接拿来用,源码在273行左右。

5)S.children

查找子元素的兼容方法,这里面涉及到两个属性,“ParentNode.children”和“Node.childNodes”。

前者返回HTMLCollection集合,可以不用做NodeType的判断。

而后者返回的是NodeList集合,会返回两种类型的NodeType,1和3,所以要做判断过滤。

6)S.map

本来就是想直接用数组的map方法,但是后面在通过map集合后,要做“null”的过滤,并且还要做“concat”操作。

因为map后的集合会出现“[Array[2],Array[3]]”的方式,我只需要一维数组即可。

7)S.init

初始化操作,通过一些逻辑操作,获取HTMLCollection集合,再做“new iSelector”操作,做一层包裹。

三、DOM操作

接下来的方法都是在S.fn内,也就是会放到iSelector.prototype中。

1)filter

一个一维的节点数组,通过特定的函数或选择器过滤后,返回一个新的数组。

在children、siblings、prev和next中也会引用这个方法。

2)find

查找元素的子节点,就是在引用上面的qsa公共方法,在某个元素下面执行querySelectorAll等。

3)closest

返回最先匹配选择器的一个祖先元素,通过循环“parentNode”,再用上面的“S.matches”匹配指定的选择器。

4)children

获取直接子元素,通过公共的“S.children”获取到元素下面的子元素,再通过“filter”过滤指定的选择器。

5)siblings

获取兄弟元素。没有直接的库方法,通过点小技巧,先获取父元素,然后再“S.children”获取到此元素的子元素,再通过“filter”过滤指定的选择器。

6)prev与next

获取上一个元素与下一个元素,这里用到了两个Element类中的属性,“previousElementSibling”和“nextElementSibling

7)before与after

将元素插入到前面与后面,原生方法中只有“insertBefore”,如果要插入到后面就要模拟一下。

通过前面的“next”方法获取当前元素的下一个元素,然后插入到这个元素之前,就达到插入到后面的效果。

四、属性操作

1)html

既可做赋值也可做获取,使用了原生的“innerHTML”。

2)attr与removeAttr

获取属性与移除属性,使用了原生的方法“setAttribute”和“removeAttribute”。

3)setClass

设置与删除元素的class值,Element类中的className可以获取当前元素的class值。

将这个值用“split(/\s+/)”来分割,然后在数组中与输入的做匹配。

4)hasClass

判断元素的class值是否存在,也用到了正则“new RegExp('(^|\\s)'+name+'(\\s|$)')”,用这个来对比是否存在。

灵活运用正则可以简化很多工作,关于正则更多信息可以参考《JavaScript与PHP中正则

五、单元测试

单元测试使用了jasmine,还使用了单元测试的一个组件jasmine-jquery,简单的引用后就可直接使用。

<link rel="stylesheet" type="text/css" href="lib/jasmine.css">
<script src="lib/jasmine.js"></script>
<script src="lib/jasmine-html.js"></script>
<script src="lib/boot.js"></script>
<script src="lib/jquery.js"></script>
<script src="lib/jasmine-jquery.js"></script>
<!-- 被测试的代码 -->
<script src="../js/iSelector.js"></script>
<!-- 测试用例代码 -->
<script src="specs/iSelector_spec.js"></script>

目录结构中fixtures保存是html文件,测试DOM操作的时候,操作的html就是这里面的。

双击index.html就可以看到测试结果。

参考资料:

Zepto.js API 中文版

深入剖析 JavaScript 的深复制

How to forget about jQuery and start using native JavaScript APIs

订制DOM选择器的更多相关文章

  1. 都别说工资低了,我们来一起写简单的dom选择器吧!

    前言 我师父(http://www.cnblogs.com/aaronjs/)说应当阅读框架(jquery),所以老夫就准备开始看了 然后公司的师兄原来写了个dom选择器,感觉不错啊!!!原来自己从来 ...

  2. 关于一个新的DOM选择器querySelector

    在传统的javascript中,提到DOM选择器,大家比较熟悉的方式是通过tag,name,id来获取,其实大家都发现如果获取比较复杂的话,用这个方法会很繁琐,这时大家应该都会想到jquery里获取一 ...

  3. 一周学会Mootools 1.4中文教程:(1)Dom选择器

    利器: 君欲善其事须先利其器,好吧因为我们的时间比较紧迫,只有六天而已,那么六天的时间用死记硬背的方式学会Mt犹如天方夜谭,因此我们需要借鉴一下Editplus的素材栏帮我们记忆就好了,当我们需要用到 ...

  4. JEasyPoi 2.1.4 (Jeecg订制) 版本发布,Excel 和 Word 简易工具类

    JEasyPoi 2.1.4 (jeecg订制)版本发布,EasyPoi Excel 和 Word 简易工具类 easypoi 功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员 ...

  5. 订制rpm包到Centos7镜像中

    本文以CentOS 7.4 最小化镜像(CentOS-7-x86_64-Minimal-1708.iso)为模版 要达到的目的: 1.订制所需的rpm软件包集成到iso文件中 2.制作完成的ISO全自 ...

  6. 订制EditText光标

    订制EditText光标 设置背景android:background="@null" 设置光标样式:android:textCursorDrawable="@drawa ...

  7. XMPP键盘订制实现图文混排

    在现阶段的通信服务中,各种标准都有,因此会出现无法实现相互连通,而XMPP(Extensible Message and presence Protocol)协议的出现,实现了整个及时通信服务协议的互 ...

  8. 【函数】wm_concat包的订制

     [函数]wm_concat包的订制   1  BLOG文档结构图     2  前言部分   2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道 ...

  9. DOM选择器

    DOM选择器分为:id.class.name.tagname.高级.关系选择器;(返回的都是标签) 一:元素节点选择器: 1. id: 返回的是单个对象 <body> <div cl ...

随机推荐

  1. hibernate中SessionFactory与Session的作用

    首先,SessionFactory是线程安全的,SessionFactory用到了工厂模式. 其创建和销毁需要耗费很大的资源,所以一个应用中的一个数据库一般只对应一个sessionfactory. S ...

  2. LabVIEW 吸星大法 - 看见的好东西都是我的(上篇)

    前言 写了多年的LabVIEW程序,你是否面临这样的问题 总是在做一些重复的工作,感觉很没有意思: 总在不停的写代码,做类似的控件,实现相同的功能,丝毫没有成就感: 总在天加班,没有时间去提高自己; ...

  3. 关于兼容IE的一些策略

    --css 盒子模型下的 box-sizing 属性,只兼容到ie8: -moz-box-sizing: border-box; -webkit-box-sizing: border-box; -o- ...

  4. SQL Server最近怎样了

    SQL Server最近怎样了 又到年终了,大家都作最后冲刺 最近园子里真的多了很多口水帖,无论大家争论得多么激烈,时间依然滴答滴答地过,争论完之后我们依然要继续埋头苦干 为年终奖.为明年做准备 这里 ...

  5. 在一个SQL Server表中的多个列找出最大值

    在一个SQL Server表中一行的多个列找出最大值 有时候我们需要从多个相同的列里(这些列的数据类型相同)找出最大的那个值,并显示 这里给出一个例子 IF (OBJECT_ID('tempdb..# ...

  6. SQL Server2016 新功能实时查询统计信息

    SQL Server2016 新功能实时查询统计信息 很多时候有这样的场景,开发抱怨DBA没有调优好数据库,DBA抱怨开发写的程序代码差,因此,DBA和开发都成为了死对头,无法真正排查问题. DBA只 ...

  7. ABP理论学习之开篇介绍

    返回总目录 为了和2016年春节赛跑,完成该系列博客,我牺牲了今天中午的时间来完成该系列的第一篇----开篇介绍.开篇介绍嘛,读过大学教材的同学都知道,这玩意总是那么无聊,跟考试没关系,干脆直接跳过, ...

  8. Java Socket

    什么是Socket Socket的概念很简单,它是网络上运行的两个程序间双向通讯的一端,既可以接收请求,也可以发送请求,利用它可以较为方便地编写网络上数据的传递. 所以简而言之,Socket就是进程通 ...

  9. 剑指Offer面试题:28.连续子数组的最大和

    一.题目:连续子数组的最大和 题目:输入一个整型数组,数组里有正数也有负数.数组中一个或连续的多个整数组成一个子数组.求所有子数组的和的最大值.要求时间复杂度为O(n).例如输入的数组为{1,-2,3 ...

  10. 备忘录--关于线程和IO知识

    因为自己还在出差中,没时间深入学习,最近工作里又有对一些技术的思考,所以这里记录下来,等回去有时间可以按照这个思路进行学习,这里主要起到备忘的作用. 1.线程难学难在我们没有理解操作系统里的线程设计机 ...