JavaScript框架设计(四) 字符串选择器(选择器模块结束)

经过前面JavaScript框架设计(三) push兼容性和选择器上下文的铺垫,实现了在某一元素下寻找,现在终于进入了字符串选择器

首先,我们解决组合选择器,因为在一个选择器字符串中,它的优先级是最低的.


组合选择器

由于组合选择器是一个包含逗号的字符串,我们可以将其用逗号分隔开

下面看代码

01.html

  1. var HPawn = function (selector) {
  2. var res = [];
  3. var list = selector.split(',');// 先将选择器字符串用逗号分隔开
  4. each(list, function (v) {
  5. get(myTrim(v), document, res);
  6. })
  7. return res;
  8. }

在这里,我终于要引出我的关键对象 HPawn了,这个会在后面的面向对象封装中见到.

由于 String.prototype.trim() 在ie8 里面是不支持的,所以自己也要实现一个myTrim.

  1. var myTrim = function (str) {
  2. // 如果本身有自己的get方法就调用,如果没有,就使用自己的myTrim方法.
  3. if (String.prototype.trim) {
  4. str = str.trim();
  5. } else {
  6. str.replace(/^\s|\s$/, "");
  7. }
  8. return str;
  9. }

str.replace(/^\s|\s$/, ""); 这里 是一个简单的正则替换.

下面是测试代码:

01.html

  1. <style>
  2. .c1,.c2,.c3,#dv{
  3. border: 1px solid lightblue;
  4. width: 300px;
  5. height: 60px;
  6. margin-bottom: 20px;
  7. }
  8. </style>
  9. </head>
  10. <body>
  11. <!--实现组合选择器-->
  12. <div class="c1">
  13. <div id="dv"></div>
  14. </div>
  15. <div class="c2"></div>
  16. <div class="c3"></div>
  17. </body>
  18. <script src="common.js"></script>
  1. onload = function () {
  2. each(HPawn('#dv,.c2,.c3,'), function (v) {
  3. this.style.backgroundColor = 'red';
  4. })
  5. }

看效果:


后代选择器

好了,上面已经解决了组合选择器的问题,下面开始后代选择器,选择器模块也在后代选择器之后结束,.

对于后代选择器,类似"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);

详细的理论过程就是上面这样,可以看到,后代选择器有几组,那么就会递归几次.

看下面的代码:

  1. var getChild = function (parent, index) {
  2. // 如果已经超出了范围,就返回父元素数组
  3. if (index >= selectors.length) {
  4. return parent;
  5. }
  6. // 如果父元素没有找到,那么就直接返回空数组
  7. if (!parent || parent.length == 0) {
  8. return [];
  9. }
  10. // 如果选择器为空字符串,那么就直接越过一级查找
  11. if (!selectors[index]) {
  12. index += 2;
  13. return getChild(parent, index);
  14. }
  15. var res = get(selectors[index], parent);
  16. return index == (selectors.length - 1)
  17. ? res :
  18. getChild(res, ++index);
  19. }

这里接受两个参数,第一个为父元素dom数组,第二个表示第几级,第一次调用的时候,index就是0.

下面是一个for循环版本,很直观的解释了后代选择器的选择算法.

  1. var parent = context;
  2. for (var i = 0; i < selectors.length; i++) {
  3. if (!selectors[i]) {
  4. continue;
  5. }
  6. // 如果parent为空,就直接不再向后判断
  7. if(!arent){
  8. return [];
  9. }
  10. parent = get(selectors[i],parent);
  11. }

比如说选择器 'div p span',使用空格分开后,得到的就是字符串数组,然后从第一个字符串查找元素下一级元素,一直查找到末尾,然后返回.

现在我们把上面的代码整合

