目 录CONTENT

文章目录

封装自己的jQuery

Administrator
2020-07-24 / 0 评论 / 0 点赞 / 6636 阅读 / 9561 字

封装自已的jQuery

jQuery是后期我们需要学习的一个框架 ,是继prototype 之后又一个优秀的框架 ,在里面提供了一些方法及属性供我们使用,现在我们根据自己所学的基础知识去完成一个自己封装的框架myQuery

第一步 :创建选择器的方法

Object.defineProperty(window, "$", {
   value: function (selector) {
       if (selector instanceof Element) {
           //说明是一个元素
           return [selector];
       } else {
           var list = document.querySelectorAll(selector);
           return Array.prototype.slice.call(list);
       }

   },
   configurable: false,
   enumerable: false,
   writable: false
})

在上面的方法里面,我们已经在全局对象window上面添加了一个属性,同时指定了value的值为一个function在这个方法的内部,我们判断了参数select的类型,这样做的目的是为了实现在方法内部的$(this)这种情况,在方法的内部this指当前元素

第二步:给封装的对象Array的原型上面去添加一些常用的方法

首先在这里我们要注意,我们始终都是数组这个东西做为最基本的对象,通过document.querySelectorAll这个方法得到的是一个NodeList集合,我们现在将这个NodeList通过Array.prototype.slice转换成了数组

常用事件方法的封装

Object.defineProperty(Array.prototype, "click", {
    value: function (callBack) {
        //this指向当前调用的对象,也就是数组
        this.forEach(function (item, index, a) {
            item.addEventListener("click", callBack);
            //callBack这个回调函数里面的this指向了当前DOM,事件绑定对象
        });
        return this;
    }
});

上面封装的是单击事件,当这个方法封装完成以后,我们在页面就可以使用如下方法对元素进行click的事件绑定了

$("#ul1>li").click(function(event){
    console.log(this);
})

接下来,我们按照上面的代码再去封装一个常用的双击事件方法

Object.defineProperty(Array.prototype, "dblclick", {
    value: function (callBack) {
        this.forEach(function (item, index, a) {
            item.addEventListener("dblclick", callBack);
        })
        return this;
    }
});

在上面两个方法里面,我们在每个方法的后面都返回了this,而this指向了当前的对象,也就是一个数组,这个我们通过这种方式可以实现链式语法


第三步 :常用属性的设置与获取

在DOM里面,我们经常要去操作innerHTMLinnerText以及 其它属性等操作,我们现在可以按照上面的方式来实现这些方法的封装

innerHTML 属性的封装

Object.defineProperty(Array.prototype, "html", {
    value: function (str) {
        //在这里,我们要判断它的参数
        if (arguments.length == 1) {
            //设置值
            this.forEach(function (item, index, a) {
                item.innerHTML = str;
            });
            return this;
        } else if (arguments.length == 0) {
            //这说明它没有参数,所以是取值
            return this.length > 0 ? this[0].innerHTML : "";

            /*这是批量取
            return this.map(function(item,index,a){
                return item.innerHTML;
            })
            */
        }
    }
});

innerText属性的封装

Object.defineProperty(Array.prototype, "text", {
    value: function (str) {
        //在这里,我们要判断它的参数
        if (arguments.length == 1) {
            //这里是设置值
            this.forEach(function (item, index, a) {
                item.innerText = str;
            });
            return this;
        } else if (arguments.length == 0) {
            //这说明它没有参数,所以是取值
            return this.length > 0 ? this[0].innerText : "";
        }

    }
});

上面的两个方法封装完成以后,我们可以通过调用html()以及text()方法实现对innerHTML 以及innerText属性的取值与赋值

取值的时候,返回的是第一个,赋值的时候是批量赋值

$("#ul1>li").html("<a href='#'>我在赋值到innerHTML</a>");
$("p").text("我在赋值到innerText");
$("#ul1>").html();  //没有设置参数,所以是取值
$("p").text();  //没有参数,所以也是取值

attributes属性的操作的封装

Object.defineProperty(Array.prototype,"attr",{
    value:function(){
        if(arguments.length==1){
            //一个参数代表取值   取值的时候只返回第一个
            return this.length>0?this[0].getAttribute(arguments[0]):"";
        }
        else if(arguments.length==2){
            //二个参数
            //遍历所有选中的元素,实现属性的赋值
            var arg=arguments;   //思考:为什么这里不能直接使用arguments
            this.forEach(function(item,index,a){
                item.setAttribute(arg[0],arg[1]);
            });
            //形成链式语法
            return this;
        }
    }
})

