?模仿jquery,写了自己的框架,只不过jquery的选择器是css的选择器功能没有实现,这个感觉有点困难,毕竟都是正则表达式,在写自己的框架的同时,也能了解一下jquery的工作原理,更好的进行开发
?
事先声明:此js比不了jquery只是在编写的过程中以个人的思路模仿jquery的部分功能 - 插件是怎么开发的,扩展是如何实现的,等等。jquery有自己强大的类似于css的选择器,我只做了id的功能
最外层的是通过
(function(){
......... //代码
})()
包装起来,为了防止和其他js声明的变量冲突
构造函数 _T = function(obj){........} 封装了DOM Element 对象,以便可以使用自己定义的方法(函数)去对这个对象进行各种原生js的操作. obj这个参数可以是DOM element的id 也可以是DOM element本身 或者是 _T创建的对象。
通过 window.t = window.ThirdteenDevil = function(obj){return new _T(obj)} 来返回由_T封装的对象,把window.t定义成返回一个由_T创建的对象的函数,使用的时候类似于t("id"),就是jquery中的$("#id"),这样可以获取到_T类创建的对象,以便使用自定义的各种方法
通过prototype属性为_T添加各种方法
_T.prototype={
? ? css:function(){},
? ? attr:function(){},
? ? data:function(){},
? ? ...
? ???
}
这样一来,通过t(obj)得到的对象就可以使用以上css,attr,data....等各种方法操作obj对象了。方法可以由使用此js的人自己扩展,类似于jquery的方法扩展。这个扩展功能依然是使用prototype来实现的。
至于插件,同jquery的插件扩展
扩展格式:
(function(t){
? ? t.plugs.自定义的插件名称 = function(){
? ?? ?? ? ......
? ?? ?? ? ......
? ?? ?? ? .....//内容
? ? }
})(t)
使用上面的格式依然是防止和其他变量冲突可以在(function(){....})()的作用域中定义自己的变量。
t.plugs是已经定义好的一个空的对象,通过给这个对象赋属性来扩展插件。
以下是代码??源文件和例子在附件中
/* * thd-simple.js * begin : 2010-11-12 * author : ThirdteenDevil * e-mail : thirdteendevil@163.com * qq : 181907667 */ (function(){ var _T = function(obj){ //原生DOM element this.el = null; //ThirdteenDevil对象的标记 this.constructor = "ThirdteenDevil"; //如果是ThirdteenDevil对象则返回形式参数 if(obj.constructor == "ThirdteenDevil"){ return obj; } if(typeof(obj) == "string"){ if(document.getElementById(obj)){ this.el = document.getElementById(obj); } }else{ this.el = obj; } } window.t = window.ThirdteenDevil = function(obj){return new _T(obj)} _T.prototype = { /* *回调函数,用于处理所有方法中function类型的参数 *@param obj 任何类型 *@return 如果@param obj是一个函数类型的对象,则返回这个函数的返回值,如果是其他类型则直接返回@param obj */ _preFormCallBack : function(obj){ var _this = this; var ret = obj; if(typeof(obj) == "function"){ ret = obj.call(_this.el,_this.el); } return ret; }, /* *如果是无参数调用 .html() *@return 对象的innerHTML属性 *--------------------------------------- *如果是.html(str) *@param str 设置对象的innerHTML值 *@return this 用于链式调用 */ html : function(v){ if(v == undefined){ return this.el.innerHTML; }else{ var v = this._preFormCallBack(v); this.el.innerHTML = v; return this; } }, /* *如果是一个参数调用 .attr(str) *@param str DOM element的属性名 *@return Dom element名为@param str属性的值 *---------------------------------------- *如果是二个参数 .attr(str,v) *@param str DOM element的属性名 *@param v Dom element @param str 属性的值 *@return this 用于链式调用 */ attr : function(){ if(arguments.length == 1){ return this.el.getAttribute(arguments[0]); } if(arguments.length == 2){ this.el.setAttribute(arguments[0],arguments[1]); return this; } }, /* *除去DOM element的属性 *@param str DOM element的属性名称 *@return this 用于链式调用 */ removeAttr : function(str){ this.el.removeAttribute(str); return this; }, /* *如果是无参调用 .css() *@return DOM element 的所有css属性 *----------------------------------------- *如果是.css(str) *@param str css属性名 可以根据.css()方法来查询 *@return 获取参数对应的css属性 *---------------------------------------- *如果是.css(str,value) *@param str css属性名 可以根据.css()方法来查询 *@value 设置@param str的值为@value *@return this 用于链式调用 */ css : function(){ var CurrentStyle = function(element){return element.currentStyle || document.defaultView.getComputedStyle(element, null);} if( (arguments.length < 1) || (arguments.length > 2) ){ if(arguments.length == 0){ var cssAttrMap = []; for(var pro in CurrentStyle(this.el)){ cssAttrMap.push([pro,CurrentStyle(this.el)[pro]]); } return cssAttrMap; }else{ throw new Error("css function arguments must be between 1 and 2 ! "); } }else{ if(arguments.length == 1){ return CurrentStyle(this.el)[arguments[0]]; }else{ this.el.style[arguments[0]] = this._preFormCallBack(arguments[1]); return this; } } }, /* *如果是无参调用 .hasClass() *@return className属性值 *----------------------------------- *如果是.hasClass(v) *@param v className, *@return 判断DOM element是否含有名为@param v的className属性 */ hasClass : function(v){ if(v == undefined){ return this.el.className; }else{ if(this.el.className != undefined){ var classStr = " " + this.el.className + " "; var rep = new RegExp(" " +v+ " ","gi"); return rep.test(classStr); }else{ return false; } } }, /* *为DOM element添加className属性值 *@param v 要添加的className属性值 *@return this用于链式调用 */ addClass : function(str){ var v = this._preFormCallBack(str); if(this.hasClass(v)){ return this; }else{ if(this.el.className){ this.el.className += (" "+v); return ; }else{ this.el.className = v; } } }, /* *为Dom element删除className属性值 *@param v 要删除的className属性值 *@return this用于链式调用 */ removeClass : function(v){ if(this.hasClass(v)){ var classStr = " " + this.el.className + " "; var rep = new RegExp(" " + v + " ","gi"); this.el.className = classStr.replace(rep," "); }else{ return this; } }, /* *为DOM element 添加/删除一个className属性值,有则删除,无则添加 *@param str className的属性值也可以是一个回调函数,返回className的属性值 *@return this 用于链式调用 */ toggleClass : function(str){ var v = this._preFormCallBack(str); if(this.hasClass(v)){ this.removeClass(v); }else{ this.addClass(v); } return this; }, /* *如果无参调用 .scrollTop() *@return DOM element scrollTop属性值 *---------------------------------------------- *一个参数调用 .scrollTop(x) *@param x DOM element scrollTop属性值 *@return this 用于链式调用 */ scrollTop : function(){ if(arguments.length == 1){ this.el.scrollTop = arguments[0]; }else{ return this.el.scrollTop; } }, /* *如果无参调用 .scrollLeft() *@return DOM element的scrollLeft属性值 *---------------------------------------------- *一个参数调用 .scrollLeft(x) *@param x DOM element scrollLeft属性值 *@return this 用于立案时调用 */ scrollLeft : function(){ if(arguments.length == 1){ this.el.scrollLeft = arguments[0]; }else{ return this.el.scrollLeft; } }, /* *为DOM element添加事件 *@param act : 事件名 click,mouseover,mouseout .... *@param cb : Function类型的回调函数 *@param pObj : json格式的参数,回调函数中调用 */ on : function(act,cb,pObj){ var _this = this; if(window.addEventListener){//FF ... this.el.addEventListener(act,function(e){ var e = e ? e : event; //e.stopPropagation();//清冒泡 cb.call(_this.el,e,_this.el,pObj)},false); }else if(window.attachEvent){//IE this.el.attachEvent("on"+act,function(e){ var e = e ? e : event; //window.event.cancelBubble = true;//清冒泡 cb.call(_this.el,e,_this.el,pObj)}); } return this; }, /* *把DOM element添加到this.el对象中 *@param obj 可以是ThirdteenDevil对象 也可以是id字符串 或者是DOM element本身 *@return this 用于链式调用 */ append : function(obj){ //把obj转成ThirdteenDevil对象后操作 this.el.appendChild(new _T(obj).el); }, /* *把this.el对象添加到DOM element中 *@param obj 可以是ThirdteenDevil对象 也可以是id字符串 或者是DOM element本身 *@return this 用于链式调用 */ appendTo : function(obj){ //把obj转成ThirdteenDevil对象后操作 new _T(obj).el.appendChild(this.el); }, /* *getElementsByTagName实现 *@param tag 元素标签名 *@return 获取子元素标签是@param tag的所有子元素(已转成数组类型) */ getElementsByTagName : function(tag){ var childs = this.el.getElementsByTagName(tag); var childArray = [] for(var i = 0 , j = childs.length ; i < j ; i++){ childArray.push(childs[i]); } return childArray; }, /* *获取ThirdteenDevil中el对象的子对象有name属性为形参的对象集合 *@param nameStr name属性的值 *@return ThirdteenDevil中el对象的子对象有name属性为形参的对象集合(已转成数组) */ getElementsByName : function(nameStr){ var els = [] this.getElementsByTagName("*").each(function(o,i){ if(nameStr == new _T(o).attr("name")){ els.push(o); } }) return els; }, /* *如果是无参调用 .data() *@return Dom Element的data属性 *---------------------------------- *如果是一个字符串类型的参数 .data(str) *@param str data的属性名 *@return data中@param str的属性值 *---------------------------------- *如果是一个对象类型的参数 .data(obj) *@param obj 拷贝obj的所有属性刀data中 *@return this 用于链式调用 *---------------------------------- *如果是2个参数 .data(str,obj) *@param str 设置data中的属性 *@param obj data中名为@param str的值 *@return this 用于链式调用 */ data : function(){ if(this.el.getAttribute("data") == undefined){ this.el.setAttribute("data",{}); } if(arguments.length == 1){ if(typeof(arguments[0]) == "string"){ return this.el.getAttribute("data")[arguments[0]]; }else if(typeof(arguments[0]) == "object"){ for(var pro in arguments[0]){ this.el.getAttribute("data")[pro] = arguments[0][pro]; } return this; } } if(arguments.length == 2){ this.el.getAttribute("data")[arguments[0]] = arguments[1]; return this; } if(arguments.length == 0){ return this.el.getAttribute("data"); } } }//_T.prototype end /* *扩展_T方法 * *如果是一个参数 .extend(obj) *@param obj 封装好将要扩展的属性或方法集合 *------------------------------------------ *如果是二个参数 .extend(str,v) *@param str 扩展的属性名 *@param v 扩展的属性名@param str的值或方法 * *如果自定义的属性和_T中的属性重名 不会覆盖原有属性 */ ThirdteenDevil.extend = function(){ if(arguments.length == 1){ for(var pro in arguments[0]){ _T.prototype[pro] = arguments[0][pro]; } } if(arguments.length == 2){ _T.prototype[arguments[0]] = arguments[1]; } } //插件 ThirdteenDevil.plugs = {}; })() //数组循环执行 Array.prototype.each = function(cb){ for(var i = 0 , j = this.length ; i < j ; i++){ cb.call(this[i],this[i],i); } } //数组移除某个值 Array.prototype.remove = function(obj){ var r = null; for(var i = 0 , j = this.length ; i < j ; i++){ if(obj == this[i]){ r = this.splice(i,1); break; }; } return r; } //得到数组中某个元素在数组中的索引值 Array.prototype.index = function(obj){ for(var i = 0 , j = this.length ; i < j ; i++){ if(obj == this[i]){ return i; }; } } //字符串去除前后空格 String.prototype.trim= function(){ return this.replace(/(^\s*)|(\s*$)/g, ""); }
?
?
?
方法的扩展:
t.extend("tt",function(){ alert(this.el.value) }) t("a").on("keyup",function(e,o){ t(this).tt(); }) t.extend({ t1: 1, t2: 2, id : "a", el : "1",//属性相同不会被覆盖 m1 : function(){ alert(this.el) alert(this.el.id); alert(this.t1); } }) t("b").on("click",function(e,o){ t(this).m1(); alert("t1 :" + t(this).t1) })
?
插件的编写:
(function(t){ t.plugs.ScrollDoor = function(obj){ this.menus = obj.menus; this.contents = obj.contents; this.hoverClass = obj.hover; this.blurClass = obj.blur; this.focusClass = obj.focus; this.evt = "click" || obj.evt; } t.plugs.ScrollDoor.prototype = { _clear : function(){ var _this = this; this.menus.each(function(m,i){ t(m).removeClass(_this.hoverClass); t(m).removeClass(_this.focusClass); }) if((this.contents != undefined) && (this.contents.length != 0)){ this.contents.each(function(m,i){ t(m).css("display","none"); }) } }, _hasContents : function(){ if((this.contents != undefined) && (this.contents.length != 0)){ return true; }else{ return false; } }, focus : function(index){ this._clear(); t(this.menus[index]).addClass(this.focusClass); if((this.contents[index] != undefined) && (this.contents.length != 0)){ t(this.contents[index]).css("display","block"); } }, run : function(){ var _this = this; this.menus.each(function(m,i){ t(m).on(_this.evt,function(e,o){ _this.focus(i); }) }) }, add : function(){ this.menus.push(arguments[0]); if(this._hasContents()){ this.contents.push(arguments[1]); } this.run(); this.focus(this.menus.length - 1); } } })(ThirdteenDevil) var sd = new t.plugs.ScrollDoor({ menus : ["m01","m02","m03"], contents : ["c01","c02","c03"], hover : "hover", focus : "focus", blur : "blur", evt : "click" }) sd.run(); sd.add("m04","c04");
?
所有文件都在附件中,详细例子见附件
?
1,append 函数跟appendTo貌似忘记返回this了
2 , 为什么不添加个each函数,这样可以把你的包装器T函数扩展为接受一个dom对象数组(比如以常见的“,”分割接受多个id值),反正只是自己用的方便库总共也就10几20个函数 每个函数利用个each函数为包装器中的所有dom对象做一次操作比如(挑个简单的)
append : function(var){
this.each(function(eli){
eli.appendChild(new _T(var));
})
}
关于each函数(each:function(fn){..............})的实现无非就是对包装器对象里的对象数组的一次遍历,有个关键点就是类似你这个事件添加函数的做法用fn.call(this,this.le[i]),使之fn第一个参数指向当前遍历到的dom对象。
而且既然你在后面已经添加了byTagName等返回Dom数组的方法,有了each函数就可以返回一个包装器对象了,这样在形式上也统一一点。
3,事件添加函数,没那个json参数会好读非常多。可能看的书还不够深入,但是我还是觉得这个参数在实际应用中出现的概率不会太大。
4,看了你整理的库还是学到了很多东西,你是直接看的jquery的库学习javascript的吗?
你的博客写的很好,不想别的人东西基本都是挪来挪去的。