所谓造轮子的好处就是复习知识点,加深对原版jquery的理解。
本文系笔者学习jquery的笔记,记述一个名为"dQuery"的初级版和缩水版jquery库的实现。主要涉及知识点包括面向对象,jquery,绑定,脚本化css等。


一. jquery的美元符意味什么?

先思考alert(typeof $)的结果中的$,它是一个对象,也是一个函数。
所以美元符字面上是jQuery,其实就是一个jq对象,里面可以接受函数,字符串(#xxx,.xxx,xxx...),还有一种是对象(比如this)。

dQuery是基于面向对象实现的,所以需要先写构造函数。为了方便遍历和其它方法操作,所有内容返回到一个数组中。这个数组依附于dQuery对象存在,之后将为这个dquery对象属性添加方法。

  1. function dQuery(vArg){//参数是变体变量
  2. this.elements=[];//选择器选择的元素扔到这个数组中
  3. }

因为新的对象可以接受三种类型的参数(字符串,对象,函数)。作为一个变体变量,根据匈牙利命名法可以用vXxx命名,同时需要分类讨论变量的情况:

1.当参数为函数时,执行函数——window.onload.——不好。

jquery写作时,经常允许多个$(function()),但是写window.onload会导致最终只执行最后一个。这时需要给函数绑定window.onload事件。
绑定事件的函数如下:

  1. function myAddEvent(obj,sEv,fn){
  2. if(obj.attachEvent){
  3. obj.attachEvent('on'+sEv,fn);
  4. }else{
  5. obj.addEventListener(sEv,fn,false);
  6. }
  7. }

所以,当为id选择器时,查找:document.getElementById(vArg.substring(1));并把它加到this.elements中去。

2.当参数为字符串时,执行选择器操作.

这里需要对id选择器,类选择器,元素选择器进行判断。
注意:把选择器结果丢到一个数组(dQuery.elements)中去。方便遍历事件。
用charAt进行判断参数首位:(#,.或是其它)

(1)首位为#时:

执行document.getElementById,同时把这个字符串稍作处理,用substring(1)方法把首个字符给去掉。

(2)首位为.

选取class类。可以设计一个getByClass函数

  1. function getByClass(oParent,sClass){
  2. var aEle=oParent.getElementsByTagName('*');//选择父元素的所有元素
  3. var aResult=[];
  4. var re=new RegExp('\\b'+sClass+'\\b','i');//正则边界
  5. var i=0;
  6. for(i=0;i<aEle.length;i++){
  7. if(re.test(aEle[i].className)){
  8. aResult.push(aEle[i]);
  9. }
  10. }
  11. return aResult;
  12. }

在本实例中,oParent父级对象实际上就是document,另一方面,因为getByClass返回的对象实际是一个数组,所以:

  1. this.elements=getByClass(document,vArg.substring(1));
(3)参数为对象时,返回一个对象本身

把这个对象push到

(4)绑定基本的事件——比如点击事件

在接下来就是绑定事件。比如click(function(){}),它是一个回调函数。需要用到原型(prototype)方法。

  1. //对选择器函数绑定click事件
  2. dQuery.prototype.click=function(fn){
  3. var i=0;
  4. //对于返回器数组的内容
  5. for(i=0;i<this.elements.length;i++){
  6. myAddEvent(this.elements[i],'click',fn);
  7. }
  8. }
(5)简写设置

到目前为止,在调用这个dQuery时,每次都需要这样写:

  1. new dQuery(function(){
  2. new dQuery('input').click(function(){
  3. alert('a');
  4. })
  5. });

每次都要使用new,不用就会出问题。
如果我想使用类似jquery的简写方式,使用$d作为简写,可以通过一个函数来定义:

  1. function $d(vArg){
  2. return new dQuery(vArg);
  3. }

所以当前的最终代码为:

  1. //可重复调用的加载函数
  2. function myAddEvent(obj,sEv,fn){
  3. if(obj.attachEvent){
  4. obj.attachEvent('on'+sEv,fn);
  5. }else{
  6. obj.addEventListener(sEv,fn,false);
  7. }
  8. }
  9. //class选择器调用函数
  10. function getByClass(oParent,sClass){
  11. var aEle=oParent.getElementsByTagName('*');//选择父元素的所有元素
  12. var aResult=[];
  13. var re=new RegExp('\\b'+sClass+'\\b','i');//正则边界
  14. var i=0;
  15. for(i=0;i<aEle.length;i++){
  16. if(re.test(aEle[i].className)){
  17. aResult.push(aEle[i]);
  18. }
  19. }
  20. return aResult;
  21. }
  22. //定义dQuery对象
  23. function dQuery(vArg){//参数是变体变量
  24. this.elements=[];//选择器选择的元素扔到这个数组中
  25. switch(typeof vArg){
  26. //如果参数是函数
  27. case 'function':
  28. myAddEvent(window,'load',vArg);
  29. break;
  30. //如果参数是字符串
  31. case 'string':
  32. switch(vArg.charAt(0)){
  33. case '#'://id选择器参数应该为#号之后的字符段
  34. var obj=document.getElementById(vArg.substring(1));
  35. this.elements.push(obj);
  36. break;
  37. case '.'://class
  38. this.elements=getByClass(document,vArg.substring(1));
  39. break;
  40. default://标签
  41. this.elements=document.getElementsByTagName(vArg);
  42. }
  43. break;
  44. //如果参数是对象。
  45. case 'object':
  46. this.elements.push(vArg);
  47. }
  48. }
  49. //对选择器函数绑定click事件
  50. dQuery.prototype.click=function(fn){
  51. var i=0;
  52. //对于返回器数组的内容
  53. for(i=0;i<this.elements.length;i++){
  54. myAddEvent(this.elements[i],'click',fn);
  55. }
  56. }
  57. function $d(vArg){
  58. return new dQuery(vArg);
  59. }
(6)小结
  • 通过element属性储存被选中的元素
  • 判断参数类型,以此做出不同的判断
  • 对于参数是函数的情况,需要进行绑定window.onload。
  • 对象则直接插入

二. 添加事件

1. 之前已经用原型方法的加上了click事件,现在研究下show/hide的实现:

从最简单的display——none/block结果开始写起:

  1. //对选择器函数绑定show/hide事件
  2. dQuery.prototype.show=function(){
  3. var i=0;
  4. //对于返回器数组的内容
  5. for(i=0;i<this.elements.length;i++){
  6. this.elements[i].style.display='block';
  7. }
  8. }
  9. dQuery.prototype.hide=function(){
  10. var i=0;
  11. //对于返回器数组的内容
  12. for(i=0;i<this.elements.length;i++){
  13. this.elements[i].style.display='none';
  14. }
  15. };

实现功能已经没有问题了。

2.hover的实现:

(1)hover有两个参数,第一个是鼠标移入函数,第二个是鼠标移出函数。实际上跟click事件一样:
  1. dQuery.prototype.hover=function(fnover,fnout){
  2. var i=0;
  3. for(i=0;i<this.elements.length;i++){
  4. //给这个对象一次性绑定两个事件
  5. myAddEvent(this.elements[i],'mouseover',fnover);
  6. myAddEvent(this.elements[i],'mouseout',fnout);
  7. }
  8. };

就调用一般的alert方法等已经没有问题了,接下来要用类似jquery中css的写法定义它的样式

(2)css有两种调用:设置样式,只有一个参数时获取样式。总之参数不定(1或2个,attr、value)。只有两个参数时很简单。只需要做个循环,把value值赋给相应的样式属性就可以了:
  1. this.elements[i].style[attr]=value;

当只有一个参数attr时,获取的样式可能不是唯一的。这时可以参考jquery的css方法定义:获取的是第一个元素的样式——如果你用xxx.style.样式的方法——获取的仅仅是行间样式,那这就麻烦了。需要再引入一个兼容性良好的样式获取函数getStyle。

  1. function getStyle(obj,attr){
  2. //元素,样式
  3. if(obj.currentStyle){//兼容ie9及以下浏览器
  4. return obj.currentStyle[attr];
  5. }else{
  6. return getComputedStyle(obj,false)[attr];
  7. }
  8. }
  1. //css方法
  2. dQuery.prototype.css=function(attr,value){
  3. if(arguments.length==2){//当参数个数为2时,使用设置css的方法
  4. var i=0;
  5. for(i=0;i<this.elements.length;i++){
  6. this.elements[i].style[attr]=value;
  7. }
  8. }else{//只有一个参数时获取样式
  9. return getStyle(this.elements[0],attr);
  10. }
  11. };

实验证明,传入background无效,但是对具体的background-color无效。

(3)改进:this

虽然说当dQuery对象的参数为一个对象时,返回的就是对象本身。但是有时候在使用this的时候(ie),this指向了其它的对象(window对象)。
不能用this的情形——行间,定时器,绑定。js库中,实际上是绑定处理的。在此需要了解call方法。因此需要对myAddEvent函数进行修改。


call方法
应该知道,jquery中充满了各种简写。比如show,全称为show.call。比如下面这个例子:

  1. function show(){
  2. alert(this);
  3. }
  4. show();
  5. show.call();

这个show函数的弹出结果为:window对象。show.call()弹出结果也是window对象,但call可以加参数,加了参数之后,this返回的值为参数——show.call('abc');弹出结果为abc。
进一步,给a,b传入两个参数:

  1. function show(a,b){
  2. alert('this是:'+this+',a是:'+a+',b是:'+b);
  3. }
  4. show(12,5);
  5. show.call(12,5);

调用show函数的第一个弹出结果是:

调用show.call(12,5)弹出:

这说明call方法中的第一个参数默认指向this。比如要调用可以写成show('abc',12,5)
与call类似,apply也类似——
apply('abc',[12,5])的结果和show('abc',12,5)完全一样。


知道了call方法,回到myAddEvent函数的修改上来,应该让fn返回的主题回到obj——

  1. function myAddEvent(obj,sEv,fn){
  2. if(obj.attachEvent){
  3. obj.attachEvent('on'+sEv,function(){
  4. fn.call(obj);//兼容ie
  5. });
  6. }else{
  7. obj.addEventListener(sEv,fn,false);
  8. }
  9. }

这样一来,就可以使用$d(this)了。

3.toggle方法

toggle方法是旧jquery中很有用的点击处理方法。可以传多个事件处理函数。每一次触发,就按顺序执行。调用形式如下:

  1. $d(function(){
  2. $d('#btn1').click(function(){
  3. $d('#div1').toggle(function fn1(){
  4. },function fn2(){
  5. },function fn3(){
  6. }...);
  7. });
  8. });

怎么模拟这个函数呢?——

(1)需要一个计数器,根据计数的结果选择执行哪个函数。

toggle可以传任意个参数。所以参数干脆不加了。


在计数器的实现过程中会出现问题。

  1. window.onload=function (){
  2. var aBtn=getElementsByTagName('button');
  3. var i=0;
  4. var count=0;//计数器
  5. for(i=0;i<aBtn.length;i++){
  6. aBtn[i].onclick=function(){
  7. alert(count++);
  8. }
  9. }
  10. }

如果我有多个按钮需要触发toggle内的事件,但是按照dQuery先前的方法,必然导致所有按钮公用一套计数器。这样一来效果就全乱了。怎么解决呢?
最好的办法是,把计数过程存为一个函数,

  1. window.onload=function (){
  2. var aBtn=getElementsByTagName('button');
  3. var i=0;
  4. function addClick(obj){
  5. var count=0;//关键步骤:计数器塞进函数里了。
  6. alert(count++);
  7. }
  8. for(i=0;i<aBtn.length;i++){
  9. aBtn[i].onclick=function(){
  10. addClick(aBtn[i]);
  11. }
  12. }
  13. }

或者(对阅读不友好):

  1. window.onload=function (){
  2. var aBtn=getElementsByTagName('button');
  3. var i=0;
  4. for(i=0;i<aBtn.length;i++){
  5. (function(obj){
  6. var count=0;//关键步骤:计数器塞进函数里了。
  7. alert(count++);
  8. })(aBtn[i]);
  9. }
  10. }
  11. }

分别计数就这么实现了。调用函数几次,就会产生多少个局部变量。也可以使用xxx.index这样的方法。


现在要在toggle里面调用私有的计数器,思路也是把计数器放到一个声明的函数里边。

  1. dQuery.prototype.toggle=function(){
  2. //私有计数器,计数器会被一组对象所享用。
  3. function addToggle(obj){
  4. var count=0;
  5. myAddEvent(obj,'click',function(){
  6. alert(count++);
  7. })
  8. }
  9. var i=0;
  10. for(i=0;i<this.elements.length;i++){
  11. addToggle(this.elements[i]);
  12. }
  13. }

接下来是使用arguments来做,用以替换掉addToggle里边的事件处理:让计数器递增的同时,选取它对参数长度取模的索引的参数,。并把本体call为参数obj(有点拗口)

  1. myAddEvent(obj,'click',function(){
  2. arguments[count++%arguments.length].call(obj);
  3. }

结果出错。主要是arguments乱了,实际执行情况是,arguments是函数myAddEvent第三个参数(函数)的参数。因此,需要提前把toggle方法的arguments传入一个数组。
完整toggle的的代码是

  1. //toggle方法:
  2. dQuery.prototype.toggle=function(){
  3. var _arguments=arguments;//把toggle的arguments存起来,以便在其它函数中可以调用。
  4. //私有计数器,计数器会被一组对象所享用。
  5. function addToggle(obj){
  6. var count=0;
  7. myAddEvent(obj,'click',function(){
  8. _arguments[count++%_arguments.length].call(obj);
  9. })
  10. }
  11. var i=0;
  12. for(i=0;i<this.elements.length;i++){
  13. addToggle(this.elements[i]);
  14. }
  15. }

接下来可以做一个简单的示例验证一下效果:
html

  1. <input type="button" id="btn1" value="显示隐藏">
  2. <div id="div1" style="width:100px;height:100px;background:red;"></div>

javascript:

  1. $d(function(){
  2. $d('input').toggle(function(){
  3. $d('#div1').hide();
  4. },function(){
  5. $d('#div1').show();
  6. })
  7. });

效果如下

三. attr方法

attr管样式,css管属性。所以方法设置方面没有太大区别。

  1. //attr方法和css方法类似。
  2. dQuery.prototype.attr=function(attr value){
  3. if(arguments.length==2){//设置属性
  4. var i=0
  5. for(i=0;i<this.elements.length;i++){
  6. this.elements[i][attr]=value;
  7. }
  8. }else{//获取属性
  9. return this.elements[0][attr];
  10. }
  11. }

没有问题。

四.获取与查找(eq,find,index)

1.eq方法跟数组索引差不多

在jquery中,$('.class1').eq(1)表示选取选择器的第2个对象。现在模拟jquery的取法。
在dQuery中,很自然想到用return this.elements[n];这样的代码选取。问题在于:返回的是一个原生的js对象,因此无法调用dQuery方法。因此需要把this.elements[n]这个原生对象转化为dQuery对象。所以——

  1. //eq选择器
  2. dQuery.prototype.eq=function(n){
  3. return new dQuery(this.elements[n]);
  4. }

这样保证了返回出来的对象是可链的。

2.find方法

find方法在jquery中是用于筛选。比如$('ul').find('li')表示在所有ul标签下选择li,而ol标记下的li不会被选择。此方法有一个父级,find里面,可能有class类,可能有div类,不可能有id类(为什么不直接用id选择器呢?所以不予考虑了)。
因为返回的是一个较为复杂的对象——它是依赖于dQuery对象的一个数组(aResult)。因此具体做法和定义dQuery的object类似。需要分类讨论:

  1. //find选择器
  2. dQuery.prototype.find=function(str){
  3. var i=0;
  4. var aResult=[];//存放临时数据
  5. for(i=0;i<this.elements.length;i++){
  6. switch(str.charAt(0)){
  7. case '.'://class类
  8. var aEle=getByClass(this.elements[i],str.substring(1));
  9. aResult.concat(aEle);//桥接到aResult内。
  10. break;
  11. default://其它标签名(TagName)
  12. var aEle=this.elements[i].getElementsByTagName(str);
  13. aResult.concat(aEle);
  14. }
  15. }
  16. var newdQuery=new dQuery();
  17. newdQuery.elements=aResult;
  18. return newdQuery;//保持可链。
  19. }

这段代码有很大问题,aResult本是一个数组,但是this.elements[i].getElementsByTagName(str)是一个html collection,是一个长得很像数组的集合。因此concat方法不适用。——concat不能用,但是push可以用。为了达到桥接的效果,可以人工定义一个小函数来实现。所以全部带代码是

  1. //find选择器
  2. //定义一个小函数,两个数组(元素集合),把两个类数组(html元素集合)合并在一块。
  3. function appendArr(arr1, arr2){
  4. var i=0;
  5. for(i=0;i<arr2.length;i++){
  6. arr1.push(arr2[i]);
  7. }
  8. }
  9. dQuery.prototype.find=function(str){
  10. var i=0;
  11. var aResult=[];//存放临时数据
  12. for(i=0;i<this.elements.length;i++){
  13. switch(str.charAt(0)){
  14. case '.'://class类
  15. var aEle=getByClass(this.elements[i],str.substring(1));
  16. aResult.concat(aEle);//桥接到aResult内。但是
  17. break;
  18. default://其它标签名(TagName)
  19. var aEle=this.elements[i].getElementsByTagName(str);
  20. appendArr(aResult,aEle);
  21. }
  22. }
  23. var newdQuery=new dQuery();
  24. newdQuery.elements=aResult;
  25. return newdQuery;//保持可链。
  26. }

3.index方法

jquery的原意是——获取一组同辈元素内,一组元素的索引值。
比如

  1. <span></span>
  2. <div id="div1"></div>
  3. <div></div>
  4. <div></div>
  1. $(funciton(){
  2. $('div').click(function(){
  3. alert($(this).index());
  4. })
  5. })

在上面的过程中,点击#div1,弹出的结果为1,而不是0(把span算进索引0了)。简单点说就是:如何求某个元素在同级中的位置。
下面实现这段代码。
首先,使用index方法的必然是一个单独的对象,而不能是一个集合或数组。所以,必须指定this.elements[0]
一个人,比如说王五必然是他爹的儿子,要找到他的兄弟,只能通过他爹来找。接下来可以有头到尾遍历判断,假设老王的第五个儿子名字是王五,那么就可以判断,王五的索引值为4.同理,js用parentNode和child进行判断。

  1. //获取索引值函数
  2. function getIndex(obj){
  3. var aBrother=obj.parentNode.children;
  4. var i=0;
  5. for(i=0;i<aBrother.length;i++){
  6. if(aBrother[i]==obj){
  7. return i;
  8. }
  9. }
  10. }

接下来要做的就是把这个封装好的函数加到原型函数内:

  1. dQuery.prototype.index=function(){
  2. return getIndex(this.elements[0]);
  3. }

那么,这个方法就算完成了。为了说明这个库是实用的,接下来以一个案例说明。



【应用案例】选项卡的实现

html骨架

  1. <div id="tab">
  2. <ul class="list_group">
  3. <li><a class="active" href="javascript:;">1</a></li>
  4. <li><a href="javascript:;">2</a></li>
  5. <li><a href="javascript:;">3</a></li>
  6. <li><a href="javascript:;">4</a></li>
  7. </ul>
  8. <div class="box">
  9. <div class="content">
  10. <img alt="1" src="data:images/1.jpg">
  11. </div>
  12. <div class="content">
  13. <img alt="2" src="data:images/2.jpg">
  14. </div>
  15. <div class="content">
  16. <img alt="3" src="data:images/3.jpg">
  17. </div>
  18. <div class="content">
  19. <img alt="4" src="data:images/4.jpg">
  20. </div>
  21. </div>
  22. </div>

css

  1. *{
  2. margin:0;
  3. padding: 0;
  4. }
  5. ul{
  6. list-style: none;
  7. }
  8. a{text-decoration: none;}
  9. #tab{
  10. width: 400px;
  11. margin:100px auto;
  12. }
  13. .list_group li{
  14. float: left;
  15. }
  16. .list_group li a{
  17. display: block;
  18. width: 40px;
  19. line-height: 30px;
  20. text-align: center;
  21. }
  22. .box{
  23. clear: both;
  24. }
  25. .content{
  26. display: none;
  27. }
  28. .content img{
  29. border: 1px solid black;
  30. width: 400px;height: 300px;
  31. }

在引用了dQuery的情况下,输入下列js代码:

  1. $d(function(){
  2. $d('li').click(function(){
  3. var index=$d(this).index();
  4. $d('.content').hide();
  5. $d('.content').eq(index).show();
  6. $d('li').css('background-color','white');
  7. $d(this).css('background-color','#ccc');
  8. })
  9. })


就成功实现了一个丑陋的选项卡。因为不是完全可链的。还有addClass等功能还有待进一步完善。


附录

dQuery代码

  1. //可重复调用的加载函数
  2. function myAddEvent(obj,sEv,fn){
  3. if(obj.attachEvent){
  4. obj.attachEvent('on'+sEv,function(){
  5. fn.call(obj);//兼容ie
  6. });
  7. }else{
  8. obj.addEventListener(sEv,fn,false);
  9. }
  10. }
  11. //class选择器调用函数
  12. function getByClass(oParent,sClass){
  13. var aEle=oParent.getElementsByTagName('*');//选择父元素的所有元素
  14. var aResult=[];
  15. var re=new RegExp('\\b'+sClass+'\\b','i');//正则边界
  16. var i=0;
  17. for(i=0;i<aEle.length;i++){
  18. if(re.test(aEle[i].className)){
  19. aResult.push(aEle[i]);
  20. }
  21. }
  22. return aResult;
  23. }
  24. //获取计算后的样式
  25. function getStyle(obj,attr){
  26. //元素,样式
  27. if(obj.currentStyle){//兼容ie9及以下
  28. return obj.currentStyle[attr];
  29. }else{
  30. return getComputedStyle(obj,false)[attr];
  31. }
  32. }
  33. //定义dQuery对象
  34. function dQuery(vArg){//参数是变体变量
  35. this.elements=[];//选择器选择的元素扔到这个数组中
  36. switch(typeof vArg){
  37. //如果参数是函数
  38. case 'function':
  39. myAddEvent(window,'load',vArg);
  40. break;
  41. //如果参数是字符串
  42. case 'string':
  43. switch(vArg.charAt(0)){
  44. case '#'://id选择器参数应该为#号之后的字符段
  45. var obj=document.getElementById(vArg.substring(1));
  46. this.elements.push(obj);
  47. break;
  48. case '.'://class
  49. this.elements=getByClass(document,vArg.substring(1));
  50. break;
  51. default://标签
  52. this.elements=document.getElementsByTagName(vArg);
  53. }
  54. break;
  55. //如果参数是对象。
  56. case 'object':
  57. this.elements.push(vArg);
  58. }
  59. }
  60. //定义简写
  61. function $d(vArg){
  62. return new dQuery(vArg);
  63. }
  64. //对选择器函数绑定click事件
  65. dQuery.prototype.click=function(fn){
  66. var i=0;
  67. //对于返回器数组的内容
  68. for(i=0;i<this.elements.length;i++){
  69. myAddEvent(this.elements[i],'click',fn);
  70. }
  71. }
  72. //对选择器函数绑定show/hide事件
  73. dQuery.prototype.show=function(){
  74. var i=0;
  75. //对于返回器数组的内容
  76. for(i=0;i<this.elements.length;i++){
  77. this.elements[i].style.display='block';
  78. }
  79. }
  80. dQuery.prototype.hide=function(){
  81. var i=0;
  82. //对于返回器数组的内容
  83. for(i=0;i<this.elements.length;i++){
  84. this.elements[i].style.display='none';
  85. }
  86. };
  87. //hover方法
  88. dQuery.prototype.hover=function(fnover,fnout){
  89. var i=0;
  90. //对于返回器数组的内容
  91. for(i=0;i<this.elements.length;i++){
  92. //给这个对象一次性绑定两个事件
  93. myAddEvent(this.elements[i],'mouseover',fnover);
  94. myAddEvent(this.elements[i],'mouseout',fnout);
  95. }
  96. };
  97. //css方法
  98. dQuery.prototype.css=function(attr,value){
  99. if(arguments.length==2){//当参数个数为2时,使用设置css的方法
  100. var i=0;
  101. for(i=0;i<this.elements.length;i++){
  102. this.elements[i].style[attr]=value;
  103. }
  104. }else{//只有一个参数时获取样式
  105. return getStyle(this.elements[0],attr);
  106. }
  107. };
  108. //toggle方法:
  109. dQuery.prototype.toggle=function(){
  110. var _arguments=arguments;//把toggle的arguments存起来,以便在其它函数中可以调用。
  111. //私有计数器,计数器会被一组对象所享用。
  112. functionaddToggle(obj){
  113. var count=0;
  114. myAddEvent(obj,'click',function(){
  115. _arguments[count++%_arguments.length].call(obj);
  116. })
  117. }
  118. var i=0;
  119. for(i=0;i<this.elements.length;i++){
  120. addToggle(this.elements[i]);
  121. }
  122. }
  123. //attr方法和css方法类似。
  124. dQuery.prototype.attr=function(attr,value){
  125. if(arguments.length==2){//设置属性
  126. var i=0;
  127. for(i=0;i<this.elements.length;i++){
  128. this.elements[i][attr]=value;
  129. }
  130. }else{//获取属性
  131. return this.elements[0][attr];
  132. }
  133. }
  134. //eq选择器
  135. dQuery.prototype.eq=function(n){
  136. return new dQuery(this.elements[n]);
  137. }
  138. //find选择器
  139. //定义一个小函数,两个数组(元素集合),把两个类数组(html元素集合)合并在一块。
  140. functionappendArr(arr1, arr2){
  141. var i=0;
  142. for(i=0;i<arr2.length;i++){
  143. arr1.push(arr2[i]);
  144. }
  145. }
  146. dQuery.prototype.find=function(str){
  147. var i=0;
  148. var aResult=[];//存放临时数据
  149. for(i=0;i<this.elements.length;i++){
  150. switch(str.charAt(0)){
  151. case '.'://class类
  152. var aEle=getByClass(this.elements[i],str.substring(1));
  153. aResult.concat(aEle);//桥接到aResult内。但是
  154. break;
  155. default://其它标签名(TagName)
  156. var aEle=this.elements[i].getElementsByTagName(str);
  157. appendArr(aResult,aEle);
  158. }
  159. }
  160. var newdQuery=new dQuery();
  161. newdQuery.elements=aResult;
  162. return newdQuery;//保持可链。
  163. }
  164. //获取索引值函数
  165. functiongetIndex(obj){
  166. var aBrother=obj.parentNode.children;
  167. var i=0;
  168. for(i=0;i<aBrother.length;i++){
  169. if(aBrother[i]==obj){
  170. return i;
  171. }
  172. }
  173. }
  174. dQuery.prototype.index=function(){
  175. return getIndex(this.elements[0]);
  176. }
 
 
 
 

本篇为完结篇。主要讲述如何造出轮子的高级特性。

一. css方法的高级操作

先看本文第一部分所讲的dQuery css方法

  1. //css方法
  2. dQuery.prototype.css=function(attr,value){
  3. if(arguments.length==2){//当参数个数为2时,使用设置css的方法
  4. var i=0;
  5. for(i=0;i<this.elements.length;i++){
  6. this.elements[i].style[attr]=value;
  7. }
  8. }else{//只有一个参数时获取样式
  9. return getStyle(this.elements[0],attr);
  10. }
  11. };

在这个方法里,只能一次一行,且只能设置一个属性。效率还不够高。现在尝试通过设置类似$d('#div1').css({width:'200px',height:'200px',background:'red'})这样的方式,向css方法传入一个json对象。允许一次性设置多个css样式。或者允许链式操作。

1. 传入json数据

传入json实质上是一个参数。所以只有一个参数时,不仅仅是获取,还可能是传入了json数据。需要对传入一个参数时,进行分类讨论


json与for-in循环
  1. var json={width:'200px',height:'200px',background:'red'};
  2. var i=0;
  3. for(i in json){
  4. alert(i+':'+json[i]);
  5. }

这段程序可以依次弹出i(json属性):json数据的窗口。


同理,在dQuery的css方法中也可以这么做。

  1. //css方法
  2. dQuery.prototype.css=function(attr,value){
  3. if(arguments.length==2){//当参数个数为2时,使用设置css的方法
  4. var i=0;
  5. for(i=0;i<this.elements.length;i++){
  6. this.elements[i].style[attr]=value;
  7. }
  8. }else if(arguments.length==1){//只有一个参数时获取样式,或传入json
  9. if(typeof attr=='string'){//attr为纯文字时,设置样式
  10. return getStyle(this.elements[0],attr);
  11. }else{//传入json,批量设置css样式
  12. for(i=0;i<this.elements.length;i++){
  13. var j='';
  14. for(j in attr){
  15. this.elements[i].style[j]=attr[j];
  16. }
  17. }
  18. }
  19. }
  20. };

测试成功。

#### (2)链式操作

函数链式操作原理:返回函数本身。
  1. function show(str){
  2. alert(str);
  3. return show;//关键
  4. }
  5. show('abc')('bcd');

这段代码将弹出两个框,abc,和bcd。只要你想,可以无限地写下去。


链式操作是jquery的主要特色。执行完一个方法之后,又返回到该对象。所以只需要在css函数的最后,补上return this。就完成了。根据这个思想,可以给每个方法加上return this,那么你的js库就可以实现完全的链式操作。

二. attr设置进一步——removeClass和addClass的实现

之前说到attr方法本质上和css方法一样的。所以attr也可以用类似的方法设置——

  1. //attr方法和css方法类似。
  2. dQuery.prototype.attr=function(attr,value){
  3. if(arguments.length==2){//设置属性
  4. var i=0;
  5. for(i=0;i<this.elements.length;i++){
  6. this.elements[i][attr]=value;
  7. }
  8. }else if(arguments.length==1){//获取属性
  9. if(typeof attr=='string'){
  10. return this.elements[0][attr];
  11. }else{
  12. for(i=0;i<this.elements.length;i++){
  13. var j='';
  14. for(j in attr){
  15. this.elements[i][j]=attr[j];
  16. }
  17. }
  18. }
  19. }
  20. return this;
  21. }

这段attr方法已经能够实现链式操作和接收json数据。现在需要利用attr方法实现添加class和移除class的功能。
jquery中,addClassremoveClass的基本功能是:给addClass方法传入一个字符串,目标元素的class尾部追加这段字符串,给removeClass传入字符串,就把该字符串从目标元素的字符串中删除掉。
用文字描述之后,实现就简单不少了。

  1. //addClass和removeClass的实现
  2. dQuery.prototype.addClass=function(str){
  3. var i=0;
  4. for(i=0;i<this.elements.length;i++){
  5. if(this.elements[i].className==''){
  6. this.elements[i].className+=str;
  7. }else{
  8. this.elements[i].className+=' '+str;
  9. }
  10. }
  11. return this;
  12. }
  13. dQuery.prototype.removeClass=function(str){
  14. var i=0;
  15. for(i=0;i<this.elements.length;i++){
  16. var _className=this.elements[i].className
  17. if(!str){//如果不传参,所有class都被清空。
  18. this.elements[i].className='';
  19. }else if(_className!=''){
  20. var arr=_className.split(' ');
  21. var j=0;
  22. for(j=0;j<arr.length;j++){
  23. if(arr[j]==str){
  24. arr.splice(j,1);//从数组第j个起删除1个(移除arr[j])
  25. this.elements[i].className=arr.join(' ');//把数组重新转化为字符串,并用空格分开。最后赋值给当下对象的className。
  26. }
  27. }
  28. }
  29. }
  30. return this;
  31. }

注意,此段代码有局限性,前提是操作者html操作正确时才能生效,类似$d('.div1').addClass('div1').removeClass('div1')虽然也能尝试理解操作者意思并容错。但是<div class="div1 div1 div2">这样错误的class标记使用removeClass会发生错误。

3.阻止冒泡及默认事件

右键菜单(contextmenu)为例——jquery阻止默认事件,冒泡的机制是

  1. $(function(){
  2. $(document).bind('contextmenu',function(){//对document绑定contextmenu事件,并执行函数。
  3. return false;
  4. })
  5. })

return false在原生js中只处理阻止默认事件,但jquery可以两样都阻止。
首先,dQuery没有bind方法。加上去就行。所谓绑定事件的框架很简单,实际上和dQuery其它事件的框架是一样的。

  1. //绑定事件的方法:
  2. dQuery.prototype.bind=function(sEv,fn){
  3. var i=0;
  4. for(i=0;i<this.elements.length;i++){
  5. myAddEvent(this.elements[i],sEv,fn);
  6. }
  7. }

只要你在页面上右键点击,就会触发函数。然而还不完善。

  1. $d(function (){
  2. $d(document).bind('contextmenu', function (){
  3. return false;
  4. });
  5. });

这里添加了return false,但右击,无法阻止任何事件。
究其深层原因,可以发现,回调函数是通过myAddEvent()这个函数实现的。
这就搞笑了。在myAddEvent()中,无论return什么都没有意义。所以要实现,完整的myAddEvent()代码是:

  1. //可重复调用的加载函数
  2. function myAddEvent(obj,sEv,fn){
  3. if(obj.attachEvent){
  4. obj.attachEvent('on'+sEv,function(){
  5. if(false==fn.call(obj)){//当调用return false的时候
  6. event.cancelBubble=true;//阻止冒泡
  7. return false;
  8. }
  9. });
  10. }else{
  11. obj.addEventListener(sEv,function(ev){
  12. if(false==fn.call(obj)){
  13. ev.cancelBubble=true;//阻止冒泡
  14. ev.preventDefault();//火狐、chrome下用于阻止默认事件的语句
  15. }
  16. },false);
  17. }
  18. }

这样,在三大浏览器下,已经实现了事件的绑定调用return false无默认事件。并且阻止冒泡。

三. 插件接口

插件需要一个机制。可以通过它自定义方法名称,方法内容.
假设页面有三个class为div1的div,扩展s一个size方法,获取某类页面元素的个数:

  1. dQuery.prototype.extend=function (name, fn){
  2. dQuery.prototype[name]=fn;
  3. };
  4. $d().extend('size',function(){
  5. alert(this.element.length);
  6. })
  7. //调用插件里的方法和内部方法无差异
  8. $d('.div1').size();

弹出结果就为3.
从这个角度说,插件机制编写方法似乎更加便捷了。

1. animate运动框架

jquery的运动形式为:xxx.animate('',目标)。目标是一个json数据。
在运动学教程中有一个比较完美的运动框架startMove。startMove的json传入的值为最终运动状态。注意:和jquery不同,数值不带单位,透明度的量度为100而不是1.

  1. $d().extend('animate',function(json){
  2. var i=0;
  3. for(i=0;i<this.elements.length;i++){
  4. console.log(this.elements);
  5. startMove(this.elements[i],json,function(){alert('hehe')})
  6. }
  7. function getStyle(obj, attr)
  8. {
  9. if(obj.currentStyle)
  10. {
  11. return obj.currentStyle[attr];
  12. }
  13. else
  14. {
  15. return getComputedStyle(obj, false)[attr];
  16. }
  17. }
  18. function startMove(obj,json,fn){
  19. clearInterval(obj.timer);
  20. obj.timer=setInterval(function(){
  21. var bStop= true;//标志着所有运动都结束了
  22. //遍历每个json属性
  23. for(var attr in json){
  24. //取当前的属性对象
  25. var iCur=0;
  26. if(attr=='opacity'){
  27. iCur=parseInt(parseFloat(getStyle(obj,attr))*100);
  28. }else{
  29. iCur=parseInt(getStyle(obj,attr));
  30. }
  31. //定义速度值
  32. var iSpeed=(json[attr]-iCur)/8;
  33. iSpeed=iSpeed>0?Math.ceil(iSpeed):Math.floor(iSpeed);
  34. //检测停止:如果我发现某个值不等于目标点bStop就不能为true。
  35. if(iCur!==json[attr]){
  36. bStop=false;
  37. }
  38. //计算
  39. if(attr=='opacity'){
  40. obj.style[attr]=(iCur+iSpeed)/100;
  41. obj.style.filter='alpha(opacity:'+(iSpeed+iCur)+')';
  42. }else{
  43. obj.style[attr]=iCur+iSpeed+'px';
  44. }
  45. }
  46. //检测是否停止,是的话关掉定时器
  47. if(bStop==true){
  48. if(iCur==json[attr]){
  49. clearInterval(obj.timer);
  50. if(fn){fn();};
  51. }
  52. }
  53. },20)
  54. }
  55. });

那么这个插件就写完了。

【案例分析】上下滑动的幻灯片(选项卡)

基本思路和之前的选项卡案例差不多。但是切换过程是上下滑动的。因此所有图片不能隐藏,应该是一张借一张纵向连接。运动是由一个大容器带着一起运动。垂直运动距离由index()值决定同时,有了addClass和removeClass方法,可以设置一个class='active'的样式。

  1. <div id="tab">
  2. <ul class="list_group">
  3. <li><a href="javascript:;">1</a></li>
  4. <li><a href="javascript:;">2</a></li>
  5. <li><a href="javascript:;">3</a></li>
  6. <li><a href="javascript:;">4</a></li>
  7. </ul>
  8. <div class="box">
  9. <div class="box2">
  10. <div class="content">
  11. <img alt="1" src="data:images/1.jpg">
  12. </div>
  13. <div class="content">
  14. <img alt="2" src="data:images/2.jpg">
  15. </div>
  16. <div class="content">
  17. <img alt="3" src="data:images/3.jpg">
  18. </div>
  19. <div class="content">
  20. <img alt="4" src="data:images/4.jpg">
  21. </div>
  22. </div>
  23. </div>
  24. </div>

css

  1. *{
  2. margin:0;
  3. padding: 0;
  4. }
  5. ul{
  6. list-style: none;
  7. }
  8. a{text-decoration: none;}
  9. #tab{
  10. width: 400px;
  11. margin:100px auto;
  12. position: relative;
  13. }
  14. .list_group li{
  15. float: left;
  16. }
  17. .list_group li a{
  18. display: block;
  19. width: 40px;
  20. line-height: 30px;
  21. text-align: center;
  22. }
  23. .box{
  24. clear: both;
  25. width: 402px;height: 302px;
  26. overflow: hidden;
  27. position: relative;
  28. }
  29. .box2{
  30. position: absolute;
  31. top:0;
  32. }
  33. .content{
  34. width: 402px;
  35. height: 302px;
  36. }
  37. .content img{
  38. border: 1px solid black;
  39. width: 400px;height: 300px;
  40. }
  41. .active{
  42. background: #ccc;
  43. }

