仿照jquery封裝一個自己的js庫(二)

来源:http://www.cnblogs.com/djtao/archive/2016/11/01/6018097.html
-Advertisement-
Play Games

本篇為完結篇。主要講述如何造出輪子的高級特性。 一. css方法的高級操作 先看本文第一部分所講的dQuery css方法 javascript //css方法 dQuery.prototype.css=function(attr,value){ if(arguments.length==2){// ...


本篇為完結篇。主要講述如何造出輪子的高級特性。

一. css方法的高級操作

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

//css方法
dQuery.prototype.css=function(attr,value){
    if(arguments.length==2){//當參數個數為2時,使用設置css的方法
        var i=0;
        for(i=0;i<this.elements.length;i++){
            this.elements[i].style[attr]=value;
        }
    }else{//只有一個參數時獲取樣式
        return getStyle(this.elements[0],attr);
    }
};

在這個方法里,只能一次一行,且只能設置一個屬性。效率還不夠高。現在嘗試通過設置類似$d('#div1').css({width:'200px',height:'200px',background:'red'})這樣的方式,向css方法傳入一個json對象。允許一次性設置多個css樣式。或者允許鏈式操作。

1. 傳入json數據

傳入json實質上是一個參數。所以只有一個參數時,不僅僅是獲取,還可能是傳入了json數據。需要對傳入一個參數時,進行分類討論


json與for-in迴圈
var json={width:'200px',height:'200px',background:'red'};
var i=0;

for(i in json){
    alert(i+':'+json[i]);
}

這段程式可以依次彈出i(json屬性):json數據的視窗。


同理,在dQuery的css方法中也可以這麼做。

//css方法
dQuery.prototype.css=function(attr,value){
    if(arguments.length==2){//當參數個數為2時,使用設置css的方法
        var i=0;
        for(i=0;i<this.elements.length;i++){
            this.elements[i].style[attr]=value;
        }
    }else if(arguments.length==1){//只有一個參數時獲取樣式,或傳入json
        if(typeof attr=='string'){//attr為純文字時,設置樣式
            return getStyle(this.elements[0],attr);
        }else{//傳入json,批量設置css樣式
            for(i=0;i<this.elements.length;i++){
                var j='';

                for(j in attr){
                    this.elements[i].style[j]=attr[j];
                }
            }
        }
    }
};

測試成功。

#### (2)鏈式操作

函數鏈式操作原理:返回函數本身。
function show(str){
    alert(str);
    return show;//關鍵
}
show('abc')('bcd');

這段代碼將彈出兩個框,abc,和bcd。只要你想,可以無限地寫下去。


鏈式操作是jquery的主要特色。執行完一個方法之後,又返回到該對象。所以只需要在css函數的最後,補上return this。就完成了。根據這個思想,可以給每個方法加上return this,那麼你的js庫就可以實現完全的鏈式操作。

二. attr設置進一步——removeClass和addClass的實現

之前說到attr方法本質上和css方法一樣的。所以attr也可以用類似的方法設置——

//attr方法和css方法類似。
dQuery.prototype.attr=function(attr,value){
    if(arguments.length==2){//設置屬性
        var i=0;

        for(i=0;i<this.elements.length;i++){
            this.elements[i][attr]=value;
        }
    }else if(arguments.length==1){//獲取屬性
        if(typeof attr=='string'){
            return this.elements[0][attr];
        }else{
            for(i=0;i<this.elements.length;i++){
                var j='';

                for(j in attr){
                    this.elements[i][j]=attr[j];
                }
            }
        }
    }
    return this;
}

這段attr方法已經能夠實現鏈式操作和接收json數據。現在需要利用attr方法實現添加class和移除class的功能。
jquery中,addClassremoveClass的基本功能是:給addClass方法傳入一個字元串,目標元素的class尾部追加這段字元串,給removeClass傳入字元串,就把該字元串從目標元素的字元串中刪除掉。
用文字描述之後,實現就簡單不少了。

//addClass和removeClass的實現
dQuery.prototype.addClass=function(str){
    var i=0;

    for(i=0;i<this.elements.length;i++){
        if(this.elements[i].className==''){
            this.elements[i].className+=str;
        }else{
            this.elements[i].className+=' '+str;
        }
    }
    return this;
}

