jq框架封装学习笔记2-选择模块
来源:互联网 发布:流量互换源码 编辑:程序博客网 时间:2024/06/11 01:19
select选择器引擎初步封装
/** * Created by Jepson on 2016/11/20. */ var select = (function () { // 基本函数, support 对象, 验证 qsa 与 byclass var support = {}; var rnative = /\{\s*\[native/; // 浏览器能力检测,对方法的定义进行检测 support.qsa = rnative.test( document.querySelectorAll + '' ); support.getElementsByClassName = rnative.test( document.getElementsByClassName + '' ); // 基本方法 function getByClassName( className, node, i ) { 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; } } // 捕获 1 是 id, 2 是 类, 3 是 通配符 4 是 标签 选择器 var rbaseselector = /^(?:\#([\w\-]+)|\.([\w\-]+)|(\*)|(\w+))$/; function select ( selector, results ) { results = results || []; var m, temp; if ( typeof selector != 'string' ) return results; // 判断是否支持 qsa 方法,支持直接用 if ( support.qsa ) { results.push.apply( results, document.querySelectorAll( selector ) ) return results; } m = rbaseselector.exec( selector ); if ( m ) { // 捕获到数据了 if ( m[ 1 ] && ( temp = document.getElementById( m[ 1 ] ) ) ) { results.push( temp ); } else if ( m[ 2 ] ) { results.push.apply( results, getByClassName( m[ 2 ] ) ); } else if ( m[ 3 ] ) { results.push.apply( results, document.getElementsByTagName( m[ 3 ] ) ); // selector } else if ( m[ 4 ] ) { results.push.apply( results, document.getElementsByTagName( m[ 4 ] ) ); // selector } // 注意: 3 和 4 是可以合并 } return results; } return select; })();
trim 的检测与自己实现
var rtrim = /^\s+|\s+$/g; var support = {}; support.trim = rnative.test(String.prototype.trim + ''); // 方法能力检测,定义 // 自定义实现 trim 方法 var myTrim = function (str) { // 表示两端去空格, 然后返回去除空格的结果 if (support.trim) { return str.trim(); } else { // 自定义实现 return str.replace(rtrim, ''); } }
带逗号的选择器功能实现
var select = ( function() { // 正则表达式 var rnative = /\{\s*\[native/; // 利用方法定义进行能力检测 var rtrim = /^\s+|s+$/g; // trim 方法没有,我们就自己搞 var rbaseselector = /^(?:\#([\w\-]+)|\.([\w\-]+)|(\*)|(\w+))$/; var support = {}; support.qsa = rnative.test( document.querySelectorAll + '' ); support.getElementsByClassName = rnative.test( document.getElementsByClassName + '' ); support.trim = rnative.test( String.prototype.trim + '' ); // 基本方法 function getByClassName( className, node ) { node = node || document; var allElem, res = [], i; if ( support.getElementsByClassName ) { node = node || document; var allElem, res = [], i; if ( support.getElementsByClassName ) { return node.getElementsByClassName( className ); } else { allElem = node.getElementsByClassName( '*' ); for( i = 0; i < allElem.length; i++ ) { if ( (" " + allElem[ i ].className + " ").indexOf( " " + className + " ") > -1 ) { allElem.push( list[ i ] ); } } return res; } } } // 自定义实现 trim 方法 var myTrim = function( str ) { // 两端去掉空格,然后返回去除空格的结果 if ( support.trim ) { return str.trim(); } else { return str.replace( rtrim, '' ); } } function select( selector, results ) { results = results || []; var m, temp, selectors, i, subselector; if ( typeof selector != 'string' ) return results; // 处理分割的问题 selectors = selector.splits( ',' ); for( i = 0; i < selectors.length; i++ ) { // selector[ i ] 选择器 subselector = myTrim( selectors[ i ] ); // 判断 if ( support.qsa ) { results.push.apply( results, document.querySelectorAll( subselector ) ); } else if ( m = rbaseselector.exec( subselector ) ) { // 捕获到数据了 if ( m[ 1 ] && ( temp = document.getElementById( m[ 1 ] ) ) ) { results.push( temp ); } else if ( m[ 2 ] ) { results.push.apply( results, getByClassName( m[ 2 ] ) ); } else if ( m[ 3 ] ) { results.push.apply( results, document.getElementsByTagName( m[ 3 ] ) ); // selector } else if ( m[ 4 ] ) { results.push.apply( results, document.getElementsByTagName( m[ 4 ] ) ); // selector } // 注意: 3 和 4 是可以合并 } } } return select; // 返回函数引用 })();
元素去重
可以得到 select 返回的就是一个数组.* ‘.p, div’很可能匹配到相同元素标签,都在数组中,那就有重复,消耗了性能*。所以可以抽象一下. 我们就是要在数组中去除重复元素
var arr = [ 1, 2, 3, 1, 3, 2, 4 ]; // => [ 1, 2, 3, 4 ]var tempArr = [];循环将 arr 中的 元素 一个一个 的加到 tempArr 中, 并且需要判断如果 tempArr 中不包含该元素才会加进去
最简单的方法
var arr = [ 1, 2, 3, 2, 4, 3, 1, 5 ]; var tempArr = []; for( var i = 0; i < arr.length; i++ ) { if ( tempArr.indexOf( arr[ i ] ) === -1 ) { tempArr.push( arr[ i ] ); } } console.log( tempArr );
但是 indexOf 早期浏览器又不支持,我们要自己封装, 能力检测 - 方法定义检测
var rnative = /\{\s*\[native/; var support = {}; support.indexOf = rnative.test(Array.prototype.indexOf + ""); console.log( support.indexOf );
兼容性 indexOf 封装
var rnative = /\{\s*\[native/; var support = {}; support.indexOf = rnative.test(Array.prototype.indexOf + ""); var myIndexOf = function (array, search, startIndex) { startIndex = startIndex || 0; if (support.indexOf) { return array.indexOf(search, startIndex); } else { for (var i = startIndex; i < array.length; i++) { if (array[i] === search) { return i; } } return -1; } } var arr = [ 1, 2, 3, 2, 4, 3, 1, 5 ]; var tempArr = []; for ( var i = 0; i < arr.length; i++ ) { if ( myIndexOf( tempArr, arr[ i ] ) == -1 ) { tempArr.push( arr[ i ] ); } } console.log( tempArr ); // 12345
所以元素去重方法就有了
var rnative = /\{\s*\[native/; var support = {}; support.indexOf = rnative.test(Array.prototype.indexOf + ""); var myIndexOf = function (array, search, startIndex) { startIndex = startIndex || 0; if (support.indexOf) { return array.indexOf(search, startIndex); } else { for (var i = startIndex; i < array.length; i++) { if (array[i] === search) { return i; } } return -1; } } // 兼容性元素去重方法 var unique = function ( array ) { var resArray = [], i = 0; for (; i < array.length; i++) { if (myIndexOf(resArray, array[i]) == -1) { resArray.push(array[i]); } } return resArray; }
添加后代元素元素选择器的封装
jquery的后代元素选择器
<body> <div>div1 <div>div2 <div>div3 <div>div4</div> </div> </div> </div> </body> <script> $( 'div div' ).each( function () { this.style.border = '1px solid red'; this.style.margin = '5px'; }); var length = $( 'div div' ).length; </script>
利用空格 实现 后代元素 选择器
现在需要一个函数,函数的参数与结构类似于 select, 该函数专门处理带有 空格的 后代元素
// 假设 selector 选择器是字符串, 而且不是基本选择器 // 肯定是 : selector1 selector2 selector3 形式 // 还有可能: selector1 > selector2:first 形式 function select2 ( selector, results ) { // 如果是空格形式的, 很简单, 将其 split 一下, 然后遍历数组, // 利用数组中的选择器来获得元素. // 比如 selecotr 是 'div div' // split 后: [ 'div', 'div' ] // 获得 div: document.getElementsByTagName( 'div' ) // 得到的是一个维数组 list // 再次遍历 list, 在 list 下再次查找 div 元素 // list[ i ].getElementsByTagName( 'div' ); // 再比如: 'div .c' // 1> 先找 div -> list // 2> list[ i ].getElementsByClassName( 'c' ) // 再看一个例子: 'div p .c' // 1> 找 'div' -> list // 2> list[ i ] 中找 p -> 重新得到一个集合 list // 3> 在 list[ i ] 再找 .c // 首先将元素 split, 得到一个选择器数组 // 遍历数组, 获得元素, 得到元素数组 // 在遍历元素数组, 再来获得元素 var selectors = selector.split( ' ' ); // 假定 'div p .c' var arr = document.getElementsByTagName( selectors[ i ].trim() ); var tempArr = []; for ( var i = 0; i < arr.length; i++ ) { tempArr.push.apply( tempArr, arr[ i ].getElementsByTagName( 'p' )); } // 在 tempArr 的基础上再次过滤 .c var tempArr2 = []; for ( var i = 0; i > tempArr.length; i++ ) { tempArr2.push.apply( tempArr2, tempArr[ i ].getElementsByClassName( 'c' ) ); } }
我们思考怎么提取,伪代码:
// 假设 selector 选择器是字符串, 而且不是基本选择器 // 肯定是 : selector1 selector2 selector3 形式 // 还有可能: selector1 > selector2:first 形式 function basicSelect ( selector, node ) {} function select2 ( selector, results ) { var selectors = selector.split( ' ' ); // 假定 'div p .c' var arr = [], node = [ document ]; for ( var i = 0; i < node.length; i++ ) { arr.push.apply( arr, basicSelect( selectors[ 0 ], node[ i ] )); } var tempArr = []; for ( var i = 0; i < arr.length; i++ ) { tempArr.push.apply( tempArr, basicSelect( selectors[ 1 ], arr[ i ] )); } // 在 tempArr 的基础上再次过滤 .c var tempArr2 = []; for ( var i = 0; i > tempArr.length; i++ ) { tempArr2.push.apply( tempArr2, basicSelect( selectors[ 2 ], tempArr[ i ] )); } }
这里 arr,tempArr, tempArr2都是临时的,用完一次就没用了,所以可以考虑全用 arr (伪代码):
function basicSelect ( selector, node ) {} function select2 ( selector, results ) { var selectors = selector.split( ' ' ); // 假定 'div p .c' var arr = [], node = [ document ]; for ( var i = 0; i < node.length; i++ ) { arr.push.apply( arr, basicSelect( selectors[ 0 ], node[ i ] )); } node = arr; arr = []; for ( var i = 0; i < node.length; i++ ) { arr.push.apply( arr, basicSelect( selectors[ 1 ], node[ i ] )); } node = arr; arr = []; for ( var i = 0; i > node.length; i++ ) { arr.push.apply( arr, basicSelect( selectors[ 2 ], node[ i ] )); } node = arr; arr = []; // 最终结果存储到 node 里面去了 }
这样我们就可以考虑把整个过程用一个循环来做
function basicSelect ( selector, node ) {} function select2 ( selector, results ) { results = results || []; var selectors = selector.split( ' ' ); var arr = [], node = [ document ]; for ( var j = 0; j < selectors.length; j++ ) { for ( var i = 0; i < node.length; i++ ) { arr.push.apply( arr, basicSelect( selectors[ j ], node[ i ] )); } node = arr; arr = []; } results.push.apply( results, node ); return results; }
好了我们最后完成一下basicSelect的代码
<div> <p class='c'>p1 <span class="c">span</span> </p> <div> <p class='c'>p3</p> <div> <p>p4</p> </div> </div> </div> <div> <p>p2</p> </div> <script> var rbaseselector = /^(?:\#([\w\-]+)|\.([\w\-]+)|(\*)|(\w+))$/; function basicSelect ( selector, node ) { node = node || document; var m, res; if ( m = rbaseselector.exec( selector ) ) { if ( m[ 1 ] ) { // id res = document.getElementById( m[ 1 ] ); if ( res ) { return [ res ]; } else { return []; } } else if ( m[ 2 ] ) { // class return node.getElementsByClassName( m[ 2 ] ); } else if ( m[ 3 ] ) { return node.getElementsByTagName( m[ 3 ] ); } else if ( m[ 4 ] ) { return node.getElementsByTagName( m[ 4 ] ); } } return []; } function select2 ( selector, results ) { results = results || []; var selectors = selector.split( ' ' ); // 假定 'div p .c' var arr = [], node = [ document ]; for ( var j = 0; j < selectors.length; j++ ) { for ( var i = 0; i < node.length; i++ ) { arr.push.apply( arr, basicSelect( selectors[ j ], node[ i ] )); } node = arr; arr = []; } results.push.apply( results, node ); return results; } // 测试代码 var list = select2( 'div p .c' ); console.log( list ); </script>
加入后代选择器的封装
var select = (function() { // 正则表达式 var rnative = /\{\s*\[native/; var rtrim = /^\s+|\s+$/g; var rbaseselector = /^(?:\#([\w\-]+)|\.([\w\-]+))|(\*)|([\w\-]+)$/; var support = {}; // 分别判断方法是否支持 support.qsa = rnative.test( document.querySelectorAll + "" ); support.getElementsByClassName = rnative.test( document.getElementsByClassName + '' ); support.trim = rnative.test( String.prototype.trim + '' ); support.indexOf = rnative.test( Array.prototype.indexOf + '' ); // 基本方法 // 兼容性方法 getByClassName function getByClassName( className, node ) { node = node || document; var allElem, res = [], i; // 支持就用,不支持,我们就自己实现 if ( support.getElementsByClassName ) { return node.getElementsByClassName( className ); } else { allElem = node.getElementsByClassName( '*' ); // 获取所有的元素 for (i = 0; i < allElem.length; i++) { // 进行筛选 if ((' ' + allElem[i].className + '').indexOf(' ' + className + ' ') > -1) { res.push(allElem[i]); } } return res; } } // 兼容性方法 myTrim var myTrim = function( str ) { // 两端去掉空格, 然后返回去掉空格的结果 if ( support.trim ) { return str.trim(); } else { // 这时候系统没有,自定义的实现 return str.replace( rtrim, '' ); } }; // 兼容性方法 myIndexOf var myIndexOf = function ( array, search, startIndex ) { startIndex = startIndex || 0; // 如果有么,就用系统的 if ( support.indexOf ) { return array.indexOf( search, startIndex ); } else { // 如果没有么,那就自己实现好了挖 for ( var i = startIndex; i < array.length; i++ ) { if ( array[ i ] === search ) { return i; // 返回索引 } } return -1; } }; // 兼容性方法 数组去重 var unique = function( array ) { var resArray = [], i = 0; for (; i < array.length; i++ ) { if ( myIndexOf( resArray, array[ i ] ) == -1 ) { resArray.push( array[ i ] ); } } return resArray; }; // 基本选择器封装,能够实现 id 类 通配符 标签选择器 function basicSelect( selector, node ) { node = node || document; var m, res; if ( m = rbaseselector.exec( selector ) ) { if ( m[ 1 ] ) { // id res = document.getElementById( m[ 1 ] ); // id 选择器比较特殊,没找到会返回 null, 但是应该要返回[] 所以这里要做一个特殊处理 if ( res ){ return [ res ]; } else { return []; } } else if ( m[ 2 ] ) { // class return getByClassName( m[ 2 ], node ); } else if ( m[ 3 ] ) { // * return node.getElementsByTagName( m[ 3 ] ); } else if ( m[ 4 ] ) { // tag return node.getElementsByTagName( m[ 4 ] ); } } return []; } // 基于 basicSelect 的 层级选择器封装 function select2( selector, results ) { results = results || []; var selectors = selector.split( ' ' ); // 将selector 根据 空格进行分割 var arr = [], node = [ document ]; for ( var j = 0; j < selectors.length; j++ ) { for ( var i = 0; i < node.length; i++ ) { // 遍历 selectors 分割出来的 子元素, 进行选择 arr.push.apply( arr, basicSelect( selectors[ j ], node[ i ] ) ); } node = arr; arr = []; } // 最后的结果全都在 results 中 results.push.apply( results, node ); return results; } // 核心暴露函数 function select( selector, results ) { results = results || []; var m, temp, selectors, i, subselector; // 你首先传给我个 东西, 那必须是字符串吧 if ( typeof selector != 'string' ) return results; // 参数没有问题,那就开搞 // 先看看 牛逼的 qsa 能不能用,能用就直接用 if ( support.qsa ) { results.push.apply( results, document.querySelectorAll( selector ) ); } else { // 不存在我们自己来搞一搞, 字符串分割 selectors = selector.split( ',' ); // 循环 for( i = 0; i < selectors.length; i++ ) { subselector = myTrim( selectors[ i ] ); // 去掉不该有的空格 // 接下来就是 处理 subselector if ( rbaseselector.test( subselector ) ) { // 基本选择器 results.push.apply( results, basicSelect( subselector ) ); } else { // select2 函数 select2( subselector, results ); } } } // return 之前过滤一波 return unique( results ); } return select; })();
解决 IE 8 中伪数组无法使用 push 的方法 - 完善封装
var list = document.getElementsByTagName( 'div' ); var arr = []; arr.push.apply( arr, list ); console.log( arr );
在 IE 8 是无法实现这个 push 效果的,伪数组无法使用 push,我们需要考虑自己重新实现 push 的 apply 方法,并加入封装之中
var select = (function() { var push = [].push; // 如果出现了错误那么就需要重写 push try { var div = document.createElement('div'); div.innerHTML = '<p></p>'; var arr = []; push.apply(arr, div.getElementsByTagName('p')); } catch (e) { push = { apply: function (array1, array2) { for (var i = 0; i < array2.length; i++) { array1[array1.length++] = array2[i]; } } }; } // 正则表达式 var rnative = /\{\s*\[native/; var rtrim = /^\s+|\s+$/g; var rbaseselector = /^(?:\#([\w\-]+)|\.([\w\-]+))|(\*)|([\w\-]+)$/; var support = {}; // 分别判断方法是否支持 support.qsa = rnative.test( document.querySelectorAll + "" ); support.getElementsByClassName = rnative.test( document.getElementsByClassName + '' ); support.trim = rnative.test( String.prototype.trim + '' ); support.indexOf = rnative.test( Array.prototype.indexOf + '' ); // 基本方法 // 兼容性方法 getByClassName function getByClassName( className, node ) { node = node || document; var allElem, res = [], i; // 支持就用,不支持,我们就自己实现 if ( support.getElementsByClassName ) { return node.getElementsByClassName( className ); } else { allElem = node.getElementsByClassName( '*' ); // 获取所有的元素 for (i = 0; i < allElem.length; i++) { // 进行筛选 if ((' ' + allElem[i].className + '').indexOf(' ' + className + ' ') > -1) { res.push(allElem[i]); } } return res; } } // 兼容性方法 myTrim var myTrim = function( str ) { // 两端去掉空格, 然后返回去掉空格的结果 if ( support.trim ) { return str.trim(); } else { // 这时候系统没有,自定义的实现 return str.replace( rtrim, '' ); } }; // 兼容性方法 myIndexOf var myIndexOf = function ( array, search, startIndex ) { startIndex = startIndex || 0; // 如果有么,就用系统的 if ( support.indexOf ) { return array.indexOf( search, startIndex ); } else { // 如果没有么,那就自己实现好了挖 for ( var i = startIndex; i < array.length; i++ ) { if ( array[ i ] === search ) { return i; // 返回索引 } } return -1; } }; // 兼容性方法 数组去重 var unique = function( array ) { var resArray = [], i = 0; for (; i < array.length; i++ ) { if ( myIndexOf( resArray, array[ i ] ) == -1 ) { resArray.push( array[ i ] ); } } return resArray; }; // 基本选择器封装,能够实现 id 类 通配符 标签选择器 function basicSelect( selector, node ) { node = node || document; var m, res; if ( m = rbaseselector.exec( selector ) ) { if ( m[ 1 ] ) { // id res = document.getElementById( m[ 1 ] ); // id 选择器比较特殊,没找到会返回 null, 但是应该要返回[] 所以这里要做一个特殊处理 if ( res ){ return [ res ]; } else { return []; } } else if ( m[ 2 ] ) { // class return getByClassName( m[ 2 ], node ); } else if ( m[ 3 ] ) { // * return node.getElementsByTagName( m[ 3 ] ); } else if ( m[ 4 ] ) { // tag return node.getElementsByTagName( m[ 4 ] ); } } return []; } // 基于 basicSelect 的 层级选择器封装 function select2( selector, results ) { results = results || []; var selectors = selector.split( ' ' ); // 将selector 根据 空格进行分割 var arr = [], node = [ document ]; for ( var j = 0; j < selectors.length; j++ ) { for ( var i = 0; i < node.length; i++ ) { // 遍历 selectors 分割出来的 子元素, 进行选择 push.apply( arr, basicSelect( selectors[ j ], node[ i ] ) ); } node = arr; arr = []; } // 最后的结果全都在 results 中 push.apply( results, node ); return results; } // 核心暴露函数 function select( selector, results ) { results = results || []; var m, temp, selectors, i, subselector; // 你首先传给我个 东西, 那必须是字符串吧 if ( typeof selector != 'string' ) return results; // 参数没有问题,那就开搞 // 先看看 牛逼的 qsa 能不能用,能用就直接用 if ( support.qsa ) { push.apply( results, document.querySelectorAll( selector ) ); } else { // 不存在我们自己来搞一搞, 字符串分割 selectors = selector.split( ',' ); // 循环 for( i = 0; i < selectors.length; i++ ) { subselector = myTrim( selectors[ i ] ); // 去掉不该有的空格 // 接下来就是 处理 subselector if ( rbaseselector.test( subselector ) ) { // 基本选择器 push.apply( results, basicSelect( subselector ) ); } else { // select2 函数 select2( subselector, results ); } } } // return 之前过滤一波 return unique( results ); } return select; })();
each 函数函数封装
function each ( arr, func ) { var i; // 在 js 中(面向对象的编程语言中) 有一个规则:鸭式变型 // 在 ES5 中还引入了 Array.isArray 的方法专门来判断数组 if ( arr instanceof Array || arr.length >= 0) { for ( i = 0; i < arr.length; i++ ) { func.call( arr[ i ], i, arr[ i ] ); } } else { for ( i in arr ) { func.call( arr[ i ], i, arr[ i ] ); } } return arr; }
map 函数的封装
function map(arr, func) { var i, res = [], tmp; // 鸭式变型 if (arr instanceof Array || arr.length >= 0) { for (i = 0; i < arr.length; i++) { tmp = func(arr[i], i); if (tmp != null) { res.push(tmp); } } } else { for (i in arr) { tmp = func(arr[i], i); if (tmp != null) { res.push(tmp); } } } return res; } var list = map([1, 2, 3, 4, 5], function (v, i) { return 'a'; });
0 0
- jq框架封装学习笔记2-选择模块
- jq框架封装学习笔记4-DOM操作模块
- jq框架封装学习笔记5-样式操作与属性操作模块
- jq框架封装学习笔记3-封装select引擎
- jq框架封装学习笔记1-框架介绍与选择器框架
- JQ 学习笔记2
- jq学习笔记2
- angularjs学习笔记-封装公共模块
- JQ 学习笔记
- JQ学习笔记
- js、jq学习笔记
- jq学习笔记1
- jq学习笔记DOM
- 03jq学习笔记
- okhttp学习笔记--网络框架封装
- 分布式服务框架学习笔记2 常用的分布式服务框架 与 通信框架选择
- h5学习笔记:框架选择记录
- ReactNative学习笔记--下拉选择菜单的简单封装
- rmmod: chdir(/lib/modules): No such file or directory
- Docker使用心得
- iWorkTime项目开发简介
- 《The two second acdvantage》读书笔记
- [嵌入式]Cortex-A8处理器编程(上)
- jq框架封装学习笔记2-选择模块
- oracle学习之:解锁用户
- 单例模式——懒汉式和饿汉式
- [github高级控件]带你走近->自定义标签云
- JQuery中的一些重要方法
- jq框架封装学习笔记3-封装select引擎
- IDEA的安装
- jq框架封装学习笔记4-DOM操作模块
- Spark系列修炼---入门笔记25