首页 > 代码库 > jq框架封装学习笔记1-框架介绍与选择器框架
jq框架封装学习笔记1-框架介绍与选择器框架
jq框架学习
- 框架的选择器模块
- 框架的结构
- DOM 基本操作(元素的操作)
- 事件处理
- 属性操作
- 样式操作
- 简单动画
简要分析 jq 框架
jq是模块化的,是一个以代码集合和功能为中心的模块
Sizzle 选择器引擎,非常常用的选择器引擎
jq 的整体结构:
(function( window, undefined ) {
//
})( window );
- 为什么使用这个结构? 将所有内容都封装了一个沙箱之中,对外只暴露 jQuery 和 $ 对象,避免了变量名的污染
- 为什么传入参数? 考虑到变量名的压缩,不传 window,每次使用window对象,就必须写window,都要6个字节,而传递后,可以一个字节,w,从统计学的角度上来看,所以要传 window。 而 传 undefined 只是为了标记 undefined这样的一个值,不传参,变量 undefined就代表 undefined。
jQuery.extend = jQuery.fn.extend = function( obj ) {
for ( var k in obj ) {
this[ k ] = obj[ k ];
}
};
等价于
jQuery.extend = function( obj ) {
for ( var k in obj ) {
this[ k ] = obj[ k ];
}
};
jQuery.fn.extend = function( obj ) {
for ( var k in obj ) {
this[ k ] = obj[ k ];
}
};
各个模块的简要说明
- 核心模块
- Callbacks 回调模块(简单的理解为函数调用模块)
- support 能力检查模块
- 数据缓存模块
- handleQueueMarkDefer 缓动队列模块
- DOM 属性操作模块
- 事件处理模块
- Sizzle 引擎
- DOM 操作模块
- CSS 样式操作模块
- ajax 操作模块
- 动画模块
选择器模块
传统的元素处理
给页面中 div 和 p 添加上边框
var divs = document.getElementsByTagName( ‘div‘ ); // HTMLCollection
var ps = document.getElementsByTagName( ‘p‘ );
// 由于 Collection 不是真的数组,也不允许使用 divs.push(...)
// 但是 Collection 也是不允许修改的 [].push.apply( divs, ps );
// 自己定义 一个数组
var arr = [];
arr.push.apply( arr, divs );
arr.push.apply( arr, ps );
arr.forEach( function( v, i ) {
v.style.border = ‘1px solid red‘;
});
封装简化
// 需要一个函数,带有两个参数第一个参数是元素的标签名,第二个参数是一个数组,
// 表示将本次获得的元素加到哪一个数组中
// getTag
function getTag( tagName, results ) {
var tags = document.getElementsByTagName( tagName );
results.push.apply(results, tags);
}
var arr = [];
getTag( ‘div‘, arr );
getTag( ‘p‘, arr );
arr.forEach( function( v, i ) {
v.style.border = ‘1px dotted red‘;
});
再一次封装简化
function getTag( tagName, results ) {
results = results || [];
var tags = document.getElementsByTagName( tagName );
results.push.apply( results, tags );
return results;
}
var arr = getTag( ‘div‘ );
arr = getTag( ‘p‘, arr );
arr.forEach( function( v, i ) {
v.style.border = ‘1px solid blue‘;
});
使用 qsa 获取元素
<div id="dv">div1</div>
<div>
div2
<p class="c">p1</p>
<p>p2</p>
<p class="c">p3</p>
<p>p4</p></div>
<div>div3</div>
<div class="c">div4</div>
<p>p4</p>
<p>p5</p>
<p>p6</p>
<p>p7</p>
var node1 = document.querySelectorAll( ‘#dv‘ );
var node2 = document.querySelectorAll( ‘p, div‘ );
var node3 = document.querySelectorAll( ‘.c‘ );
var node4 = document.querySelectorAll( ‘#p‘ );
var node5 = document.querySelectorAll( ‘div .c‘ );
封装一个 select
// 自己封装一个函数 select
function select( selector, results ) {
results = results || [];
var nodeList = document.querySelectorAll( selector );
results.push.apply( results, nodeList );
return results;
}
select( ‘div .c‘ ).forEach( function( v ) {
v.style.border = ‘1px solid blue‘;
});
封装基本元素选择器函数
function byId( idName, results ) {
// 获得的元素,返回
results = results || [];
var find = document.getElementById( idName ) ;
// 只有找到数据才会加入
if( find ) results.push( find );
return results;
}
// tag
function byTag( tagName, results ) {
results = results || [];
results.push.apply(results,document.getElementsByTagName( tagName ));
return results;
}
// byClass
function byClass( className, results ) {
results = results || [];
results.push.apply(results,document.getElementsByClassName( className ));
return results;
}
function byAll( results ) {
return byTag( ‘*‘, results );
}
而低版本浏览器是没有 getElementsByClassName 的,需要自己封装
<div class="c"></div>
<div class="c1 c2 c"></div>
<div class="c1 c c2"></div>
<div class="c1 c2"></div>
function getByClass( className ) {
// 首先获取页面中所有元素 然后再筛选出指定类名的
var list = document.getElementsByTagName( ‘*‘ );
var res = [];
for( var i = 0; i < list.length; i++ ) {
if( list[ i ].className == className ) {
res.push( list[ i ] );
}
}
return res;
}
要保证 class 属性中 有 c,那么 要么 c 在中间,两端有空格,例如:“c1 c c2”
要么,c 在两端,一边有空格,一边是引号, 例如:“c1 c” 或 “c c2”
首先判断 className 是否包含 c
然后再看 c 是否在 两端,indexOf,0,length - 1,即判断 1 或 length - 2 是否为空格
如果在中间,那么就看两端是否为空格 i - 1 和 i + 1
正则表达式:
c 在中间或两端
/^\sc\s|\sc\s|\sc$|^c$/g
function contains( str1, str2 ) {
// 利用字符串拼接得到正则式
return ( /^c\s|\sc\s|\sc$/g ).test( str1 );
var regexpStr = "^\\s" + str2 + "\\s|\\s" + str2 + "\\s|\\s" + str2 + "$";
return ( new RegExp( regexpStr, ‘g‘ ) ).test( str1 );
}
// 改良 getByClass
function getByClass( className ) {
// 首先获取页面中的所有元素, 然后在筛选出 指定类名的
var list = document.getElementsByTagName( ‘*‘ );
var res = [];
for ( var i = 0; i < list.length; i++ ) {
if ( list[ i ].className == className || contains( list[ i ].className, className ) ) {
res.push( list[ i ] );
}
}
return res;
}
console.log(getByClass(‘c‘));
console.log(getByClass(‘c1‘));
jquery 作者的实现方法
// jq 作者的实现方法
function getByClass ( className ) {
var list = document.getElementsByTagName( ‘*‘ );
var res = [];
for ( var i = 0; i < list.length; i++ ) {
// 目的是在 list[ i ].className 中 看是否有 className
if ( ( " " + list[ i ].className + " " ).indexOf( " " + className + " " ) > -1 ) {
res.push( list[ i ] );
}
}
return res;
}
// c " c " " c " 0
// c1 c2 c " c1 c2 c " " c " 6
// c1 c c2 " c1 c c2 " " c " 3
// c1 c2 " c1 c2 " " c "
var list = getByClass( ‘c‘ );
浏览器兼容处理办法
document.getElementsByClassName
str.indexOf
arr.filter
...
低版本的浏览器可能不支持
一般由两种主流的实现方法
- 检查系统是否提供,如果没有则自己加入
- 重新定义一个替代性方法(函数),在方法中判断系统是否支持,如果不支持则自己实现算法
第一种方法:检查系统是否提供,如果没有则自己加入,并不推荐,因为修改了 document 对象,污染了 document 对象
if ( !document.getElementsByClassName ) {
// 进入该范围表示: get*byClassName 不存在
// 进入兼容区域
document.getElementsByClassName = function ( className ) {
var list = document.getElementsByTagName( ‘*‘ );
var res = [];
for ( var i = 0; i < list.length; i++ ) {
if ( ( " " + list[ i ].className + " " ).indexOf( " " + className + " " ) > -1 ) {
res.push( list[ i ] );
}
}
return res;
};
}
第二种方法:重新定义一个替代性方法(函数),在方法中判断系统是否支持,如果支持,就直接使用,如果不支持则自己实现算法,注意,不是修改 document 对象
function getByClass(className) {
// 检查系统是否支持该方法, 如果支持就直接使用
// 如果不持之在自己实现算法完成. 注意, 不是修改 document 对象
if (document.getElementsByClassName) {
return document.getElementsByClassName(className);
} else {
var list = document.getElementsByTagName(‘*‘), res = [];
for (var i = 0; i < list.length; i++) {
if (( " " + list[i].className + " " ).indexOf(" " + className + " ") > -1) {
res.push(list[i]);
}
}
return res;
}
}
第二种方法实现需要考虑一些细节
- 为了方法更加的高效, 应该提供一个参数, 表示在什么标签下获得元素. 默认是 document
- 每次在检索的时候, 都要处理 if 判断, 应该减少原型链的遍历
在框架被一加载的时候, 就完成判断, 是否存在存在一个变量中, 每次再判断的时候, 直接判断变量, 这里用了能力检测 => 对方法的定义进行检查
var support = {};
var rnative = /\{\s*\[native/;
support.getElementsByClassName = rnative.test(document.getElementsByClassName + ‘‘);
function getByClassName(className, node) {
node = node || document;
var list, res = [], i;
if (support.getElementsByClassName) {
return node.getElementsByClassName(className);
} else {
list = document.getElementsByTagName(‘*‘);
for (i = 0; i < list.length; i++) {
if (( " " + list[i].className + " " ).indexOf(" " + className + " ") > -1) {
res.push(list[i]);
}
}
return res;
}
}
对浏览器进行能力检测
- 对方法功能的检查
- 对方法的定义进行检查
一般提供一个对象, support, 里面有很多属性,属性名与方法名相同, 但是其值 为 bool 值 表示该方法是否可用。
var support = {
qsa:true,
getElementsByClassName: false
}
- 对方法的能力进行检查,首先看是否有该方法,第二看自己构造一个情节,让方法调用看是否符合要求设置情节来检测一波(不太推荐,代码量较大)
var support = {};
// 以 getElementsByClassName 为例
support.getElementsByClassName = (function () {
var div = document.createElement(‘div‘);
// 验证该方法是否可用
if (typeof div.getElementsByClassName === ‘function‘) {
// 证明他是一个方法
div.innerHTML = ‘<div class="c"></div>‘; // 准备一个 标签结构
var res = div.getElementsByClassName(‘c‘); // 调用方法以后, 看是否可以完成要的功能
if (res && res.length === 1 && res[0].className === ‘c‘) {
return true;
}
}
return false;
})();
- 对方法的定义进行检测
alert( document.getElementsByClassName );
document.getElementsByClassName = function ( selector ) {
return 123;
};
alert( document.getElementsByClassName );
// 内置的方法都会返回 function 函数名 () { [native code] }
// 一旦方法存在, 并且方法的实现都是 [native ... ] 那么就可以肯定, 方法可用
所以对方法的定义进行检测, 检查方法是不是系统实现的,内置的方法都会返回 function 函数名 () { [native code] },他是不可修改的,且是不可见的
var support = {};
support.getElementsByClassName = (function () {
var str = document.getElementsByClassName + ‘‘;
var rnative = /\{\s*\[native/;
return rnative.test(str);
})();
alert(support.getElementsByClassName); // IE 8 false / chrome True
jq框架封装学习笔记1-框架介绍与选择器框架