dQuery.prototype.removeClass=function(str){
    var i=0;

    for(i=0;i<this.elements.length;i++){ 
        var _className=this.elements[i].className
        if(!str){//如果不傳參,所有class都被清空。
            this.elements[i].className='';
        }else if(_className!=''){
            var arr=_className.split(' ');
            var j=0;

            for(j=0;j<arr.length;j++){
                if(arr[j]==str){
                    arr.splice(j,1);//從數組第j個起刪除1個(移除arr[j])
                    this.elements[i].className=arr.join(' ');//把數組重新轉化為字元串,並用空格分開。最後賦值給當下對象的className。
                }
            }
        }
    }
    return this;
}

註意,此段代碼有局限性,前提是操作者html操作正確時才能生效,類似$d('.div1').addClass('div1').removeClass('div1')雖然也能嘗試理解操作者意思並容錯。但是<div class="div1 div1 div2">這樣錯誤的class標記使用removeClass會發生錯誤。

3.阻止冒泡及預設事件

右鍵菜單(contextmenu)為例——jquery阻止預設事件,冒泡的機制是

$(function(){
    $(document).bind('contextmenu',function(){//對document綁定contextmenu事件,並執行函數。
        return false;
    })
})

return false在原生js中只處理阻止預設事件,但jquery可以兩樣都阻止。
首先,dQuery沒有bind方法。加上去就行。所謂綁定事件的框架很簡單,實際上和dQuery其它事件的框架是一樣的。

//綁定事件的方法:
dQuery.prototype.bind=function(sEv,fn){
    var i=0;

    for(i=0;i<this.elements.length;i++){
        myAddEvent(this.elements[i],sEv,fn);
    }
}

只要你在頁面上右鍵點擊,就會觸發函數。然而還不完善。

$d(function (){
    $d(document).bind('contextmenu', function (){
        return false;
    });
});

這裡添加了return false,但右擊,無法阻止任何事件。
究其深層原因,可以發現,回調函數是通過myAddEvent()這個函數實現的。
這就搞笑了。在myAddEvent()中,無論return什麼都沒有意義。所以要實現,完整的myAddEvent()代碼是:

//可重覆調用的載入函數
function myAddEvent(obj,sEv,fn){
    if(obj.attachEvent){
        obj.attachEvent('on'+sEv,function(){
            if(false==fn.call(obj)){//當調用return false的時候
                event.cancelBubble=true;//阻止冒泡
                return false;
            } 
        });
    }else{
        obj.addEventListener(sEv,function(ev){
            if(false==fn.call(obj)){
                ev.cancelBubble=true;//阻止冒泡
                ev.preventDefault();//火狐、chrome下用於阻止預設事件的語句
            } 
        },false);
    }
}

這樣,在三大瀏覽器下,已經實現了事件的綁定調用return false無預設事件。並且阻止冒泡。

三. 插件介面

插件需要一個機制。可以通過它自定義方法名稱,方法內容.
假設頁面有三個class為div1的div,擴展s一個size方法,獲取某類頁面元素的個數:

dQuery.prototype.extend=function (name, fn){
    dQuery.prototype[name]=fn;
};

$d().extend('size',function(){
    alert(this.element.length);
})

//調用插件里的方法和內部方法無差異
$d('.div1').size();

彈出結果就為3.
從這個角度說,插件機制編寫方法似乎更加便捷了。

1. animate運動框架

jquery的運動形式為:xxx.animate('',目標)。目標是一個json數據。
在運動學教程中有一個比較完美的運動框架startMove。startMove的json傳入的值為最終運動狀態。註意:和jquery不同,數值不帶單位,透明度的量度為100而不是1.

