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

博主很久没有更博了.

在上一篇 JavaScript框架设计(二) 中实现了最基本的选择器,getId,getTaggetClass ,并且解决了document.getElementsByClassName() 这个函数在ie8下面的兼容性问题,似乎以前的代码已经没问题了? 但是, 注意这一句代码: result.push.apply(result,document.getElementsByTageName),result.push.apply 方法接受的是一个伪数组,由于apply要求第一个参数之后的参数必须用数组包装起来,因此我们巧妙地使用apply对后面的 document.getElementsByTageName 伪数组进行展开.但是,ie8下面是不行的,在ie8里面,这里只能接受真数组.

所以我们实现一个自己的push

02.js

var myPush = function (target, eles) {
var j = target.length,
i = 0;
while (target[j++] = eles[i++]) { };
target.length = j - 1;
}

所以我们原来的代码可以改成这样:

var getTag = function (tag, result) {
result = result || [];
try {
result.push.apply(result, document.getElementsByTagName(tag));
}
catch (e) {
myPush(result, document.getElementsByTagName(tag));
}
return result;
}

似乎这样是可以的,但是 由于try catch是错误异常处理,是非常消耗性能的!!!,所以,要做就只能做一次,然后将Array原型上面的push改为我们自己的push 那么我们要做的就是和document.getElementsByClassName() 一样,首先便检测它是否可用.

那么我们修改我们的push方法

common.js

push = {
apply: function (target, eles) {
var j = target.length,
i = 0;
while (target[j++] = eles[i++]) { };
target.length = j - 1;
}
}

为什么要这么做? 因为push方法一定会有一个apply调用模式,我们用这个对象来模拟push.apply操作.

然后,看下面的代码

try {
var div = document.createElement('div');
div.innerHTML = "<div></div><div></div>";
var divs = div.getElementsByTagName('div');
var result = [];
result.push.apply(result, divs);
} catch (e) {
Array.prototype.push = push;
}

这样一来,如果push报错,那么我们就直接在Array的原型的push方法替换为我们自己的方法,以后所有的调用都不会再报错了.


好了,push的兼容性问题已经解决,下面开始进入选择器的上下文问题

我们将以前所有的代码都整合,放在common.js里面,后面所有的修改都是在common$.js上面修改.

实现查找某一个dom对象下的元素

我们想实现一个功能:

get(selector,父元素) 即在这个父元素下查找指定元素.父元素可以是dom对象,dom数组,选择器字符串.

第一步,我们假定父元素就是dom对象.

那么,我们要先修改的就是 getId,getTag,getClass 这三个方法.

修改如下,

common1.js

var getTag = function (tag, context, result) {
result = result || [];
context = context || document;
result.push.apply(result, context.getElementsByTagName(tag));
return result;
} var getId = function (id, result) {
result = result || [];
result.push(document.getElementById(id));
return result;
} var getClass = function (className, context, result) {
result = result || [];
context = context || document;
// 首先判断我们的docoument.getElementsByClassName() 有没有这个功能
var res;
if (support.getElementsByClassName) {
res = context.getElementsByClassName(className);
} else {
// 自己实现getElementByClassName
// 思路 : 首先获得所有元素,然后再在所有元素中获得带有这个类的元素
res = myGetByClassName(className, context); }
result.push.apply(result, res);
return result;
}

注意,由于context用的会比results多,所以我们可以将context放置为第二个参数.

而getElementById() 这个方法只有document对象有,所以我们不考虑.

然后我们修改get方法:

var get = function (selector, context, result) {
result = result || [];
context = context || document;
var rquickExpr = /^(?:#([\w-]+)|\.([\w-]+)|([\w]+)|(\*))$/,
// var rquickExpr = /^(?: (#[\w-]+) | (\.[\w-]+) | ([\w]+) | (\*) )$/;
m = rquickExpr.exec(selector);
if (!m) {
return result;
}
if (m[1]) {
// 注意 在这里全部换位context
result = getId(m[1], result);
} else if (m[2]) {
result = getClass(m[2], context, result);
} else {
result = getTag(m[3] || '*', context, result)
}
return result;
};

下面是测试代码

03.html

    <style>
.c div {
border: 1px solid green;
height: 50px;
width: 300px;
margin-bottom: 30px;
} .c1 {
border: 1px solid green;
height: 50px;
width: 300px;
margin-bottom: 30px;
}
</style>
</head> <body>
<div class="c">
<div></div>
<div></div>
</div>
<hr>
<div>
<div class="c1"></div>
</div>
<script src="common1.js"></script>
<script>
onload = function(){
each(get('div',get('.c')[0]),function(){
this.style.backgroundColor = "green";
});
}
</script>
</body>

下面是效果

先睡觉了,明天继续.


上下文context 为一个dom数组

好了,上面已经解决了上下文是一个dom对象的问题,但是这样有一个问题,就是我每次只能查询一个元素下面的,如果我想查找多个呢?

很简单,如果上下文是一个数组,我们可以对这个数组进行循环,对数组中的每一个元素执行上面的操作.

那么,由于 我们单独的get方法都是通过通用的get方法来调用,那么这个操作只需要在通用的get方法里面完成就可以了.

common2.js

var get = function (selector, context, result) {
result = result || [];
context = context || document;
// 如果不是数组,那我们就让它成为一个数组.
if (context.nodeType) {
context = [context];
}
var rquickExpr = /^(?:#([\w-]+)|\.([\w-]+)|([\w]+)|(\*))$/,
// var rquickExpr = /^(?: (#[\w-]+) | (\.[\w-]+) | ([\w]+) | (\*) )$/;
m = rquickExpr.exec(selector);
each(context, function (v) {
if (!m) {
return;
}
if (m[1]) {
result = getId(m[1], result);
} else if (m[2]) {
result = getClass(m[2], this, result);
} else {
result = getTag(m[3] || '*', this, result)
}
}) return result;
};

注意这一步非常巧妙

    if (context.nodeType) {
context = [context];
}
// 如果不是数组,我们就让它成为一个数组.

然后在单独的get方法里面对context的判断可以去掉了,因为get方法调用的时候一定会传入一个dom对象作为上下文.

common2.js

var getTag = function (tag, context, result) {
result = result || [];
result.push.apply(result, context.getElementsByTagName(tag));
return result;
} var getId = function (id, result) {
result = result || [];
result.push(document.getElementById(id));
return result;
} var getClass = function (className, context, result) {
result = result || [];
// 首先判断我们的docoument.getElementsByClassName() 有没有这个功能
var res;
if (support.getElementsByClassName) {
res = context.getElementsByClassName(className);
} else {
// 自己实现getElementByClassName
// 思路 : 首先获得所有元素,然后再在所有元素中获得带有这个类的元素
res = myGetByClassName(className, context); }
result.push.apply(result, res);
return result;
}

下面是测试代码:

04.html

    <style>
.c div {
border: 1px solid green;
height: 50px;
width: 300px;
margin-bottom: 30px;
} .c1 {
border: 1px solid green;
height: 50px;
width: 300px;
margin-bottom: 30px;
}
</style>
</head> <body>
<div class="c">
<div></div>
<div></div>
</div>
<hr>
<div>
<div class="c1"></div>
</div>
<script src="common2.js"></script>
<script>
onload = function(){
each(get('div',get('.c')),function(){
this.style.backgroundColor = "green";
});
each(get('.c1',get('div')),function(){
this.style.backgroundColor = "yellow";
})
}
</script>
</body>

测试结果,前面两个div变绿,最后一个变黄

是不是欧了


context 上下文是一个字符串

好了,上面我们对父元素是一个dom对象和dom数组都做了判断,可是我们在使用jQuery时候,我们常常在这里传递一个字符串,因此,我们在这里对字符串的额情况也做一个分析.

其实,很简单,判断是不是字符串,如果是字符串,那么就递归调用get方法.

仅仅加上两句代码

看下面的get方法

common3.js

    if (typeof context ===  'string') {
context = get(context);
}

好了,写个页面测试一下:


这个页面应该和上面页面得到一模一样的结果,是不是这样呢?

好了,选择器上下文的问题已经解决了.下来JavaScript框架设计(四)开始进入组合选择器.

JavaScript框架设计(三) push兼容性和选择器上下文的更多相关文章

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

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

  2. JavaScript 框架设计(二)

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

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

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

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

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

  5. 游戏UI框架设计(三) : 窗体的层级管理

    游戏UI框架设计(三) ---窗体的层级管理 UI框架中UI窗体的"层级管理",最核心的问题是如何进行窗体的显示管理.窗体(预设)的显示我们前面定义了三种类型: 普通.隐藏其他.反 ...

  6. Javascript框架设计思路图

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

  7. JavaScript 框架设计

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

  8. 浅谈JavaScript框架设计

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

  9. WisDom.Net 框架设计(三) 数据缓存

    WisDom.Net  --数据缓存 1.几种缓存方式       1.静态全局变量 C#静态变量使用 static 修饰符进行声明,在类被实例化时创建,通过类进行访问不带有 static 修饰符声明 ...

随机推荐

  1. problem-eclipse创建maven项目报错

    Could not calculate build plan: Plugin org.apache.maven.plugins:maven-resources-plugin:2.5 or one of ...

  2. STM32之输入捕获以及小小应用(库)

    五一之际,先祝大家五一快乐.其实快乐很简单,工作的人有假放,学习的人也有假放,像我,有假放才有更多的时间学自己想学的东西.51假期学51,可惜没有32假期呀.好了..言归正传,大家听过吸星大法吧..在 ...

  3. host Object和native Object的区别

    Native Object: JavaScript语言提供的不依赖于执行宿主的对象,其中一些是内建对象,如:Global.Math:一些是在脚本运行环境中创建来使用的,如:Array.Boolean. ...

  4. Linux 系统查看物理内存使用率的命令脚本,以百分比形式输出。

    想监视系统内存?好像是没法直接得到现成的百分比的,自己取值计算一下吧 totalmem=`free -m | grep 'Mem' | awk '{print $3}'` usedmem=`free ...

  5. vue-cli 路由 实现类似tab切换效果(vue 2.0)

    1,更改main.js 2,在App.vue中,写入两个跳转链接(router-link),分别跳转到"home""About" (home.About即分别是 ...

  6. Hive_配置远程Metastore

    注 : 待测试 一.准备两三台linux机器,最好是hadoop集群环境 机器A:10.0.0.2 机器B:10.0.0.3 机器C:10.0.0.4 二.个机器安装信息 机器A安装mysql(用于存 ...

  7. 系统定位在iOS8中的改变

    CLLocationManager这个系统定位的类在iOS8之前要实现定位,只需要遵守CLLocationManagerDelegate这个代理即可: - (void)startLocate {   ...

  8. 使用Dotfuscator 进行.Net代码混淆 代码加密的方法

    混淆代码能在一定程度上放置代码被盗用,保护我们的知识产权 1.打开vs2012,选择工具-〉Dotfuscator Software Services 2.选择你需要混淆的DLL 文件,可以多选择 3 ...

  9. TaintDroid剖析之IPC级污点传播

    TaintDroid剖析之IPC级污点传播 作者:简行.走位@阿里聚安全 前言 在前三篇文章中我们详细分析了TaintDroid对DVM栈帧的修改,以及它是如何在修改之后的栈帧中实现DVM变量级污点跟 ...

  10. Nova PhoneGap框架 第二章 理解index.html

    跟绝大多数PhoneGap程序一样,Index.html是程序的入口.这个页面应该完成应用程序的初始化工作. 首先,让我们来看看这个页面通常都长什么样子: 下面我将一一解释这个页面都做了哪些初始化工作 ...