jQuery初體驗

學習了DOM api 以後,作些簡單的小練習node

在HTML寫5個無序列表segmentfault

<ul>
    <li id="item1">選項1</li>
    <li id="item2">選項2</li>
    <li id="item3">選項3</li>
    <li id="item4">選項4</li>
    <li id="item5">選項5</li>
</ul>

獲取item3的因此的兄弟節點如何作呢?api

DOM 提供了nextSbiling`previousSbiling,有人說用parentNode.children獲取,可是這樣作item3`也在裏面了,因此就本身作一個API,
我把這個名字叫作getSiblings數組

//node 是參數,傳遞item3進來,傳出item3的全部兄弟節點。
function getSiblings(node){
    var allChildren = node.parentNode.children    //先獲取item3全部兄弟節點,保存在allChildren中,是一個僞數組
    
    //剔除item3節點
    var array = {length:0};    //建立一個僞數組,存儲item3節點的全部兄弟節點,由於是僞數組,因此給它添加length:0
    for(var i=0; i<allChildren.length; i++){     //用for循環遍歷allChildren數組
       if(allChildren[i] !== node){    //用if循環判斷若是有allChildren[i]是item3剔除出去
           array[array.length] = allChildren[i];    //由於array是僞數組,沒有push,因此用array[array.length]添加進去
           array.length++;    //僞數組,length不會自動加1
        }
    }   
    return array;    //返回出去
}

getSiblings(item3);

若是要給item3添加或刪除class,用自帶的API 只能一個個添加,看下面瀏覽器

item3.classList.add('a')
item3.classList.add('b')
item3.classList.add('c')
item3.classList.remove('b')

若是本身作一個API,是否是能夠實現批量添加呢函數

//node classes 是參數,要添加或刪除class的節點,這裏傳遞的classes是僞數組
 function addOrRemoveClass(node,classes){
    for(var aaa in classes){   //用for循環遍歷classes的key
        var a = classes[aaa];    //用一個變量來存儲classes的value
        if(a){    //用if循環判斷classes的value是true仍是false
            node.classList.add(aaa);   //若是是true,則添加key
        }else {
            node.classList.remove(aaa);//不然移除
        }
    }
}
addOrRemoveClass(item3,{'a':true,'b':false,'c':true});

上面代碼if...else...中有重複代碼,可優化成下面這樣。學習

優化代碼的原則就是提出重複的代碼。優化

var methondName = a?'add':'remove';    //add、remove要用字符串
node.classList[methondName](aaa)

下面先看添加class怎麼實現。
foreach接受一個函數參數,函數參數是數組的valuekey,能夠看以前總結的Array基本概念this

//node classes 是參數,要添加或刪除class的節點,這裏classes是數組
function addClass(node,classes){
    classes.forEach(value => node.classList.add(value))  //把數組的`value`添加到class中
}
addClass(item3,[a,b,c])

用這樣的方法建立好用的API,但有個不足的地方,這些都是全局變量,若是在大項目中,很容易覆蓋別人的變量。prototype

因此把本身寫的API 添加到Node的原型上

Node.prototype.getSiblings = function(){
    var allChildren = this.parentNode.children;    //this指代調用時前面的那個
    var array = {length:0};
    for(var i=0; i<allChildren.length;i++){
        if(allChildren[i] !== this){    //this指代調用時前面的那個
            array[array.length] = allChildren[i];
            array.length++;
        }
    }
    return array
}

Node.prototype.addClass = function(classes){
    classes.forEach(value => this.classList.add(value))
}

item3.getSibllings.call(item3)    //用call的話,直接指定this,不用call的話,是隱示指向`.`前面那個
item3.addClass.call(item3,['a','b','c'])

不理解this的能夠看函數小知識點

在原型上面添加方法也和上面同樣有弊端,第一項目一大,全部人都在原型上面添加,就會形成原型裏面很混亂;第二你也不知作別人有沒寫和你同樣的方法,這又會形成覆蓋的問題。
由於這是在Node上直接寫的,那我能不能本身寫一個Node這樣就不會和人家重名了,就算遇到,改一個名字唄。

新的Node叫它Node2,它返回一個對象,對象裏面有兩個函數,也就是getSiblingsaddClass,並用node2初始化。

//Node2至關於原來的Node
window.Node2 = funcuntion(){    
    return{        //返回2個方法
        getSiblings:function(){},
        addClass:function(){}
    }
}

var node2 = Node2(''item3)    //初始化
node2.getSiblings.call()    //正常調用
node2.addClass.call()

getSiblingsaddClass方法仍是和前面同樣,以前咱們用this來指代node,可是這裏用this的話會指向node2,而咱們要操做的是node,因此要把this變成node才行。

window.Node2 = function(node){    //參數是要操做的節點
    return{
        getSiblings:function(){
            var allChildren = node.parentNode.children;     //這裏不能用this 了,由於用this會變成前面的.node2,而這裏咱們要操做的是node
            var array = {length:0};
            for(var i = 0; i < allChildren.length;i++){
                if(allChildren[i] !== node){
                    array[array.length] = allChildren[i];
                    array.length++;
                }
            }
            return array;
        },
        
        addClass:function(classes){
            classes.forEach(value => node.classList.add(value))
        }
    }
}

