首页 > 代码库 > jq框架封装学习笔记1-框架介绍与选择器框架

jq框架封装学习笔记1-框架介绍与选择器框架

jq框架学习

  1. 框架的选择器模块
  2. 框架的结构
  3. DOM 基本操作(元素的操作)
  4. 事件处理
  5. 属性操作
  6. 样式操作
  7. 简单动画

简要分析 jq 框架

jq是模块化的,是一个以代码集合和功能为中心的模块

Sizzle 选择器引擎,非常常用的选择器引擎

jq 的整体结构:

    (function( window, undefined ) {
        //
    })( window );
  1. 为什么使用这个结构? 将所有内容都封装了一个沙箱之中,对外只暴露 jQuery 和 $ 对象,避免了变量名的污染
  2. 为什么传入参数? 考虑到变量名的压缩,不传 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 ];
        }
    };

各个模块的简要说明

  1. 核心模块
  2. Callbacks 回调模块(简单的理解为函数调用模块)
  3. support 能力检查模块
  4. 数据缓存模块
  5. handleQueueMarkDefer 缓动队列模块
  6. DOM 属性操作模块
  7. 事件处理模块
  8. Sizzle 引擎
  9. DOM 操作模块
  10. CSS 样式操作模块
  11. ajax 操作模块
  12. 动画模块

选择器模块

传统的元素处理

给页面中 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
...
低版本的浏览器可能不支持

一般由两种主流的实现方法

  1. 检查系统是否提供,如果没有则自己加入
  2. 重新定义一个替代性方法(函数),在方法中判断系统是否支持,如果不支持则自己实现算法

第一种方法:检查系统是否提供,如果没有则自己加入,并不推荐,因为修改了 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;
        }
    }

第二种方法实现需要考虑一些细节

  1. 为了方法更加的高效, 应该提供一个参数, 表示在什么标签下获得元素. 默认是 document
  2. 每次在检索的时候, 都要处理 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;
        }
    }

对浏览器进行能力检测

  1. 对方法功能的检查
  2. 对方法的定义进行检查

一般提供一个对象, support, 里面有很多属性,属性名与方法名相同, 但是其值 为 bool 值 表示该方法是否可用。

    var support = {
        qsa:true,
        getElementsByClassName: false
    }
  1. 对方法的能力进行检查,首先看是否有该方法,第二看自己构造一个情节,让方法调用看是否符合要求设置情节来检测一波(不太推荐,代码量较大)
    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;
    })();
  1. 对方法的定义进行检测
     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
<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    jq框架封装学习笔记1-框架介绍与选择器框架