$d().extend('animate',function(json){
    var i=0;

    for(i=0;i<this.elements.length;i++){
        console.log(this.elements);
        startMove(this.elements[i],json,function(){alert('hehe')})
    }

    function getStyle(obj, attr)
    {
        if(obj.currentStyle)
        {
            return obj.currentStyle[attr];
        }
        else
        {
            return getComputedStyle(obj, false)[attr];
        }
    }
    
    function startMove(obj,json,fn){
      
        clearInterval(obj.timer);
          
      
        obj.timer=setInterval(function(){
            var bStop= true;//標志著所有運動都結束了
     
     
            //遍歷每個json屬性
            for(var attr in json){
     
     
                //取當前的屬性對象
                var iCur=0;
                if(attr=='opacity'){
                    iCur=parseInt(parseFloat(getStyle(obj,attr))*100);
                }else{
                    iCur=parseInt(getStyle(obj,attr));
                }
                 
     
                //定義速度值
                var iSpeed=(json[attr]-iCur)/8;
                iSpeed=iSpeed>0?Math.ceil(iSpeed):Math.floor(iSpeed);
                 
     
     
                //檢測停止:如果我發現某個值不等於目標點bStop就不能為true。
                if(iCur!==json[attr]){
                    bStop=false;
                }
                 
                 
     
                //計算
                if(attr=='opacity'){
                     obj.style[attr]=(iCur+iSpeed)/100;
                     obj.style.filter='alpha(opacity:'+(iSpeed+iCur)+')';
                }else{
                        obj.style[attr]=iCur+iSpeed+'px';
                }
                 
     
            }
     
            //檢測是否停止,是的話關掉定時器
            if(bStop==true){
                if(iCur==json[attr]){
                    clearInterval(obj.timer);
                    if(fn){fn();};  
                }
            }
        },20)
    }
});

那麼這個插件就寫完了。

【案例分析】上下滑動的幻燈片(選項卡)

基本思路和之前的選項卡案例差不多。但是切換過程是上下滑動的。因此所有圖片不能隱藏,應該是一張借一張縱向連接。運動是由一個大容器帶著一起運動。垂直運動距離由index()值決定同時,有了addClass和removeClass方法,可以設置一個class='active'的樣式。

  <div id="tab">
      <ul class="list_group">
          <li><a href="javascript:;">1</a></li>
          <li><a href="javascript:;">2</a></li>
          <li><a href="javascript:;">3</a></li>
          <li><a href="javascript:;">4</a></li>
      </ul>
      <div class="box">
        <div class="box2">
          <div class="content">
              <img alt="1" src="images/1.jpg">
          </div>
          <div class="content">
              <img alt="2" src="images/2.jpg">
          </div>
          <div class="content">
              <img alt="3" src="images/3.jpg">
          </div>
          <div class="content">
              <img alt="4" src="images/4.jpg">
          </div>
        </div>
      </div>
  </div>

css

*{
    margin:0;
    padding: 0;
}
ul{
    list-style: none;
}
a{text-decoration: none;}

#tab{
    width: 400px;
    margin:100px auto;
    position: relative;
}
.list_group li{
    float: left;
}
.list_group li a{
    display: block;
    width: 40px;
    line-height: 30px;
    text-align: center;
}
.box{
    clear: both;
    width: 402px;height: 302px;
    overflow: hidden;
    position: relative; 
}
.box2{
    position: absolute;
    top:0;

}
.content{
    width: 402px;
    height: 302px;
}
.content img{
    border: 1px solid black;
    width: 400px;height: 300px;
}
.active{
    background: #ccc;
}

javascript

$d(function(){
    $d('li').click(function(){
        var index=$d(this).index();
        var moveLength=-302*index;
        $d('.box2').animate({top:moveLength});
        $d('li').removeClass('active');
        $d(this).addClass('active');
    })
})

效果:

效果還是差強人意,可以做自動播放:

$d().extend('size',function(){
    return this.elements.length;
})//定義一個統計元素個數的dQuery插件

$d(function(){
    var iNow=0;
    var timer=null;

    //定義一個函數指令,可以移動.box2 -302*iNow個單位。
    function tab(){
        var moveLength=-302*iNow;   
        $d('.box2').animate({top:moveLength});
        $d('li').removeClass('active');
        $d('li').eq(iNow).addClass('active');
    }

    //自動播放定義定時器內的函數
    function timerInner(){
        iNow++;
        if(iNow==$d('li').size()){
            iNow=0;
        }
        tab();
    }
    timer=setInterval(timerInner,1000);//調用定時器

    //點擊事件函數
    $d('li').click(function(){  
        iNow=$d(this).index(); 
        tab();
    });

    //滑鼠移入選項卡範圍,關掉定時器timer,移出時再打開
    $d('#tab').hover(function(){
        clearInterval(timer);
    },function(){
        timer=setInterval(timerInner,1000)
    })

})

效果更加人性化了。

小結:與前一個版本的dQuery庫相比,寫法更進一步。

2.拖拽插件

