jQuery原理(自己封装一个jQuery)

jQuery原理

1.来看一下jQuery自己的结构

  • (function( window, undefined ) {
         var jQuery = function( ) {
             return new jQuery.prototype.init( );
         }
         jQuery.prototype = {
             constructor: jQuery,
              init:function(){}
         }
         jQuery.prototype.init.prototype = jQuery.prototype;
         window.jQuery = window.$ = jQuery;
     })( window );
    /*
    1.jQuery的本质是一个闭包
    2.jQuery为什么要使用闭包来实现?
      为了避免多个框架的冲突
    3.jQuery如何让外界访问内部定义的局部变量
       window.xxx = xxx;
    4.jQuery为什么要给自己传递一个window参数?
       为了方便后期压缩代码,直接将window赋值给一个短的变量
       为了提升查找的效率
    5.jQuery为什么要给自己接收一个undefined参数?
       为了方便后期压缩代码
       IE9以下的浏览器undefined可以被修改, 为了保证内部使用的undefined不被修改, 所以需要接收            一个正确的undefined
     6.new jQuery.prototype.init( );
        为了在init里访问jQuery里边的方法,将init的原型改为jQuery的原型
    */
    

以上搭建框架的原型图![8D`9TUHYG(N)BB98TDIGQI.png

我们也仿照这个格式来搭建自己的基本结构

  • (function( window, undefined ) {
         var njQuery = function( ) {
             return new njQuery.prototype.init( );
         }
         njQuery.prototype = {
             constructor: njQuery
         }
         njQuery.prototype.init.prototype = njQuery.prototype;
         window.njQuery = window.$ = njQuery;
     })( window );
    

2.入口函数

2.1入口函数测试
  • <script>
        window.onload = function (ev) {
        // $(function () {
            /*
            jQ入口函数传入不同参数得到的实例
            1.传入 '' null undefined NaN  0  false
            2.传入html片段
            3.传入选择器
            4.传入数组
            5.传入伪数组
            6.传入对象
            7.传入DOM元素
            8.传入基本数据类型
             */
            //  1.传入 '' null undefined NaN  0  false
            // 会返回一个空的jQuery对象给我们
            console.log($());
            console.log($(''));
            console.log($(null));
            console.log($(undefined));
            console.log($(NaN));
            console.log($(0));
            console.log($(false));
    
            // 2.传入html片段
            // 会将创建好的DOM元素存储到jQuery对象中返回
            console.log($('<p>1</p><p>2</p><p>3</p>'));
            // console.log($('    <div><p>1</p></div><div><p>2</p></div>    '));
    
            // 3.传入选择器
            // 会将找到的所有元素存储到jQuery对象中返回
            console.log($('li'));
    
            // 4.传入数组
            // 会将数组中存储的元素依次存储到jQuery对象中立返回
            var arr = [1, 2, 3, 4, 5, 6];
            console.log($(arr));
    
            // 5.传入伪数组
            // 会将数组中存储的元素依次存储到jQuery对象中立返回
            var likeArr = {0:"lnj", 1:"33", 2:"male", length: 3};
            console.log($(likeArr));
    
            // console.log(typeof arr);
            // console.log(typeof likeArr);
            // console.log(arr.toString());
            // console.log(likeArr.toString());
            // console.log(({}).toString.apply(arr));
    
            // 6.传入对象
            // 会将传入的对象存储到jQuery对象中返回
            function Person() {}
            console.log($(new Person()));
    
            // 7.传入DOM元素
            // 会将传入的DOM元素存储到jQuery对象中返回
            console.log($(document.createElement('div')));
    
            // 8.传入基本数据类型
            // 会将传入的基本数据类型存储到jQuery对象中返回
            console.log($(123));
            console.log($(true));
    
            /*
             1.传入 '' null undefined NaN  0  false, 返回空的jQuery对象
             2.字符串:
             代码片段:会将创建好的DOM元素存储到jQuery对象中返回
             选择器: 会将找到的所有元素存储到jQuery对象中返回
             3.数组:
             会将数组中存储的元素依次存储到jQuery对象中立返回
             4.除上述类型以外的:6 7 8
             会将传入的数据存储到jQuery对象中返回
            */
            // });
        }
    </script>
    

先来了解一些方法

2.2 call和apply
  • <script>
        /*
        apply和call方法的作用:
        专门用于修改方法内部的this
    
        格式:
        call(对象, 参数1, 参数2, ...);
        apply(对象, [数组]);
        */
        function test() {
            console.log(this);
        }
        // window.test();
        var obj = {"name": "lnj2"};
        /*
        1.通过window.test找到test方法
        2.通过apply(obj)将找到的test方法内部的this修改为自定义的对象
        */
         window.test.apply(obj);
         window.test.call(obj);
    
        function sum(a, b) {
            console.log(this);
            console.log(a + b);
        }
        // window.sum.call(obj, 1, 2);
        /*
        1.通过window.sum找到sum方法
        2.通过apply(obj)将找到的sum方法内部的this修改为自定义的对象
        3.将传入数组中的元素依次取出, 传递给形参
        */
         window.sum.apply(obj, [3, 5]);
    
    
        // 真数组转换伪数组的一个过程
        // var arr = [1, 3, 5, 7, 9];
        // var obj = {};
        /*
        1.通过[].push找到数组中的push方法
        2.通过apply(obj)将找到的push方法内部的this修改为自定义的对象
        3.将传入数组中的元素依次取出, 传递给形参
        */
        [].push.apply(obj, arr);
        console.log(obj);
        window.onload = function (ev) {
            // 系统自带的伪数组
            var res = document.querySelectorAll("div");
            // 自定义的伪数组
            var obj = {0:"lnj", 1:"33", length: 2};
            // var arr = []; // 真数组
            // [].push.apply(arr, obj);
            // console.log(arr);
    
            //上面这个方法只能在IE9以上的浏览器用,所以使用 如果想将伪数组转换为真数组那么可以使用如下方法
            var arr = [].slice.call(obj);
            //即将数组的this改为obj,然后通过slice方法什么参数都没有传递,将数组中的元素放到一个新的数组中原样返回
            console.log(arr);
            // var arr2 = [1, 3, 5, 7, 9];
            // 如果slice方法什么参数都没有传递, 会将数组中的元素放到一个新的数组中原样返回
            // var res2 = arr2.slice();
            // var res2 = arr2.slice(2);
            // var res2 = arr2.slice(2, 4);
            // console.log(res2);
        }
    </script>
    
2.3 去除传入字符串两边的空格
  • function(str){
        if(!njQuery.isString(str)){
            return str;
        }
        // 判断是否支持trim方法 高版本浏览器
        if(str.trim){
            return str.trim();
        }else{
            return str.replace(/^\s+|\s+$/g, "");//正则表达式
        }
    }
    
2.4 判断是不是数组
  • //是数组的话一定是对象,并且具有length属性,并且不是window
    function(sele){
        if(njQuery.isObject(sele) &&
            !njQuery.isWindow(sele) &&
            "length" in sele){
            return true;
        }
        return false;
    }
    
2.5 extend方法
  • //当我们有很多自定义的函数时,在维护时会很不方便,extend就是为了解决这个问题
    function njQuery() {
    
    }
    /*
    njQuery.extend = function (obj) {
        // 此时此刻的this就是njQuery这个类
        // console.log(this);
        for(var key in obj){
            // njQuery["isTest"] = function () {console.log("test");}
            this[key] = obj[key];
        }
    }
    njQuery.extend({
        isTest: function () {
            console.log("test");
        }
    });
    njQuery.isTest();
    */
    /*
    njQuery.prototype.extend = function (obj) {
        // 此时此刻的this是njQuery对象
        // console.log(this);
        for(var key in obj){
            // q["isDemo"] = function () {console.log("demo");}
            this[key] = obj[key];
        }
    }
    var q = new njQuery();
    q.extend({
        isDemo: function () {
            console.log("demo");
        }
    });
    q.isDemo();
    */
    //将以上两种合起来写就是下面这样的,既可以添加静态方法,又可以添加实例方法
    njQuery.extend = njQuery.prototype.extend = function (obj) {
        //console.log(this);
        for(var key in obj){
            this[key] = obj[key];
        }
    }
     njQuery.extend({});
    var q = new njQuery();
    q.extend();
    
2.6判断dom元素是否加载完毕
  • /*
    onload事件会等到DOM元素加载完毕, 并且还会等到资源也加载完毕才会执行
    DOMContentLoaded事件只会等到DOM元素加载完毕就会执行回调
    */
    /*
    window.onload = function (ev) {
        // var res = document.querySelectorAll("div");
        // console.log(res);
        console.log("onload");
    }
    document.addEventListener("DOMContentLoaded", function () {
        // var res = document.querySelectorAll("div");
        // console.log(res);
        console.log("DOMContentLoaded");
    });
    但是低版本的浏览器不支持addEventListene
    */
    /*
    document.readyState属性有如下的状态
    uninitialized - 还未开始载入
    loading - 载入中
    interactive - 已加载,文档与用户可以开始交互
    complete - 载入完成
    
    onreadystatechange事件就是专门用于监听document.readyState属性的改变的
    */
    /*
    以下是在低版本浏览器中也能用
    document.attachEvent("onreadystatechange", function () {
        if(document.readyState == "complete"){
            console.log("onreadystatechange");
        }
    });
    */
    //综上所述,用来判断的方法就是
    if(document.addEventListener){
        document.addEventListener("DOMContentLoaded", function () {
            fn();
        });
    }else{
        document.attachEvent("onreadystatechange", function () {
            if(document.readyState == "complete"){
               fn();
            }
        });
    }
    
    $(function () {
        var res = document.querySelectorAll("div");
        console.log(res);
    });
    

实现以下方法

//jQ原型上的核心方法和属性:
/*
        
             1、jquery 获取jQ版本号
             2、selector 实例默认的选择器取值
             3、length 实例默认的长度
             3、push 给实例添加新元素
             4、sort 对实例中的元素进行排序
             5、splice 按照指定下标指定数量删除元素,也可以替换删除的元素

             6、toArray 把实例转换为数组返回
             7、get  获取指定下标的元素,获取的是原生DOM

             6、eq 获取指定下标的元素,获取的是jQuery类型的实例对象
             7、first 获取实例中的第一个元素,是jQuery类型的实例对象
             8、last 获取实例中的最后一个元素,是jQuery类型的实例对象

             9、each 遍历实例,把遍历到的数据传给回调使用
             10、map  遍历实例,把遍历到的数据传给回调使用,然后把回调的返回值收集起来组成一个新的数组返回
          */
//dom操作相关的方法
/*
             1、empty ==> 清空指定元素中的所有内容
             2、remove ==> 删除所有的元素或指定元素
             3、html ==> 设置所有元素的内容,获取第一个元素的内容
             4、text ==> 设置所有元素的文本内容,获取所有元素的文本内容
            5、元素.appendTo.指定元素 ==> 将元素添加到指定元素内部的最后
            6.元素.prependTo.指定元素 ==> 将元素添加到指定元素内部的最前面
            7、指定元素.append.元素 ==> 将元素添加到指定元素内部的最后
            8、指定元素.prepend.元素 ==> 将元素添加到指定元素内部的最前面
            9.元素.insertBefore.指定元素  ==>将元素添加到指定元素外部的前面
            10.next([expr]) 获取紧邻的后面同辈元素的元素
             11.prev([expr]) 获取元素紧邻的前一个同辈元素             
             12.元素.insertAfter.指定元素  ==>将元素添加到指定元素外部的后面
             13.指定元素.after.元素  ==>将元素添加到指定元素外部的后面
             14.元素.insertBefore.指定元素  ==>将元素添加到指定元素外部的前面
             15.指定元素.before.元素  ==>将元素添加到指定元素外部的前面
             16、元素.replaceAll.指定元素 ==> 替换所有指定元素
             17.指定元素.replaceWith.元素 ==> 替换所有指定元素
*/
//属性操作相关方法
/*
            1.attr(): 设置或者获取元素的属性节点值
            2.prop(): 设置或者获取元素的属性值
            3.css(): 设置获取样式
            4.val(): 获取设置value的值
            5.hasClass(): 判断元素中是否包含指定类
            6.addClass(): 给元素添加一个或多个指定的类
            7.removeClass(): 删除元素中一个或多个指定的类
            8.toggleClass(): 没有则添加,有则删除
*/
//事件操作相关方法
/*
          1.on(type, callback): 注册事件
          2.off(type, callback): 移出事件
*/

代码实现

(function( window, undefined ) {
    var njQuery = function(selector) {
        return new njQuery.prototype.init(selector);
    }
    njQuery.prototype = {
        constructor: njQuery,
        init: function (selector) {
            // 0.去除字符串两端的空格
            selector = njQuery.trim(selector);
            // 1.传入 '' null undefined NaN  0  false, 返回空的jQuery对象
            if(!selector){
                return this;
            }
            // 2.方法处理
            else if(njQuery.isFunction(selector)){
                njQuery.ready(selector);
            }
            // 3.字符串
            else if(njQuery.isString(selector)){
                // 2.1判断是否是代码片段 <a>
                if(njQuery.isHTML(selector)){
                    // 1.根据代码片段创建所有的元素
                    var temp = document.createElement("div");
                    temp.innerHTML = selector;
                    // 2.将创建好的一级元素添加到jQuery当中
                    [].push.apply(this, temp.children);
                }
                // 2.2判断是否是选择器
                else{
                    // 1.根据传入的选择器找到对应的元素
                    var res = document.querySelectorAll(selector);
                    // 2.将找到的元素添加到njQuery上
                    [].push.apply(this, res);
                }
            }
            // 4.数组
            else if(njQuery.isArray(selector)){
                // 转换为真数组
                var arr = [].slice.call(selector);
                // 将真数组数据添加到njQuery上
                [].push.apply(this, arr);
            }
            // 5.除上述类型以外
            else{
                this[0] = selector;
                this.length = 1;
            }
            // 返回njQuery
            return this;
        },
        jquery: "1.1.0",
        selector: "",
        length: 0,
        push: [].push,
        sort: [].sort,
        splice: [].splice,
        toArray: function () {
            return [].slice.call(this);
        },
        get: function (num) {
            // 没有传递参数
            if(arguments.length === 0){
                return this.toArray();
            }
            // 传递不是负数
            else if(num >= 0){
                return this[num];
            }
            // 传递负数
            else{
                return this[this.length + num];
            }
        },
        eq: function (num) {
            // 没有传递参数
            if(arguments.length === 0){
                return new njQuery();
            }else{
                return njQuery(this.get(num));
            }
        },
        first: function () {
            return this.eq(0);
        },
        last: function () {
            return this.eq(-1);
        },
        each: function (fn) {
            return njQuery.each(this, fn);
        }
    }
    njQuery.extend = njQuery.prototype.extend = function (obj) {
        for(var key in obj){
            this[key] = obj[key];
        }
    }
    // 工具方法
    njQuery.extend({
        isString : function(str){
            return typeof str === "string"
        },
        isHTML : function(str){
            return str.charAt(0) === "<" &&
                str.charAt(str.length - 1) === ">" &&
                str.length >= 3;
        },
        trim : function(str){
            if(!njQuery.isString(str)){
                return str;
            }
            // 判断是否支持trim方法
            if(str.trim){
                return str.trim();
            }else{
                return str.replace(/^\s+|\s+$/g, "");
            }
        },
        isObject : function(sele){
            return typeof sele === "object"
        },
        isWindow : function(sele){
            return sele === window;
        },
        isArray : function(sele){
            if(njQuery.isObject(sele) &&
                !njQuery.isWindow(sele) &&
                "length" in sele){
                return true;
            }
            return false;
        },
        isFunction : function(sele){
            return typeof sele === "function";
        },
        ready: function (fn) {
            // 如果已经加载过了, 那么直接调用回调
            if(document.readyState == "complete"){
                fn();
            }
            // 如果没有加载过,判断是否支持addEventListener方法, 支持就使用addEventListener方法监听DOM加载
            else if(document.addEventListener){
                document.addEventListener("DOMContentLoaded", function () {
                    fn();
                });
            }
            // 如果不支持addEventListener方法, 就使用attachEvent方法监听
            else{
                document.attachEvent("onreadystatechange", function () {
                    if(document.readyState == "complete"){
                       fn();
                    }
                });
            }
        },
        each: function (obj, fn) {
            // 1.判断是否是数组
            if(njQuery.isArray(obj)){
                for(var i = 0; i < obj.length; i++){
                   // var res = fn(i, obj[i]);
                   var res = fn.call(obj[i], i, obj[i]);
                   if(res === true){
                       continue;
                   }else if(res === false){
                       break;
                   }
                }
            }
            // 2.判断是否是对象
            else if(njQuery.isObject(obj)){
                for(var key in obj){
                    // var res = fn(key, obj[key]);
                    var res = fn.call(obj[key], key, obj[key]);
                    if(res === true){
                        continue;
                    }else if(res === false){
                        break;
                    }
                }
            }
            return obj;
        },
        map: function (obj, fn) {
            var res = [];
            // 1.判断是否是数组
            if(njQuery.isArray(obj)){
                for(var i = 0; i < obj.length; i++){
                    var temp = fn(obj[i], i);
                    if(temp){
                        res.push(temp);
                    }
                }
            }
            // 2.判断是否是对象
            else if(njQuery.isObject(obj)){
                for(var key in obj){
                    var temp =fn(obj[key], key);
                    if(temp){
                        res.push(temp);
                    }
                }
            }
            return res;
        },
        // 来源: http://www.w3school.com.cn/xmldom/prop_node_nextsibling.asp
        get_nextsibling: function (n) {
            var x = n.nextSibling;
            while (x != null && x.nodeType!=1)
            {
                x=x.nextSibling;
            }
            return x;
        },
        get_previoussibling: function (n) {
            var x=n.previousSibling;
            while (x != null && x.nodeType!=1)
            {
                x=x.previousSibling;
            }
            return x;
        },
        getStyle: function (dom, styleName) {
            if(window.getComputedStyle){
                return window.getComputedStyle(dom)[styleName];
            }else{
                return dom.currentStyle[styleName];
            }
        },
        addEvent: function(dom, name, callBack) {
            if(dom.addEventListener){
                dom.addEventListener(name, callBack);
            }else{
                dom.attachEvent("on"+name, callBack);
            }
        }
    });
    // DOM操作相关方法
    njQuery.prototype.extend({
        empty: function () {
            // 1.遍历指定的元素
            this.each(function (key, value) {
                value.innerHTML = "";
            });
            // 2.方便链式编程
            return this;
        },
        remove: function (sele) {
            if(arguments.length === 0){
                // 1.遍历指定的元素
                this.each(function (key, value) {
                    // 根据遍历到的元素找到对应的父元素
                    var parent = value.parentNode;
                    // 通过父元素删除指定的元素
                    parent.removeChild(value);
                });
            }else{
                var $this = this;
                // 1.根据传入的选择器找到对应的元素
                $(sele).each(function (key, value) {
                    // 2.遍历找到的元素, 获取对应的类型
                    var type = value.tagName;
                    // 3.遍历指定的元素
                    $this.each(function (k, v) {
                        // 4.获取指定元素的类型
                        var t = v.tagName;
                        // 5.判断找到元素的类型和指定元素的类型
                        if(t === type){
                            // 根据遍历到的元素找到对应的父元素
                            var parent = value.parentNode;
                            // 通过父元素删除指定的元素
                            parent.removeChild(value);
                        }
                    });
                })
            }
            return this;
        },
        html: function (content) {
            if(arguments.length === 0){
                return this[0].innerHTML;
            }else{
                this.each(function (key, value) {
                    value.innerHTML = content;
                })
            }
        },
        text: function (content) {
            if(arguments.length === 0){
                var res = "";
                this.each(function (key, value) {
                    res += value.innerText;
                });
                return res;
            }else{
                this.each(function (key, value) {
                    value.innerText = content;
                });
            }
        },
        appendTo: function (sele) {
            // 1.统一的将传入的数据转换为jQuery对象
            var $target = $(sele);
            var $this = this;
            var res = [];
            // 2.遍历取出所有指定的元素
            $.each($target, function (key, value) {
                // 2.遍历取出所有的元素
                $this.each(function (k, v) {
                    // 3.判断当前是否是第0个指定的元素
                    if(key === 0){
                        // 直接添加
                        value.appendChild(v);
                        res.push(v);
                    }else{
                        // 先拷贝再添加
                        var temp = v.cloneNode(true);
                        value.appendChild(temp);
                        res.push(temp);
                    }
                });
            });
            // 3.返回所有添加的元素
            return $(res);
        },
        prependTo: function (sele) {
            // 1.统一的将传入的数据转换为jQuery对象
            var $target = $(sele);
            var $this = this;
            var res = [];
            // 2.遍历取出所有指定的元素
            $.each($target, function (key, value) {
                // 2.遍历取出所有的元素
                $this.each(function (k, v) {
                    // 3.判断当前是否是第0个指定的元素
                    if(key === 0){
                        // 直接添加
                        value.insertBefore(v, value.firstChild);
                        res.push(v);
                    }else{
                        // 先拷贝再添加
                        var temp = v.cloneNode(true);
                        value.insertBefore(temp, value.firstChild);
                        res.push(temp);
                    }
                });
            });
            // 3.返回所有添加的元素
            return $(res);
        },
        append: function (sele) {
            // 判断传入的参数是否是字符串
            if(njQuery.isString(sele)){
                this[0].innerHTML += sele;
            }else{
                $(sele).appendTo(this);
            }
            return this;
        },
        prepend: function (sele) {
            // 判断传入的参数是否是字符串
            if(njQuery.isString(sele)){
                this[0].innerHTML = sele + this[0].innerHTML;
            }else{
                $(sele).prependTo(this);
            }
            return this;
        },
        insertBefore: function (sele) {
            // 1.统一的将传入的数据转换为jQuery对象
            var $target = $(sele);
            var $this = this;
            var res = [];
            // 2.遍历取出所有指定的元素
            $.each($target, function (key, value) {
                var parent = value.parentNode;
                // 2.遍历取出所有的元素
                $this.each(function (k, v) {
                    // 3.判断当前是否是第0个指定的元素
                    if(key === 0){
                        // 直接添加
                        parent.insertBefore(v, value);
                        res.push(v);
                    }else{
                        // 先拷贝再添加
                        var temp = v.cloneNode(true);
                        parent.insertBefore(temp, value);
                        res.push(temp);
                    }
                });
            });
            // 3.返回所有添加的元素
            return $(res);
        },
        insertAfter: function (sele) {
            // 1.统一的将传入的数据转换为jQuery对象
            var $target = $(sele);
            var $this = this;
            var res = [];
            // 2.遍历取出所有指定的元素
            $.each($target, function (key, value) {
                var parent = value.parentNode;
                var nextNode = $.get_nextsibling(value);
                // 2.遍历取出所有的元素
                $this.each(function (k, v) {
                    // 3.判断当前是否是第0个指定的元素
                    if(key === 0){
                        // 直接添加
                        parent.insertBefore(v, nextNode);
                        res.push(v);
                    }else{
                        // 先拷贝再添加
                        var temp = v.cloneNode(true);
                        parent.insertBefore(temp, nextNode);
                        res.push(temp);
                    }
                });
            });
            // 3.返回所有添加的元素
            return $(res);
        },
        replaceAll: function (sele) {
            // 1.统一的将传入的数据转换为jQuery对象
            var $target = $(sele);
            var $this = this;
            var res = [];
            // 2.遍历取出所有指定的元素
            $.each($target, function (key, value) {
                var parent = value.parentNode;
                // 2.遍历取出所有的元素
                $this.each(function (k, v) {
                    // 3.判断当前是否是第0个指定的元素
                    if(key === 0){
                        // 1.将元素插入到指定元素的前面
                        $(v).insertBefore(value);
                        // 2.将指定元素删除
                        $(value).remove();
                        res.push(v);
                    }else{
                        // 先拷贝再添加
                        var temp = v.cloneNode(true);
                        // 1.将元素插入到指定元素的前面
                        $(temp).insertBefore(value);
                        // 2.将指定元素删除
                        $(value).remove();
                        res.push(temp);
                    }
                });
            });
            // 3.返回所有添加的元素
            return $(res);
        },
        clone: function (deep) {
            var res = [];
            // 判断是否是深复制
            if(deep){
                // 深复制
                this.each(function (key, ele) {
                    var temp = ele.cloneNode(true);
                    // 遍历元素中的eventsCache对象
                    njQuery.each(ele.eventsCache, function (name, array) {
                        // 遍历事件对应的数组
                        njQuery.each(array, function (index, method) {
                            // 给复制的元素添加事件
                            $(temp).on(name, method);
                        });
                    });
                    res.push(temp);
                });
                return $(res);
            }else{
                // 浅复制
                this.each(function (key, ele) {
                    var temp = ele.cloneNode(true);
                    res.push(temp);
                });
                return $(res);
            }
        }
    });
    // 筛选相关方法
    njQuery.prototype.extend({
        next: function (sele) {
            var res = [];
            if(arguments.length === 0){
                // 返回所有找到的
                this.each(function (key, value) {
                    var temp = njQuery.get_nextsibling(value);
                    if(temp != null){
                        res.push(temp);
                    }
                });
            }else{
                // 返回指定找到的
                this.each(function (key, value) {
                    var temp = njQuery.get_nextsibling(value)
                    $(sele).each(function (k, v) {
                        if(v == null || v !== temp) return true;
                        res.push(v);
                    });
                });
            }
            return $(res);
        },
        prev: function (sele) {
            var res = [];
            if(arguments.length === 0){
                this.each(function (key, value) {
                    var temp = njQuery.get_previoussibling(value);
                    if(temp == null) return true;
                    res.push(temp);
                });
            }else{
                this.each(function (key, value) {
                    var temp = njQuery.get_previoussibling(value);
                    $(sele).each(function (k, v) {
                        if(v == null || temp !== v) return true;
                        res.push(v);
                    })
                });
            }
            return $(res);
        }
    });
    // 属性操作相关的方法
    njQuery.prototype.extend({
        attr: function (attr, value) {
            // 1.判断是否是字符串
            if(njQuery.isString(attr)){
                // 判断是一个字符串还是两个字符串
                if(arguments.length === 1){
                    return this[0].getAttribute(attr);
                }else{
                    this.each(function (key, ele) {
                        ele.setAttribute(attr, value);
                    });
                }
            }
            // 2.判断是否是对象
            else if(njQuery.isObject(attr)){
                var $this = this;
                // 遍历取出所有属性节点的名称和对应的值
                $.each(attr, function (key, value) {
                    // 遍历取出所有的元素
                    $this.each(function (k, ele) {
                        ele.setAttribute(key, value);
                    });
                });
            }
            return this;
        },
        prop: function (attr, value) {
            // 1.判断是否是字符串
            if(njQuery.isString(attr)){
                // 判断是一个字符串还是两个字符串
                if(arguments.length === 1){
                    return this[0][attr];
                }else{
                    this.each(function (key, ele) {
                        ele[attr] = value;
                    });
                }
            }
            // 2.判断是否是对象
            else if(njQuery.isObject(attr)){
                var $this = this;
                // 遍历取出所有属性节点的名称和对应的值
                $.each(attr, function (key, value) {
                    // 遍历取出所有的元素
                    $this.each(function (k, ele) {
                        ele[key] = value;
                    });
                });
            }
            return this;
        },
        css: function (attr, value) {
            // 1.判断是否是字符串
            if(njQuery.isString(attr)){
                // 判断是一个字符串还是两个字符串
                if(arguments.length === 1){
                    return njQuery.getStyle(this[0], attr);
                }else{
                    this.each(function (key, ele) {
                        ele.style[attr] = value;
                    });
                }
            }
            // 2.判断是否是对象
            else if(njQuery.isObject(attr)){
                var $this = this;
                // 遍历取出所有属性节点的名称和对应的值
                $.each(attr, function (key, value) {
                    // 遍历取出所有的元素
                    $this.each(function (k, ele) {
                        ele.style[key] = value;
                    });
                });
            }
            return this;
        },
        val: function (content) {
            if(arguments.length === 0){
                return this[0].value;
            }else{
                this.each(function (key, ele) {
                    ele.value = content;
                });
                return this;
            }
        },
        hasClass: function (name) {
            var flag = false;
            if(arguments.length === 0){
                return flag;
            }else{
                this.each(function (key, ele) {
                    // 1.获取元素中class保存的值
                    var className = " "+ele.className+" ";
                    // 2.给指定字符串的前后也加上空格
                    name = " "+name+" ";
                    // 3.通过indexOf判断是否包含指定的字符串
                    if(className.indexOf(name) != -1){
                        flag = true;
                        return false;
                    }
                });
                return flag;
            }
        },
        addClass: function (name) {
            if(arguments.length === 0) return this;

            // 1.对传入的类名进行切割
            var names = name.split(" ");
            // 2.遍历取出所有的元素
            this.each(function (key, ele) {
                // 3.遍历数组取出每一个类名
                $.each(names, function (k, value) {
                    // 4.判断指定元素中是否包含指定的类名
                    if(!$(ele).hasClass(value)){
                        ele.className = ele.className + " " + value;
                    }
                });
            });
            return this;
        },
        removeClass: function (name) {
            if(arguments.length === 0){
                this.each(function (key, ele) {
                    ele.className = "";
                });
            }else{
                // 1.对传入的类名进行切割
                var names = name.split(" ");
                // 2.遍历取出所有的元素
                this.each(function (key, ele) {
                    // 3.遍历数组取出每一个类名
                    $.each(names, function (k, value) {
                        // 4.判断指定元素中是否包含指定的类名
                        if($(ele).hasClass(value)){
                            ele.className = (" "+ele.className+" ").replace(" "+value+" ", "");
                        }
                    });
                });
            }
            return this;
        },
        toggleClass: function (name) {
            if(arguments.length === 0){
                this.removeClass();
            }else{
                // 1.对传入的类名进行切割
                var names = name.split(" ");
                // 2.遍历取出所有的元素
                this.each(function (key, ele) {
                    // 3.遍历数组取出每一个类名
                    $.each(names, function (k, value) {
                        // 4.判断指定元素中是否包含指定的类名
                        if($(ele).hasClass(value)){
                            // 删除
                            $(ele).removeClass(value);
                        }else{
                            // 添加
                            $(ele).addClass(value);
                        }
                    });
                });
            }
            return this;
        }
    });
    // 事件操作相关的方法
    njQuery.prototype.extend({
        on: function (name, callBack) {
            // 1.遍历取出所有元素
            this.each(function (key, ele) {
                // 2.判断当前元素中是否有保存所有事件的对象
                if(!ele.eventsCache){
                    ele.eventsCache = {};
                }
                // 3.判断对象中有没有对应类型的数组
                if(!ele.eventsCache[name]){
                    ele.eventsCache[name] = [];
                    // 4.将回调函数添加到数据中
                    ele.eventsCache[name].push(callBack);
                    // 5.添加对应类型的事件
                    njQuery.addEvent(ele, name, function () {
                        njQuery.each(ele.eventsCache[name], function (k, method) {
                            method.call(ele);
                        });
                    });
                }else{
                    // 6.将回调函数添加到数据中
                    ele.eventsCache[name].push(callBack);
                }
            });
            return this;
        },
        off: function (name, callBack) {
            // 1.判断是否没有传入参数
            if(arguments.length === 0){
                this.each(function (key, ele) {
                    ele.eventsCache = {};
                });
            }
            // 2.判断是否传入了一个参数
            else if(arguments.length === 1){
                this.each(function (key, ele) {
                    ele.eventsCache[name] = [];
                });
            }
            // 3.判断是否传入了两个参数
            else if(arguments.length === 2){
                this.each(function (key, ele) {
                    njQuery.each(ele.eventsCache[name], function (index, method) {
                        // 判断当前遍历到的方法和传入的方法是否相同
                        if(method === callBack){
                            ele.eventsCache[name].splice(index,  1);
                        }
                    });
                });
            }
            return this;
        }
    });
    njQuery.prototype.init.prototype = njQuery.prototype;
    window.njQuery = window.$ = njQuery;
})( window );
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342