javascript

  1. $d(function(){
  2. $d('li').click(function(){
  3. var index=$d(this).index();
  4. var moveLength=-302*index;
  5. $d('.box2').animate({top:moveLength});
  6. $d('li').removeClass('active');
  7. $d(this).addClass('active');
  8. })
  9. })

效果:

效果还是差强人意,可以做自动播放:

  1. $d().extend('size',function(){
  2. return this.elements.length;
  3. })//定义一个统计元素个数的dQuery插件
  4. $d(function(){
  5. var iNow=0;
  6. var timer=null;
  7. //定义一个函数指令,可以移动.box2 -302*iNow个单位。
  8. function tab(){
  9. var moveLength=-302*iNow;
  10. $d('.box2').animate({top:moveLength});
  11. $d('li').removeClass('active');
  12. $d('li').eq(iNow).addClass('active');
  13. }
  14. //自动播放定义定时器内的函数
  15. function timerInner(){
  16. iNow++;
  17. if(iNow==$d('li').size()){
  18. iNow=0;
  19. }
  20. tab();
  21. }
  22. timer=setInterval(timerInner,1000);//调用定时器
  23. //点击事件函数
  24. $d('li').click(function(){
  25. iNow=$d(this).index();
  26. tab();
  27. });
  28. //鼠标移入选项卡范围,关掉定时器timer,移出时再打开
  29. $d('#tab').hover(function(){
  30. clearInterval(timer);
  31. },function(){
  32. timer=setInterval(timerInner,1000)
  33. })
  34. })

