封装自已的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里面,我们经常要去操作innerHTML
、innerText
以及 其它属性等操作,我们现在可以按照上面的方式来实现这些方法的封装
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
的长度判断,我们实现了调用getAttribute
与setAttribute
方法(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;
}
});
在上面的代码里面,我们已经可以得到addClasss
、removeClass
以及toggleClass
这三个方法了,这三个方法分别对象classList
的add(),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
对当前元素实行隐藏
在本文档的第一节,我们对常用的事件click
与dblclick
进行了封装,但是还有一些其它的事件我们没有封装,这个时候我们需要考虑要对其它所有的事情也要做到封装。但是我们又不能将所有的事件都封装成方法,这个时候,可以进行如下操作
通用事件方法绑定
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里面的一些常用的操作都封装起来了,当然,如果还想要封装其它的方法,也可以按照上面的形式进行封装
评论区