加入頁面上有一個絕對定位的div#div1
樣式如下:

#div1{
    width: 100px;height: 100px;
    background: red;
    position: absolute;
}

引入方法是:

$d().extend('drag', function (){
    var i=0;
    
    for(i=0;i<this.elements.length;i++){
        drag(this.elements[i]);
    }
    
    function drag(oDiv){//拖拽函數
        oDiv.onmousedown=function (ev){
            var oEvent=ev||event;
            var disX=oEvent.clientX-oDiv.offsetLeft;
            var disY=oEvent.clientY-oDiv.offsetTop;
            
            document.onmousemove=function (ev){
                var oEvent=ev||event;
                
                oDiv.style.left=oEvent.clientX-disX+'px';
                oDiv.style.top=oEvent.clientY-disY+'px';
            };
            
            document.onmouseup=function (){
                document.onmousemove=null;
                document.onmouseup=null;
            };
        };
    }
});

調用方法:

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

一個簡單到令人髮指的效果就做好了。



附錄

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

//可重覆調用的載入函數
function myAddEvent(obj,sEv,fn){
    if(obj.attachEvent){
        obj.attachEvent('on'+sEv,function(){
            if(false==fn.call(obj)){//當調用return false的時候
                event.cancelBubble=true;
                return false;
            } 
        });
    }else{
        obj.addEventListener(sEv,function(ev){
            if(false==fn.call(obj)){
                ev.cancelBubble=true;
                ev.preventDefault();//火狐、chrome下用於阻止預設事件的語句
            } 
        },false);
    }
}



//class選擇器調用函數
function getByClass(oParent,sClass){
    var aEle=oParent.getElementsByTagName('*');//選擇父元素的所有元素
    var aResult=[];
    var re=new RegExp('\\b'+sClass+'\\b','i');//正則邊界
    var i=0;
    for(i=0;i<aEle.length;i++){
        if(re.test(aEle[i].className)){
            aResult.push(aEle[i]);
        }
    }
    return aResult;
}



//獲取計算後的樣式
function getStyle(obj,attr){
    //元素,樣式
    if(obj.currentStyle){//相容ie9及以下
        return obj.currentStyle[attr];
    }else{
        return getComputedStyle(obj,false)[attr];
    }
}

//定義dQuery對象
function dQuery(vArg){//參數是變體變數
    this.elements=[];//選擇器選擇的元素扔到這個數組中
    switch(typeof vArg){
        //如果參數是函數
        case 'function':
            myAddEvent(window,'load',vArg);
            break;
        //如果參數是字元串
        case 'string':
            switch(vArg.charAt(0)){
                case '#'://id選擇器參數應該為#號之後的字元段
                    var obj=document.getElementById(vArg.substring(1));
                    this.elements.push(obj);
                break;

                case '.'://class
                    this.elements=getByClass(document,vArg.substring(1));
                    break;

                default://標簽
                    this.elements=document.getElementsByTagName(vArg);
            }
            break;
        //如果參數是對象。
        case 'object':
            this.elements.push(vArg);
            
    }
}

//定義簡寫
function $d(vArg){
    return  new dQuery(vArg);
}



//對選擇器函數綁定click事件
dQuery.prototype.click=function(fn){
    var i=0;
    //對於返回器數組的內容
    for(i=0;i<this.elements.length;i++){
        myAddEvent(this.elements[i],'click',fn);
    }
    return this;
}

//對選擇器函數綁定show/hide事件
dQuery.prototype.show=function(){
    var i=0;
    //對於返回器數組的內容
    for(i=0;i<this.elements.length;i++){
        this.elements[i].style.display='block';
    }
    return this;
}

dQuery.prototype.hide=function(){
    var i=0;
    //對於返回器數組的內容
    for(i=0;i<this.elements.length;i++){
        this.elements[i].style.display='none';
    }
    return this;
};

//hover方法
dQuery.prototype.hover=function(fnover,fnout){
    var i=0;
    //對於返回器數組的內容
    for(i=0;i<this.elements.length;i++){
        //給這個對象一次性綁定兩個事件
        myAddEvent(this.elements[i],'mouseover',fnover);
        myAddEvent(this.elements[i],'mouseout',fnout);
    }
    return this;
};

