JavaScript框架设计(四) 字符串选择器(选择器模块结束)
JavaScript框架设计(四) 字符串选择器(选择器模块结束)
经过前面JavaScript框架设计(三) push兼容性和选择器上下文的铺垫,实现了在某一元素下寻找,现在终于进入了字符串选择器
首先,我们解决组合选择器,因为在一个选择器字符串中,它的优先级是最低的.
组合选择器
由于组合选择器是一个包含逗号的字符串,我们可以将其用逗号分隔开
下面看代码
01.html
var HPawn = function (selector) {
var res = [];
var list = selector.split(',');// 先将选择器字符串用逗号分隔开
each(list, function (v) {
get(myTrim(v), document, res);
})
return res;
}
在这里,我终于要引出我的关键对象 HPawn了,这个会在后面的面向对象封装中见到.
由于 String.prototype.trim() 在ie8 里面是不支持的,所以自己也要实现一个myTrim.
var myTrim = function (str) {
// 如果本身有自己的get方法就调用,如果没有,就使用自己的myTrim方法.
if (String.prototype.trim) {
str = str.trim();
} else {
str.replace(/^\s|\s$/, "");
}
return str;
}
str.replace(/^\s|\s$/, ""); 这里 是一个简单的正则替换.
下面是测试代码:
01.html
<style>
.c1,.c2,.c3,#dv{
border: 1px solid lightblue;
width: 300px;
height: 60px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<!--实现组合选择器-->
<div class="c1">
<div id="dv"></div>
</div>
<div class="c2"></div>
<div class="c3"></div>
</body>
<script src="common.js"></script>
onload = function () {
each(HPawn('#dv,.c2,.c3,'), function (v) {
this.style.backgroundColor = 'red';
})
}
看效果:

后代选择器
好了,上面已经解决了组合选择器的问题,下面开始后代选择器,选择器模块也在后代选择器之后结束,.
对于后代选择器,类似"div p span .c",我们首先想到的就是使用空格" "分割,得到每一个部分,从每一个部分中找到下面的部分,这就是一个递归的过程.
由于以空格分隔是比较特殊的,所以我们先介绍String.prototype.split() 这个方法.
如图:

在对字符串"1 2 3"(注意,2和3之间是两个空格),我们得到了四个元素,'1', '2'. '', '3',包含一个空字符串.
为什么会这样呢?
我们分析split的原理.
首先,游标从第一个字符开始,发现不是' ',就会到第二个字符' ',发现是' ',砍一刀,将前面的东西push到结果中.再继续走到达'2',发现不是' ',继续向前,到达' ',砍一刀,将前面的push到结果中,再向前走,发现还是' ',再砍一刀,将前面的字符串,也就是空字符串push到结果中...
好了,既然这样,那我们用' '来分割字符串的时候,很有可能会包含空字符串,我们必须做个判断.
看下面的递推式
var str = "l1,l2,l3,l4";
res = getChild(context,l1,0);
res = getChild(res,l2,1);
res = getChild(res,l3,2);
res = getChild(res,l4,3);
详细的理论过程就是上面这样,可以看到,后代选择器有几组,那么就会递归几次.
看下面的代码:
var getChild = function (parent, index) {
// 如果已经超出了范围,就返回父元素数组
if (index >= selectors.length) {
return parent;
}
// 如果父元素没有找到,那么就直接返回空数组
if (!parent || parent.length == 0) {
return [];
}
// 如果选择器为空字符串,那么就直接越过一级查找
if (!selectors[index]) {
index += 2;
return getChild(parent, index);
}
var res = get(selectors[index], parent);
return index == (selectors.length - 1)
? res :
getChild(res, ++index);
}
这里接受两个参数,第一个为父元素dom数组,第二个表示第几级,第一次调用的时候,index就是0.
下面是一个for循环版本,很直观的解释了后代选择器的选择算法.
var parent = context;
for (var i = 0; i < selectors.length; i++) {
if (!selectors[i]) {
continue;
}
// 如果parent为空,就直接不再向后判断
if(!arent){
return [];
}
parent = get(selectors[i],parent);
}
比如说选择器 'div p span',使用空格分开后,得到的就是字符串数组,然后从第一个字符串查找元素下一级元素,一直查找到末尾,然后返回.
现在我们把上面的代码整合
02.js
/// <reference path="common.js" />
var myTrim = function (str) {
// 如果本身有自己的get方法就调用,如果没有,就使用自己的trim方法.
if (String.prototype.trim) {
str = str.trim();
} else {
str.replace(/^\s|\s$/, "");
}
return str;
}
// 在组合选择器的基础上实现后代选择器
var HPawn = function (selector, context, result) {
var result = result || [];
context = context || document;
var list = selector.split(',');// 先将选择器字符串用逗号分隔开
// 首先,应当对每一个被分开的元素进行split(' ')
each(list, function (v) {
v = myTrim(v);
var selectors = v.split(' ');
// 在这里,一个递归
var getChild = function (parent, index) {
// 如果已经超出了范围,就返回父元素数组
if (index >= selectors.length) {
return parent;
}
// 如果父元素没有找到,那么就直接返回空数组
if (!parent || parent.length == 0) {
return [];
}
// 如果选择器为空字符串,那么就直接越过一级查找
if (!selectors[index]) {
index += 2;
return getChild(parent, index);
}
var res = get(selectors[index], parent);
return index == (selectors.length - 1)
? res :
getChild(res, ++index);
}
each(selectors, function (v) {
result.push.apply(result, getChild(context, 0));
})
});
return result;
}
好了,后代选择器已经完成了,下面写个demo.
02.htm
<style>
div {
border: 1px solid green;
padding: 20px;
box-sizing: border-box;
margin-bottom: 30px;
}
.c {
height:300px;
width: 500px;
}
.c1,
.c2,
.c4 {
height: 80px;
width: 400px;
}
.c3,
.c5 {
height: 40px;
width: 200px;
}
</style>
<div class="c">
<div class="c1">
<div class="c3"></div>
</div>
<div class="c2"></div>
</div>
<hr/>
<div class="c4">
<div class="c5"></div>
</div>
</head>
<body>
<script src="common.js"></script>
<script src="02.js"></script>
</body>
02.js
// 这个代码实在是太简洁了,太棒了
onload = function () {
each(HPawn('.c4 .c5,.c .c1 .c3'), function () {
this.style.backgroundColor = 'orange';
})
}
下面是效果