效果更加人性化了。

小结:与前一个版本的dQuery库相比,写法更进一步。

2.拖拽插件

加入页面上有一个绝对定位的div#div1
样式如下:

  1. #div1{
  2. width: 100px;height: 100px;
  3. background: red;
  4. position: absolute;
  5. }

引入方法是:

  1. $d().extend('drag', function (){
  2. var i=0;
  3. for(i=0;i<this.elements.length;i++){
  4. drag(this.elements[i]);
  5. }
  6. function drag(oDiv){//拖拽函数
  7. oDiv.onmousedown=function (ev){
  8. var oEvent=ev||event;
  9. var disX=oEvent.clientX-oDiv.offsetLeft;
  10. var disY=oEvent.clientY-oDiv.offsetTop;
  11. document.onmousemove=function (ev){
  12. var oEvent=ev||event;
  13. oDiv.style.left=oEvent.clientX-disX+'px';
  14. oDiv.style.top=oEvent.clientY-disY+'px';
  15. };
  16. document.onmouseup=function (){
  17. document.onmousemove=null;
  18. document.onmouseup=null;
  19. };
  20. };
  21. }
  22. });

调用方法:

  1. $d('#div1').drag();

一个简单到令人发指的效果就做好了。



附录

1.dQuery基本代码(dQuery.js)

  1. //可重复调用的加载函数
  2. function myAddEvent(obj,sEv,fn){
  3. if(obj.attachEvent){
  4. obj.attachEvent('on'+sEv,function(){
  5. if(false==fn.call(obj)){//当调用return false的时候
  6. event.cancelBubble=true;
  7. return false;
  8. }
  9. });
  10. }else{
  11. obj.addEventListener(sEv,function(ev){
  12. if(false==fn.call(obj)){
  13. ev.cancelBubble=true;
  14. ev.preventDefault();//火狐、chrome下用于阻止默认事件的语句
  15. }
  16. },false);
  17. }
  18. }
  19. //class选择器调用函数
  20. function getByClass(oParent,sClass){
  21. var aEle=oParent.getElementsByTagName('*');//选择父元素的所有元素
  22. var aResult=[];
  23. var re=new RegExp('\\b'+sClass+'\\b','i');//正则边界
  24. var i=0;
  25. for(i=0;i<aEle.length;i++){
  26. if(re.test(aEle[i].className)){
  27. aResult.push(aEle[i]);
  28. }
  29. }
  30. return aResult;
  31. }
  32. //获取计算后的样式
  33. function getStyle(obj,attr){
  34. //元素,样式
  35. if(obj.currentStyle){//兼容ie9及以下
  36. return obj.currentStyle[attr];
  37. }else{
  38. return getComputedStyle(obj,false)[attr];
  39. }
  40. }
  41. //定义dQuery对象
  42. function dQuery(vArg){//参数是变体变量
  43. this.elements=[];//选择器选择的元素扔到这个数组中
  44. switch(typeof vArg){
  45. //如果参数是函数
  46. case 'function':
  47. myAddEvent(window,'load',vArg);
  48. break;
  49. //如果参数是字符串
  50. case 'string':
  51. switch(vArg.charAt(0)){
  52. case '#'://id选择器参数应该为#号之后的字符段
  53. var obj=document.getElementById(vArg.substring(1));
  54. this.elements.push(obj);
  55. break;
  56. case '.'://class
  57. this.elements=getByClass(document,vArg.substring(1));
  58. break;
  59. default://标签
  60. this.elements=document.getElementsByTagName(vArg);
  61. }
  62. break;
  63. //如果参数是对象。
  64. case 'object':
  65. this.elements.push(vArg);
  66. }
  67. }
  68. //定义简写
  69. function $d(vArg){
  70. return new dQuery(vArg);
  71. }
  72. //对选择器函数绑定click事件
  73. dQuery.prototype.click=function(fn){
  74. var i=0;
  75. //对于返回器数组的内容
  76. for(i=0;i<this.elements.length;i++){
  77. myAddEvent(this.elements[i],'click',fn);
  78. }
  79. return this;
  80. }
  81. //对选择器函数绑定show/hide事件
  82. dQuery.prototype.show=function(){
  83. var i=0;
  84. //对于返回器数组的内容
  85. for(i=0;i<this.elements.length;i++){
  86. this.elements[i].style.display='block';
  87. }
  88. return this;
  89. }
  90. dQuery.prototype.hide=function(){
  91. var i=0;
  92. //对于返回器数组的内容
  93. for(i=0;i<this.elements.length;i++){
  94. this.elements[i].style.display='none';
  95. }
  96. return this;
  97. };
  98. //hover方法
  99. dQuery.prototype.hover=function(fnover,fnout){
  100. var i=0;
  101. //对于返回器数组的内容
  102. for(i=0;i<this.elements.length;i++){
  103. //给这个对象一次性绑定两个事件
  104. myAddEvent(this.elements[i],'mouseover',fnover);
  105. myAddEvent(this.elements[i],'mouseout',fnout);
  106. }
  107. return this;
  108. };
  109. //css方法
  110. dQuery.prototype.css=function(attr,value){
  111. if(arguments.length==2){//当参数个数为2时,使用设置css的方法
  112. var i=0;
  113. for(i=0;i<this.elements.length;i++){
  114. this.elements[i].style[attr]=value;
  115. }
  116. }else if(arguments.length==1){//只有一个参数时获取样式,或传入json
  117. if(typeof attr=='string'){//attr为纯文字时,设置样式
  118. return getStyle(this.elements[0],attr);
  119. }else{//传入json,批量设置css样式
  120. for(i=0;i<this.elements.length;i++){
  121. var j='';
  122. for(j in attr){
  123. this.elements[i].style[j]=attr[j];
  124. }
  125. }
  126. }
  127. }
  128. return this;
  129. };
  130. //toggle方法:
  131. dQuery.prototype.toggle=function(){
  132. var _arguments=arguments;//把toggle的arguments存起来,以便在其它函数中可以调用。
  133. //私有计数器,计数器会被一组对象所享用。
  134. functionaddToggle(obj){
  135. var count=0;
  136. myAddEvent(obj,'click',function(){
  137. _arguments[count++%_arguments.length].call(obj);
  138. })
  139. }
  140. var i=0;
  141. for(i=0;i<this.elements.length;i++){
  142. addToggle(this.elements[i]);
  143. }
  144. return this;
  145. }
  146. //attr方法和css方法类似。
  147. dQuery.prototype.attr=function(attr,value){
  148. if(arguments.length==2){//设置属性
  149. var i=0;
  150. for(i=0;i<this.elements.length;i++){
  151. this.elements[i][attr]=value;
  152. }
  153. }else if(arguments.length==1){//获取属性
  154. if(typeof attr=='string'){
  155. return this.elements[0][attr];
  156. }else{
  157. for(i=0;i<this.elements.length;i++){
  158. var j='';
  159. for(j in attr){
  160. this.elements[i][j]=attr[j];
  161. }
  162. }
  163. }
  164. }
  165. return this;
  166. }
  167. //addClass和removeClass的实现
  168. dQuery.prototype.addClass=function(str){
  169. var i=0;
  170. for(i=0;i<this.elements.length;i++){
  171. if(this.elements[i].className==''){
  172. this.elements[i].className+=str;
  173. }else{
  174. this.elements[i].className+=' '+str;
  175. }
  176. }
  177. return this;
  178. }
  179. dQuery.prototype.removeClass=function(str){
  180. var i=0;
  181. for(i=0;i<this.elements.length;i++){
  182. var _className=this.elements[i].className
  183. if(!str){//如果不传参,所有class都被清空。
  184. this.elements[i].className='';
  185. }else if(_className!=''){
  186. var arr=_className.split(' ');
  187. var j=0;
  188. for(j=0;j<arr.length;j++){
  189. if(arr[j]==str){
  190. arr.splice(j,1);//从数组第j个起删除1个(移除arr[j])
  191. this.elements[i].className=arr.join(' ');//把数组重新转化为字符串,并用空格分开。最后赋值给当下对象的className。
  192. }
  193. }
  194. }
  195. }
  196. return this;
  197. }
  198. //eq选择器
  199. dQuery.prototype.eq=function(n){
  200. return new dQuery(this.elements[n]);
  201. }
  202. //find选择器
  203. //定义一个小函数,两个数组(元素集合),把两个类数组(html元素集合)合并在一块。
  204. functionappendArr(arr1, arr2){
  205. var i=0;
  206. for(i=0;i<arr2.length;i++){
  207. arr1.push(arr2[i]);
  208. }
  209. }
  210. dQuery.prototype.find=function(str){
  211. var i=0;
  212. var aResult=[];//存放临时数据
  213. for(i=0;i<this.elements.length;i++){
  214. switch(str.charAt(0)){
  215. case '.'://class类
  216. var aEle=getByClass(this.elements[i],str.substring(1));
  217. aResult.concat(aEle);//桥接到aResult内。但是
  218. break;
  219. default://其它标签名(TagName)
  220. var aEle=this.elements[i].getElementsByTagName(str);
  221. appendArr(aResult,aEle);
  222. }
  223. }
  224. var newdQuery=new dQuery();
  225. newdQuery.elements=aResult;
  226. return newdQuery;//保持可链。
  227. }
  228. //获取索引值函数
  229. functiongetIndex(obj){
  230. var aBrother=obj.parentNode.children;
  231. var i=0;
  232. for(i=0;i<aBrother.length;i++){
  233. if(aBrother[i]==obj){
  234. return i;
  235. }
  236. }
  237. }
  238. dQuery.prototype.index=function(){
  239. return getIndex(this.elements[0]);
  240. }
  241. //绑定事件的方法:
  242. dQuery.prototype.bind=function(sEv,fn){
  243. var i=0;
  244. for(i=0;i<this.elements.length;i++){
  245. myAddEvent(this.elements[i],sEv,fn);
  246. }
  247. }
  248. //插件机制
  249. dQuery.prototype.extend=function (name, fn){
  250. dQuery.prototype[name]=fn;
  251. };

