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
原创粉丝点击