前言
jq功能强大,是每个前端人的开发利器。本文用意不在实现一个像jq那样的强大函数库,只是对jq中常用功能做一个简单实现,构建一个自己的微型库。
正文
一、对外提供接口
//为了避免污染全局变量,采用闭包来处理
(function(){
function $(){}
window.$ = $;
})()
二、选择器功能的实现
//思考:在jq中$()返回一个对象,假如使用简单工厂模式即可实现,但是这样做显然有十分大的弊端。参考jq源码做如下处理
function $(selector,context){
return new $.fn.init(selector,context);
}
$.fn = $.prototype;
$.fn.init = function(selector,context){
if(selector.nodeType === 1){ // 如果是DOM节点直接返回
this[0] = selector;
this.length = 1;
return this;
}
var context = context || document;
var nodeList = context.querySelectorAll(selector);
this.length = nodeList.length;
for(var i=0; i<this.length; i++){
this[i] = nodeList[i];
}
}
$.fn.init.prototype = $.fn;
三、extend的实现
//主要实现方法的拷贝,jq中主要使用了拷贝继承,比较简单好理解
$.fn.extend = $.extend = function(destination,source){
if(typeof source === 'undefined'){
source = destination;
destination = this; //指向$.fn.init.prototype
}
for(var property in source){
if(source.hasOwnProperty(property)){
destination[property] = source[property];
}
}
return destination; //可以实现链式操作
}
四、each方法的实现
//主要实现对对象中elements的遍历操作
$.fn.extend({
each : function(callback){
for(var i=0; i<this.length; i++){
callback(this[i],i);
}
return this;
}
})
五、DOM相关方法的实现
//主要实现DOM操作,添加、删除、清空 (append、prepend、remove、empty)
$.fn.extend({
clone : function(){
var obj = {};
console.log(this[0].tagName.toLowerCase());
var node = document.createElement(this[0].tagName.toLowerCase());
node.innerHTML = this[0].innerHTML;
obj[0] = node;
return obj;
},
append : function(ele){
this.each(function(v,k){
v.appendChild($(ele).clone()[0]);
});
ele = null;
return this;
},
prepend : function(ele){
this.each(function(v,k){
v.insertBefore($(ele).clone()[0],v.childNodes[0]);
})
ele = null;
return this;
},
remove : function(){
this.each(function(v,k){
v.parentNode.removeChild(v);
});
return this;
},
empty : function(){
this.each(function(v,k){
v.innerHTML = '';
})
}
})
六、css方法的实现
$.fn.extend({
css : function(cssRules,value){
if($.isString(cssRules)){
if(value){ // $().css('color','red')
this.each(function(v,k){
v.style[cssRules] = value;
})
}else{ // $().css('width') 注意:获取时length=1
return window.getComputedStyle(this[0], null).getPropertyValue(cssRules);
}
}else{ // $().css({})
for(var att in cssRules){
this.each(function(v,k){
v.style[att] = cssRules[att];
})
}
}
}
})
七、DOM过滤相关方法的实现
$.fn.extend({
children : function(selector){
return $(selector,this[0]);
},
perent : function(){
return $(this[0].parentNode);
},
siblings : function(){
var obj = {};
var elesArr = [];
var j = 0;
var nodes = this[0].parentNode.childNodes;
for(var i=0; i<nodes.length; i++){
if(nodes[i].nodeType === 1){
elesArr.push(nodes[i]);
}
}
obj['length'] = elesArr.length - 1;
for(var i=0; i<elesArr.length; i++){
if(elesArr[i]!==this[0]){
obj[j] = elesArr[i];
j++;
}
}
return obj;
}
})
八、实现属性的操作以及class的操作
$.fn.extend({
attr : function(attr,value){
if(value){
this.each(function(v,k){
v[attr] = value;
})
return this;
}else{
return this[0].getAttribute(attr);
}
},
removeAttr : function(attr){
this.each(function(v,k){
v.removeAttrbute(attr);
})
return this;
},
hasClass : function(className){
var classList = this[0].classList;
for(var i=0; i<classList.length; i++){
if(classList[i] === className){
return true;
}
}
return false;
},
addClass : function(className){
this.each(function(v,k){
if(!$(v).hasClass(className)){
console.log(v.className + ' ' + className);
v.className += ' ' + className;
}
})
},
removeClass : function(className){
this.each(function(v,k){
if($(v).hasClass(className)){
var newClass = v.className.split(' ').filter(function(curr,index){
return curr !== className;
}).join(' ');
v.className = newClass;
}
})
},
html : function(htmlText){
if(htmlText){
this.each(function(v,k){
v.innerHTML = htmlText;
})
}else{
return this[0].innerHTML;
}
}
})
九、事件相关操作
//jq中采用的是事件绑定,类似的下面也采用事件绑定
$.fn.extend({
on : function(event,fn){
this.each(function(v,k){
v.addEventListener(event,fn,false);
});
return this;
},
off : function(event,fn){
this.each(function(v,k){
v.removeEventListener(event,fn,false);
});
return this;
},
trigger : function(event){
var evt = document.createEvent('HTMLEvents');
evt.initEvent(event,false,false);
this.each(function(v,k){
v.dispatchEvent(evt);
});
return this;
}
})
十、ajax
//注意:Promise对象已在ES6中规范,不再自己实现
$.extend({
ajax : function(opts){
var xhr = new XMLHttpRequest(),
type = opts.type || 'GET',
url = opts.url,
params;
params = (function(obj){ //传输的参数
var str = '';
for(var k in obj){
str += k + '=' + obj[k] + '&';
}
str = str.slice(0, str.length-1);
})(opts.data)
type = type.toUpperCase();
if(type === 'GET'){
url += '?' + params;
}
xhr.open(type, url);
if(type === 'POST'){
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
}
xhr.send(params ? params : null);
return new Promise(function(resolve, reject){
xhr.onload = function(){
if(xhr.status === 200){
resolve(xhr.responseText);
}else{
reject(xhr.status);
}
}
})
}
})
十一、Cookie
$.extend({
cookie: function (cookieName, cookieValue, day) {
var readCookie = function (name) {
var arr,
reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'),
matched = document.cookie.match(reg);
if(arr = matched) {
return unescape(arr[2]);
} else {
return null;
}
};
var setCookie = function (name, value, time) {
var Days = time || 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
};
if (cookieName && cookieValue) {
setCookie(cookieName, cookieValue, day);
} else if (cookieName && $.isNull(cookieValue)) {
setCookie(cookieName, '', -1);
} else if (cookieName) {
return readCookie(cookieName);
}
}
})
结语
在实现的过程中没有考虑过多的细节,如浏览器的差异等。欢迎补充。