//css方法
dQuery.prototype.css=function(attr,value){
    if(arguments.length==2){//當參數個數為2時,使用設置css的方法
        var i=0;
        for(i=0;i<this.elements.length;i++){
            this.elements[i].style[attr]=value;
        }
    }else if(arguments.length==1){//只有一個參數時獲取樣式,或傳入json
        if(typeof attr=='string'){//attr為純文字時,設置樣式
            return getStyle(this.elements[0],attr);
        }else{//傳入json,批量設置css樣式
            for(i=0;i<this.elements.length;i++){
                var j='';

                for(j in attr){
                    this.elements[i].style[j]=attr[j];
                }
            }
        }       
    }
    return this;
};

//toggle方法:
dQuery.prototype.toggle=function(){
    var _arguments=arguments;//把toggle的arguments存起來,以便在其它函數中可以調用。

    //私有計數器,計數器會被一組對象所享用。
    function addToggle(obj){
        var count=0;
        myAddEvent(obj,'click',function(){
            _arguments[count++%_arguments.length].call(obj);
        })
    }

    var i=0;
    for(i=0;i<this.elements.length;i++){
        addToggle(this.elements[i]);
    } 
    return this;
}


//attr方法和css方法類似。
dQuery.prototype.attr=function(attr,value){
    if(arguments.length==2){//設置屬性
        var i=0;

        for(i=0;i<this.elements.length;i++){
            this.elements[i][attr]=value;
        }
    }else if(arguments.length==1){//獲取屬性
        if(typeof attr=='string'){
            return this.elements[0][attr];
        }else{
            for(i=0;i<this.elements.
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.1概述 定義一個用於創建對象的介面,讓子類決定實例化哪一個類。Factory Method使一個類的實例化延遲到其子類。這就是工廠方法模式的定義。 得到一個類的子類的實例最常用的辦法就是使用new運算符和該子類的構造方法,但是在某些情況下,用戶可能不應該或無法使用這種辦法來得到一個子類的實例,其 ...
  • 像activeMQ等消息隊列中,我們經常會使用發佈訂閱模式,但是你有沒有想過,客戶端時如何及時得到訂閱的主題的信息?其實就裡就用到了觀察者模式。在軟體系統中,當一個對象的行為依賴於另一個對象的狀態時,觀察者模式就相當有用。如果不使用觀察者模式提供的通用結構,而需要我們實現類似的功能,想想我們該如何實 ...
  • 前言 今天複習一下SpringMVC+Hibernate的搭建,本來想著將Spring-Security許可權控制框架也映入其中的,但是發現內容太多了,Spring-Security的就留在下一篇吧,這篇主要搭建SpringMVC4.1.4和Hibernate4.3.8,之前也打了好多SpringMV ...
  • 轉自:https://yq.aliyun.com/articles/8611 微服務架構的理論基礎 - 康威定律 摘要可能出乎很多人意料之外的一個事實是,微服務很多核心理念其實在半個世紀前的一篇文章中就被闡述過了,而且這篇文章中的很多論點在軟體開發飛速發展的這半個世紀中竟然一再被驗證,這就是康威定律 ...
  • 在我的博客《Hibernate總結(一)》在對資料庫的增刪改查前後重覆的使用了得到Session與關閉Session等操作,因此我想到了模板設計模式。 模板設計模式概述: 定義一個操作中的演算法的骨架,而將步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重定義演算法的某些特定步驟。 廢話不 ...
  • 閱讀目錄 前言 六邊形架構 終於開始建項目了 DDD中的3個臭皮匠 CQRS(Command Query Responsibility Segregation) 結語 閱讀目錄 前言 六邊形架構 終於開始建項目了 DDD中的3個臭皮匠 CQRS(Command Query Responsibilit ...
  • 一、簡介 do_App的openPage支持16種過場動畫,這個示例直觀的展示16種動畫的效果。適合初學者。 二、效果圖 三、相關下載 https://github.com/do-project/code4do/tree/master/open_animation 四、相關討論 http://bbs ...
  • 線上預覽 插件說明 - jbox 是一款基於 jQuery 的多功能對話框插件,能夠實現網站的整體風格效果,給用戶一個新的視覺享受。 運行環境 - 相容 IE6+、Firefox、Chrome、Safari、Opera 等主流瀏覽器。備註:IE不支持邊框的圓角樣式,不推薦大家使用蛋痛的IE瀏覽器。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...