2.动画插件

注:动画插件可以设计类似jquery中fadeIn/Out,slideUp/Down 等等常见的动画效果。

  1. $d().extend('animate',function(json){
  2. var i=0;
  3. for(i=0;i<this.elements.length;i++){
  4. startMove(this.elements[i],json)
  5. }
  6. function getStyle(obj, attr)
  7. {
  8. if(obj.currentStyle)
  9. {
  10. return obj.currentStyle[attr];
  11. }
  12. else
  13. {
  14. return getComputedStyle(obj, false)[attr];
  15. }
  16. }
  17. function startMove(obj,json,fn){
  18. clearInterval(obj.timer);
  19. obj.timer=setInterval(function(){
  20. var bStop= true;//标志着所有运动都结束了
  21. //遍历每个json属性
  22. for(var attr in json){
  23. //取当前的属性对象
  24. var iCur=0;
  25. if(attr=='opacity'){
  26. iCur=parseInt(parseFloat(getStyle(obj,attr))*100);
  27. }else{
  28. iCur=parseInt(getStyle(obj,attr));
  29. }
  30. //定义速度值
  31. var iSpeed=(json[attr]-iCur)/8;
  32. iSpeed=iSpeed>0?Math.ceil(iSpeed):Math.floor(iSpeed);
  33. //检测停止:如果我发现某个值不等于目标点bStop就不能为true。
  34. if(iCur!==json[attr]){
  35. bStop=false;
  36. }
  37. //计算
  38. if(attr=='opacity'){
  39. obj.style[attr]=(iCur+iSpeed)/100;
  40. obj.style.filter='alpha(opacity:'+(iSpeed+iCur)+')';
  41. }else{
  42. obj.style[attr]=iCur+iSpeed+'px';
  43. }
  44. }
  45. //检测是否停止,是的话关掉定时器
  46. if(bStop==true){
  47. if(iCur==json[attr]){
  48. clearInterval(obj.timer);
  49. if(fn){fn();};
  50. }
  51. }
  52. },20)
  53. }
  54. });