02.js

  1. /// <reference path="common.js" />
  2. var myTrim = function (str) {
  3. // 如果本身有自己的get方法就调用,如果没有,就使用自己的trim方法.
  4. if (String.prototype.trim) {
  5. str = str.trim();
  6. } else {
  7. str.replace(/^\s|\s$/, "");
  8. }
  9. return str;
  10. }
  11. // 在组合选择器的基础上实现后代选择器
  12. var HPawn = function (selector, context, result) {
  13. var result = result || [];
  14. context = context || document;
  15. var list = selector.split(',');// 先将选择器字符串用逗号分隔开
  16. // 首先,应当对每一个被分开的元素进行split(' ')
  17. each(list, function (v) {
  18. v = myTrim(v);
  19. var selectors = v.split(' ');
  20. // 在这里,一个递归
  21. var getChild = function (parent, index) {
  22. // 如果已经超出了范围,就返回父元素数组
  23. if (index >= selectors.length) {
  24. return parent;
  25. }
  26. // 如果父元素没有找到,那么就直接返回空数组
  27. if (!parent || parent.length == 0) {
  28. return [];
  29. }
  30. // 如果选择器为空字符串,那么就直接越过一级查找
  31. if (!selectors[index]) {
  32. index += 2;
  33. return getChild(parent, index);
  34. }
  35. var res = get(selectors[index], parent);
  36. return index == (selectors.length - 1)
  37. ? res :
  38. getChild(res, ++index);
  39. }
  40. each(selectors, function (v) {
  41. result.push.apply(result, getChild(context, 0));
  42. })
  43. });
  44. return result;
  45. }

好了,后代选择器已经完成了,下面写个demo.

02.htm

  1. <style>
  2. div {
  3. border: 1px solid green;
  4. padding: 20px;
  5. box-sizing: border-box;
  6. margin-bottom: 30px;
  7. }
  8. .c {
  9. height:300px;
  10. width: 500px;
  11. }
  12. .c1,
  13. .c2,
  14. .c4 {
  15. height: 80px;
  16. width: 400px;
  17. }
  18. .c3,
  19. .c5 {
  20. height: 40px;
  21. width: 200px;
  22. }
  23. </style>
  24. <div class="c">
  25. <div class="c1">
  26. <div class="c3"></div>
  27. </div>
  28. <div class="c2"></div>
  29. </div>
  30. <hr/>
  31. <div class="c4">
  32. <div class="c5"></div>
  33. </div>
  34. </head>
  35. <body>
  36. <script src="common.js"></script>
  37. <script src="02.js"></script>
  38. </body>

02.js

// 这个代码实在是太简洁了,太棒了

  1. onload = function () {
  2. each(HPawn('.c4 .c5,.c .c1 .c3'), function () {
  3. this.style.backgroundColor = 'orange';
  4. })
  5. }

下面是效果

看起来已经大功告成了.


好了,选择器模块我们已经实现了组合选择器和后代选择器.

但是jQuery还有很多,比如 父子选择器,兄弟选择器,下一个,上一个,过滤器. 这些以后还需要再细心钻研啊.

好了,现在我们写一个简单的循环将这两个选择器做一个简化

03.js

  1. var Hpawn = function (selector) {
  2. var result = []
  3. var selectors = selector.split(',');
  4. for (var i = 0, length = selectors.length; i < length; i++) {
  5. var singleSelectors = selectors[i].split(' ');
  6. var parent = document;
  7. for (var j = 0; j < singleSelectors.length; i++) {
  8. parent = get(singleSelectors[j], parent);
  9. }
  10. result.push.apply(result, parent);
  11. }
  12. return res;
  13. }

上面的代码需要用到common.js里面的东西,也就是通用的get方法.

看起来,组合选择器和父子选择器就是这么简单,几句代码就搞定了,确实是,逻辑比较简单.

但是,它只能用作理解,太不严谨了.

好了,选择器模块已经结束.

下面,我会JavaScript框架设计(五)开始一个新的模块:Dom操作模块

最后特别感谢恩师蒋坤老师(jk)对我的知识学习和人生引导的极大帮助,非常感谢他.