通过对参数arguments的长度判断,我们实现了调用getAttributesetAttribute 方法(1个参数是取值,两个参数是赋值)


上面的方法都是对属性进行了封装,现在我们对classList这个操作进行封装

classList属性的操作封装

Object.defineProperty(Array.prototype, "addClass", {
    value: function (cName) {
        this.forEach(function (item, index, a) {
            item.classList.add(cName);
        });
        return this;
    }
});
Object.defineProperty(Array.prototype, "removeClass", {
    value: function (cName) {
        this.forEach(function (item, index, a) {
            item.classList.remove(cName);
        });
        return this;
    }
});
Object.defineProperty(Array.prototype, "toggleClass", {
    value: function (cName) {
        this.forEach(function (item, index, a) {
            item.classList.toggle(cName);
        })
        return this;
    }
});

在上面的代码里面,我们已经可以得到addClasssremoveClass以及toggleClass 这三个方法了,这三个方法分别对象classListadd(),remove()以及toggle() 这三个方法

但是我们仍然能够发现问题,我们每次都调用了Object.defineProperty这个方法去添加特殊属性,这样很麻烦。在之前的时候,我们也学过使用Object.defineProperties这个方法来实现同时定义多个属性或方法,现在我们下面的代码就使用这种新的方式去定义


对当前元素周围元素的选取方法的封装

Object.defineProperties(Array.prototype, {
    //获取当前元素的父级元素
    parent: {
        value: function () {
            return this.map(function (item, index, a) {
                return item.parentElement;
            })
        }
    },
    //区域当前元素的子元素
    children: {
        value: function () {
            var arr = [];
            this.forEach(function (item, index, a) {
                // item.children  HTMLCollection
                for (var i = 0; i < item.children.length; i++) {
                    arr.push(item.children[i]);
                }
            });
            return arr;
        }
    },
    //获取当前元素的后面一个元素
    next: {
        value: function () {
            return this.map(function (item, index, a) {
                return item.nextElementSibling;
            }).filter(function (item, index, a) {
                return item != null;
            });
        }
    },
    //获取当前元素的前面一个元素
    prev:{
        value: function () {
            return this.map(function (item, index, a) {
                return item.previousElementSibling;
            }).filter(function (item, index, a) {
                return item != null;
            });
        }
    },
    //hide隐藏当前元素
    hide:{
        value:function(){
            this.forEach(function(item,index,a){
                item.style.display="none";
            });
        }
    }
});

上在我们对parent,children,next,prev这四个方法进行了封装,可以获取父级的,子级的,前面的以及后面的元素了,同时还定义了一个方法hide对当前元素实行隐藏


在本文档的第一节,我们对常用的事件clickdblclick进行了封装,但是还有一些其它的事件我们没有封装,这个时候我们需要考虑要对其它所有的事情也要做到封装。但是我们又不能将所有的事件都封装成方法,这个时候,可以进行如下操作

通用事件方法绑定

Object.defineProperty(Array.prototype,"on",{
    value:function(){
        var arg=arguments;  // 参数数组
        if(arguments.length==2){
            //第一个参数代表事件名,第二个参数代表回调函数
            this.forEach(function(item,index,a){
                /*
                var _fn=arg[1];
                item.addEventListener(arg[0],_fn);
                */

                item.addEventListener.apply(item,arg);
            });
            return this;
        } 
        else if(arguments.length==3){
            //三个参数  
            //第一个参数代表事件名称,第二个参数代表要进行判断的事件触发者,第三个参数代表回调
            this.forEach(function(item,index,a){
                item.addEventListener(arg[0],function(event){
                    var e=event||window.event;
                    // 对事件触发者进行判断,看是否是我需要触的事件
                    if(e.target.matches(arg[1])){
                        //条件成立,进行事件调用
                        var _fn=arg[2];
                        _fn.call(e.target,e);  //调用方法,把this指向当前的事件触发者
                    }
                })
            });
            return this;
        }       
    }
});

经过上面的绑定以后,我们可以对其它的事件也进行方法绑定了

普通的绑定

$("#ul1>li").on("click",function(event){
    console.log("我是常规的方式进行的绑定");
})

事件委托类型的绑定

$("#ul1").on("click","li",function(event){
   console.log("我是事件委托方式的绑定")
})

注意,在上面的代码里面,事件本身还是绑定在id="ul1"这个元素上面,只是现在它有三个参数,第一个参数代表要绑定的事件名称,第二个参数代表要判断的事件触者,第三个代表回调函数


至此,我们已经可以把jQuery里面的一些常用的操作都封装起来了,当然,如果还想要封装其它的方法,也可以按照上面的形式进行封装

0

评论区