3.拖拽插件

  1. $d().extend('drag', function (){
  2. var i=0;
  3. for(i=0;i<this.elements.length;i++){
  4. drag(this.elements[i]);
  5. }
  6. function drag(oDiv){//拖拽函数
  7. oDiv.onmousedown=function (ev){
  8. var oEvent=ev||event;
  9. var disX=oEvent.clientX-oDiv.offsetLeft;
  10. var disY=oEvent.clientY-oDiv.offsetTop;
  11. document.onmousemove=function (ev){
  12. var oEvent=ev||event;
  13. oDiv.style.left=oEvent.clientX-disX+'px';
  14. oDiv.style.top=oEvent.clientY-disY+'px';
  15. };
  16. document.onmouseup=function (){
  17. document.onmousemove=null;
  18. document.onmouseup=null;
  19. };
  20. };
  21. }
  22. });
 

仿照jquery封装一个自己的js库的更多相关文章

  1. 仿照jquery封装一个自己的js库(一)

    所谓造轮子的好处就是复习知识点,加深对原版jquery的理解. 本文系笔者学习jquery的笔记,记述一个名为"dQuery"的初级版和缩水版jquery库的实现.主要涉及知识点包 ...

  2. 仿照jquery封装一个自己的js库(二)

    本篇为完结篇.主要讲述如何造出轮子的高级特性. 一. css方法的高级操作 先看本文第一部分所讲的dQuery css方法 //css方法 dQuery.prototype.css=function( ...

  3. Zepto——简化版jQuery,移动端首选js库

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826054.html 一:Zepto是什么 Zepto最初是为移动端开发的js库,是jQuery的轻量级替 ...

  4. C 封装一个简单二叉树基库

    引文 今天分享一个喜欢佩服的伟人,应该算人类文明极大突破者.收藏过一张纸币类型如下 那我们继续科普一段关于他的简介 '高斯有些孤傲,但令人惊奇的是,他春风得意地度过了中产阶级的一生,而  没有遭受到冷 ...

  5. jQuery避免$符和其他JS库冲突的方法对比

    1.如果jquery库在第三方库之后引用.这个时候jquery库会占用$. 解决办法:剔除$符号的使用权. <script type="text/javascript" sr ...

  6. 基于jQuery封装一个瀑布流插件

    /*封装一个瀑布流插件*/ (function($){ $.fn.WaterFall = function(){ /*这是你初始化 调用这个方法的时候的 那个jquery选着到的dom对象 this* ...

  7. 仿照jQuery写一个关于选择器的框架(带了注释,请多多指教~)

    var select = (function () { //这是一个异常与捕获的代码,它表示的意思是:如果push方法出现了错误那么就需要重写push方法 try { //这边是自己模拟一个场景,来使 ...

  8. Jquery,YUI这个著名js库名称作用的理解

    看廖雪峰大神的教程,其中讲到变量作用域问题.在命名空间中,写到:因为全局变量绑到了window上,不同的js文件访问相同全局变量或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现. 减少冲 ...

  9. 【写一个自己的js库】 1.搭个架子先

    最近在看<javascript dom 高级程序设计>,想着跟着里面的代码敲一遍吧,也算是做一下学习笔记吧,所以这不是重新发明轮子,只是个学习的过程. 1.先确定自己的命名空间,并且加入几 ...

随机推荐

  1. 【HIHOCODER 1589】回文子串的数量(Manacher)

    描述 给定一个字符串S,请统计S的所有|S| * (|S| + 1) / 2个子串中(首尾位置不同就算作不同的子串),有多少个是回文字符串? 输入 一个只包含小写字母的字符串S. 对于30%的数据,S ...

  2. 二叉排序树:HDU3791-二叉搜索树(用指针建立二叉排序树)

    二叉搜索树 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Descr ...

  3. (转)JVM各种内存溢出是否产生dump

    对于java的内存溢出,如果配置-XX:+HeapDumpOnOutOfMemoryError,很明确的知道堆内存溢出时会生成dump文件.但永久代内存溢出不明确是否会生成,今天来做一个实验: 永久代 ...

  4. 大家好,我是一个JAVA初学者,想在这里记下自己学习过程中的点点滴滴,请多多关照

    大家好,我是一个JAVA初学者,想在这里记下自己学习JAVA的点点滴滴,请多多关照. 以前一直在QQ空间里记录的,但感觉有些麻烦,而且有些东西自己理解的并不完善甚至都不正确,现在开始在这里重新记录,从 ...

  5. android:exported属性

    这个属性用于指示该服务是否能够被其他应用程序组件调用或跟它交互.如果设置为true,则能够被调用或交互,否则不能.设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定 ...

  6. Django Model one

    models :URL---->http://www.cnblogs.com/wupeiqi/p/6216618.html null                     数据库中字段是否可以 ...

  7. iOS关于Xcode上的Other linker flags

    Targets选项下有Other linker flags的设置,用来填写XCode的链接器参数,如:-ObjC -all_load -force_load等.还记得我们在学习C程序的时候,从C代码到 ...

  8. PHP模版引擎twig wordpress中调用文章第一张图片

    wordpress当文章没有添加Featured media的时候, 就调用文章第一张图片, 调用的wordpress代码函数为: <?php echo catch_that_image(); ...

  9. dp好题 玲珑杯 Expected value of the expression

    152 - Expected value of the expression Time Limit:2s Memory Limit:128MByte Submissions:135Solved:65 ...

  10. 九度oj 题目1496:数列区间

    题目描述: 有一段长度为n(1<=n<=1000000)的数列,数列中的数字从左至右从1到n编号.初始时数列中的数字都是0. 接下来我们会对其进行m(1<=m<=100000) ...