看起来已经大功告成了.
好了,选择器模块我们已经实现了组合选择器和后代选择器.
但是jQuery还有很多,比如 父子选择器,兄弟选择器,下一个,上一个,过滤器. 这些以后还需要再细心钻研啊.
好了,现在我们写一个简单的循环将这两个选择器做一个简化
03.js
var Hpawn = function (selector) {
var result = []
var selectors = selector.split(',');
for (var i = 0, length = selectors.length; i < length; i++) {
var singleSelectors = selectors[i].split(' ');
var parent = document;
for (var j = 0; j < singleSelectors.length; i++) {
parent = get(singleSelectors[j], parent);
}
result.push.apply(result, parent);
}
return res;
}
上面的代码需要用到common.js里面的东西,也就是通用的get方法.
看起来,组合选择器和父子选择器就是这么简单,几句代码就搞定了,确实是,逻辑比较简单.
但是,它只能用作理解,太不严谨了.
好了,选择器模块已经结束.
下面,我会JavaScript框架设计(五)开始一个新的模块:Dom操作模块
最后特别感谢恩师蒋坤老师(jk)对我的知识学习和人生引导的极大帮助,非常感谢他.
JavaScript框架设计(四) 字符串选择器(选择器模块结束)的更多相关文章
- JavaScript框架设计(三) push兼容性和选择器上下文
JavaScript框架设计(三) push兼容性和选择器上下文 博主很久没有更博了. 在上一篇 JavaScript框架设计(二) 中实现了最基本的选择器,getId,getTag和getClass ...
- 偶的《javascript框架设计》终于出版
#cnblogs_post_body p{ text-indent:2em!important; } 历时两年多,我的书终于付梓出版了.应各方面的要求,写软文一篇,隆重介绍一下此书对各位程序员的钱途有 ...
- JS读书心得:《JavaScript框架设计》——第12章 异步处理
一.何为异步 执行任务的过程可以被分为发起和执行两个部分. 同步执行模式:任务发起后必须等待直到任务执行完成并返回结果后,才会执行下一个任务. 异步执行模式:任务发起后不等待任务执行完成,而是马上 ...
- Javascript框架设计思路图
这个系列的随笔都是关于Javascript框架设计一书的读书笔记(作者是司徒正美),不是本人原创!!! 一.简介: 1.市面上主流的JS框架,大多数是由一个个模块组合而成,模块化是大多数让软件所遵循的 ...
- 游戏UI框架设计(四) : 模态窗体管理
游戏UI框架设计(四) --模态窗体管理 我们在开发UI窗体时,对于"弹出窗体"往往因为需要玩家优先处理弹出小窗体,则要求玩家不能(无法)点击"父窗体",这种窗 ...
- JavaScript 框架设计
JavaScript 高级框架设计 在现在,jQuery等框架已经非常完美,以致于常常忽略了JavaScript原生开发,但是这是非常重要的. 所以,我打算写一个简单的框架,两个目的 熟练框架的思想 ...
- JavaScript 框架设计(二)
JavaScript 高级框架设计 (二) 上一篇,JavaScript高级框架设计(一)我们 实现了对tag标签的选择 下来我们实现对id的选择,即id选择器. 我们将上一篇的get命名为getTa ...
- 浅谈JavaScript框架设计
在这个js框架随处乱跑的时代,你是否考虑过写一个自己的框架?下面的内容也许会有点帮助. 一个框架应该包含哪些内容? 1.语言扩展 大部分现有的框架都提供了这部分内容,语言扩展应当是以ECMAScrip ...
- JS框架设计读书笔记之-选择器引擎02
选择器引擎涉及相关概念 概念 以Sizzle的主函数声明为例,来说明引擎的相关概念. function Sizzle(selector, context, results, seed) { //... ...
随机推荐
- 9_bootstrap less 移动端
chrome,firefox提供了"Device Emulation"功能,可模拟常见的各种浏览设备 android ADT或ios Xcode附带的设备模拟器,或第三方在线测试工 ...
- NFSv4的引用,迁移和备份(用户手册 v0.2)
RFC3530 定义了NFS文件系统迁移和引用的管理机制.文件系统定位功能通过fs_location属性向客户端提供文件系统的位置信息.fs_location属 性是一个包含有位置信息的列表,位置信息 ...
- oracle触发器和存储过程的格式
最近接到一个任务要根据一个表来转移另一个表的数据到第三个表.想了想,用决定用触发器+存储过程的方式来做.有些时间没有写存储过程和触发器了,查了一下资料,确定了oracle的触发器和存储过程的格式. 触 ...
- java-JDBC从数据库中读取数据并进行日期民族男女的转换
代码如下: package com.itnba.maya.mysql; import java.sql.*; import java.text.SimpleDateFormat; public cla ...
- tab22
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- web图片识别
<!doctype html><html lang="en"><head> <meta charset="UTF-8" ...
- window.location.href 中文乱码问题。。。。
window.location.href 中文乱码问题.... 要解决此问题需要两次解码, 第一次解码: 是在页面中的js脚本中解码:window.location.href = "save ...
- 《HiWind企业快速开发框架实战》(1)框架的工作原理
<HiWind企业快速开发框架实战>(1)框架的工作原理 1.HiWind架构 HiWind的基本架构如下: 持久层部分:同时为框架本身的业务服务,也为开发人员的自定义业务服务. 逻辑层: ...
- 关于新加坡IT薪酬
很多朋友发邮件或留言问我关于新加坡IT薪酬的问题,由于前段时间比较忙,所以没有及时一一回复,在此表示抱歉. 新加坡IT薪酬范围大概如下(月薪,新加坡币对人民币为1:5): Junior Develop ...
- Intro to CSS 3D transforms
原文地址:Intro to CSS 3D transforms,本文只是翻译了其中的一部分,省去了作者写文章的原因浏览器兼容部分(已经过时) Perspective 元素需要设置需要设置perspec ...