var node2 = Node2(item3);
node2.getSiblings();    //這裏爲何不須要傳入節點,由於在初始化的時候,已經把節點傳進去了
node2.addClass(['a','b','c']);

若是把Node2改爲jQuery其餘都不變,是否是也能夠,看下面,getSiblingsaddClass裏面的方法就不寫了。

window.jQuery = funcuntion(){    
    return{
        getSiblings:function(){},
        addClass:function(){}
    }
}

var node2 = jQuery(item3)    
node2.getSiblings.call()
node2.addClass.call()

jQuery就是一個升級的DOM,它接受一個參數,而後返回一個新的對象,這個新對象有新的API,就是這邊的getSiblingsaddClass,它們的內部是怎麼實現的呢,仍是去調用瀏覽器提供的API,區別是你就寫下面調用的API,一句話就能夠了,而jQuery是在裏面調用老的API,進行復雜的轉換。

簡單的說jQuery就是接收一個參數,返回給你一個新對象,你調用它提供的API 就能夠了。

固然了這只是jQuery的基本原理,實際遠比它複雜。

再來看下面的例子,若是我傳遞的是選擇器怎麼操做呢?

window.jQuery = function(nodeOrSelector){
    var node;    //存儲if判斷後的參數
    if(typeof nodeOrSelector === 'string'){  //由於選擇器是以字符串的形式傳遞進來的,因此先要用if進行判斷,typeof用來判斷類型的
        node = document.querySelector(nodeOrSelector);   //若是是字符串,就是選擇器,根據選擇器找到對應的節點,並保存在聲明的變量中
    }else{
        node = nodeOrSelector;  //若是不是,直接保存在變量中
    }
    
    return{
        getSiblings:function(){
            var allChildren = node.parentNode.children;
            var array={length:0};
            for(var i = 0 ;i < allChildren.length;i++){
                if(allChildren[i] !== node){
                    array[array.length] = allChildren[i];
                    array.length++;
                }
            }
            return array;
        },
        
        addClass:function(classes){
            classes.forEach(value => node.classList.add(value))
        }
    }
    
}

var node2 = jQuery('#item3');    //傳遞選擇器,使用字符串的形式表示
node2.getSiblings();
node2.addClass(['red']);

初始化的時候傳遞了一個選擇器,jQuery 內部先進行判斷,傳進來的是否是字符串,若是是,我就找到對應的節點;後面的操做和以前講的同樣。

咱們再來升級下jQuery,若是我想同時操做5個節點呢?看下面:

window.jQuery = function(nodeOrSelector){
    var nodes = {}; //querySelectorAll輸出的是僞數組,因此用來存儲的變量也要初始化成僞數組
    if(typeof nodeOrSelector === 'string'){     //用typeof判斷傳進來的類型
        var temp = document.querySelectorAll(nodeOrSelector);//若是傳進來的是選擇器,就獲取對應全部的節點,querySelectorAll輸出的是僞數組
        //由於獲取到的不是純淨的僞數組(原型不是直接指向Object),先用臨時變量存儲,再用for循環遍歷一遍就能夠了
        for(var i = 0; i < temp.length; i++){
            nodes[i] = temp[i]
        }
        nodes.length = temp.length;     //把臨時變量的length長度賦值給nodes,下面遍歷時須要用到
    }else if(nodeOrSelector instanceof node){   //instanceof用來判斷傳進來的是否是節點,由於節點只能傳進來一個
        nodes = {   //由於上面用的是僞數組,因此這邊也要一致,雖然節點是一個,但也要作成僞數組的形式
            0:nodeOrSelector,
            length:1
        };
    }
    
    //這時僞數組內部是{0,1,2,3,4}的屬性,沒有addclass的方法,因此這邊能夠直接往裏面添加方法
    nodes.addClass = function(classes){ 
        for(var i = 0; i < nodes.length; i++){
            classes.forEach(value => nodes[i].classList.add(value));    //nodes不是元素,這邊操做是在元素身上,因此要for循環遍歷,動態添加
        }
    }
    
    nodes.text = function(text){    
        if(text === undefined){     //這裏默認沒傳參數就是獲取節點文本
            var texts = [];     //文本最終是數組的形式
            for(var i = 0; i < nodes.length; i++){  //遍歷nodes,就能夠獲取到了
                texts.push(nodes[i].textContent);
        }
        return texts;
        }else{      //有參數就是設置文本
            for(var i = 0; i < nodes.length; i++){
                nodes[i].textContent = text;
            }
        }
    }
    
    return nodes;   //由於整個jQuery內部是一個僞數組,因此這邊返回出去了確定也是一個僞數組了
}

var node2 = jQuery('ul > li');
node2.addClass(['red']);
console.log(node2.text());
node2.text('hi');

最後簡化一下jQuery ,初始化時的變量名最好帶上$,後面好辨認。

window.$ = jQuery;
var $node2 = $('item3');
相關文章
相關標籤/搜索