JavaScript框架设计(四) 字符串选择器(选择器模块结束)的更多相关文章

  1. JavaScript框架设计(三) push兼容性和选择器上下文

    JavaScript框架设计(三) push兼容性和选择器上下文 博主很久没有更博了. 在上一篇 JavaScript框架设计(二) 中实现了最基本的选择器,getId,getTag和getClass ...

  2. 偶的《javascript框架设计》终于出版

    #cnblogs_post_body p{ text-indent:2em!important; } 历时两年多,我的书终于付梓出版了.应各方面的要求,写软文一篇,隆重介绍一下此书对各位程序员的钱途有 ...

  3. JS读书心得:《JavaScript框架设计》——第12章 异步处理

    一.何为异步   执行任务的过程可以被分为发起和执行两个部分. 同步执行模式:任务发起后必须等待直到任务执行完成并返回结果后,才会执行下一个任务. 异步执行模式:任务发起后不等待任务执行完成,而是马上 ...

  4. Javascript框架设计思路图

    这个系列的随笔都是关于Javascript框架设计一书的读书笔记(作者是司徒正美),不是本人原创!!! 一.简介: 1.市面上主流的JS框架,大多数是由一个个模块组合而成,模块化是大多数让软件所遵循的 ...

  5. 游戏UI框架设计(四) : 模态窗体管理

    游戏UI框架设计(四) --模态窗体管理 我们在开发UI窗体时,对于"弹出窗体"往往因为需要玩家优先处理弹出小窗体,则要求玩家不能(无法)点击"父窗体",这种窗 ...

  6. JavaScript 框架设计

    JavaScript 高级框架设计 在现在,jQuery等框架已经非常完美,以致于常常忽略了JavaScript原生开发,但是这是非常重要的. 所以,我打算写一个简单的框架,两个目的 熟练框架的思想 ...

  7. JavaScript 框架设计(二)

    JavaScript 高级框架设计 (二) 上一篇,JavaScript高级框架设计(一)我们 实现了对tag标签的选择 下来我们实现对id的选择,即id选择器. 我们将上一篇的get命名为getTa ...

  8. 浅谈JavaScript框架设计

    在这个js框架随处乱跑的时代,你是否考虑过写一个自己的框架?下面的内容也许会有点帮助. 一个框架应该包含哪些内容? 1.语言扩展 大部分现有的框架都提供了这部分内容,语言扩展应当是以ECMAScrip ...

  9. JS框架设计读书笔记之-选择器引擎02

    选择器引擎涉及相关概念 概念 以Sizzle的主函数声明为例,来说明引擎的相关概念. function Sizzle(selector, context, results, seed) { //... ...

随机推荐

  1. iOS 中 ARC 项目 兼容 MRC

    iOS 项目中MRC 和 ARC 项目的代码兼容问题: 1.ARC 项目中导入 MRC 第三方类的时候要在此类上添加 -objc-arc. 2.MRC 项目中导入 ARC 类的时候要在次类上添加 -f ...

  2. ajax函数封装

    function ajax(url, fnSucc, fnFaild) { //1.创建Ajax对象 if(window.XMLHttpRequest)//必须加window否则ie报错 { var ...

  3. Jquery学习插件之手风琴

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  4. C#使用HttpWebRequest 进行请求,提示 基础连接已经关闭: 发送时发生错误。

    本人今天遇到的错误,C#使用HttpWebRequest 进行请求,提示 基础连接已经关闭: 发送时发生错误. 测试了很久,才发现,是安全协议问题,把安全协议加上就可以了

  5. 用jdbc访问大段文本数据

    package it.cast.jdbc; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.F ...

  6. 一个哥们看到数据库日志不断增大 [log_reuse_wait_desc]为replication 之后的做法

    一哥们看到数据库日志不断增大 [log_reuse_wait_desc]为replication 之后的做法 一天那个哥们看到数据库日志暴涨,用sys.databases 视图看一下[log_reus ...

  7. ABP理论学习之依赖注入

    返回总目录 本篇目录 什么是依赖注入 传统方式产生的问题 解决办法 依赖注入框架 ABP中的依赖注入基础设施 注册 解析 其他 ASP.NET MVC和ASP.NET Web API集成 最后提示 什 ...

  8. Spring Rabbitmq HelloWorld实例

    之前的博客和大家分享了Rabbitmq的基本框架,及其工作原理,网址为 < http://www.cnblogs.com/jun-ma/p/4840869.html >.今天呢,想和大家一 ...

  9. Hadoop学习笔记—15.HBase框架学习(基础实践篇)

    一.HBase的安装配置 1.1 伪分布模式安装 伪分布模式安装即在一台计算机上部署HBase的各个角色,HMaster.HRegionServer以及ZooKeeper都在一台计算机上来模拟. 首先 ...

  10. 在C#代码中应用Log4Net(一)简单使用Log4Net

    首先让我们先把Log4Net跑起来,示例代码在文章最后面可以下载 1.先把Log4Net引入到工程中,为了演示方便,我们先建立一个winform程序.在程序的根目录下面,建立一个Libs文件夹,以便存 ...