封裝函數(shù)到實(shí)現(xiàn)簡(jiǎn)化版jQuery

需求分析

  • 獲取一個(gè)節(jié)點(diǎn)的所有兄弟;
  • 給一個(gè)節(jié)點(diǎn)添加加多個(gè) class;
  • DOM的API實(shí)現(xiàn)比較繁瑣,所以自己封裝 API ;

功能實(shí)現(xiàn)

1.封裝函數(shù)

  • 獲取兄弟
    操作步驟:
    在 html 中有一個(gè) ul 標(biāo)簽,在 ul 中有 5 個(gè) li 。
<ul>
  <li id="item1">選項(xiàng)1</li>
  <li id="item2">選項(xiàng)2</li>
  <li id="item3">選項(xiàng)3</li>
  <li id="item4">選項(xiàng)4</li>
  <li id="item5">選項(xiàng)5</li>
</ul>
  • 獲取 id 為 item3 的兄弟元素。
    首先定義一個(gè) allChildren 變量來存儲(chǔ) item3 的父節(jié)點(diǎn)所有的子元素;
var allChildren = item3.parentNode.children;

然后定義一個(gè)空數(shù)組來存兄弟元素;

var array = {length:0};
  • 遍歷所有的孩子節(jié)點(diǎn),如果不是 item3 ,那么就存到 arr 數(shù)組中。
for(let i = 0;i < allChildren.length;i++){
  if(allChildren[i] !== item3){
    array[array.length] = allChildren[i];   //數(shù)組下標(biāo)一一對(duì)應(yīng)存儲(chǔ) item 元素
    array.length+=1;
  }
}

這個(gè) array 數(shù)組是一個(gè)偽數(shù)組,它的原型鏈直接指向了 Object.protottype并沒有指向 Array.prototype (只有原型鏈中指向 Array.prototype 的數(shù)組才是真正的數(shù)組。)

  • 封裝成函數(shù)
    封裝成一個(gè)具名函數(shù),方便調(diào)用,return這個(gè)數(shù)組,給我們的函數(shù)加一個(gè)參數(shù),然后調(diào)用這個(gè)函數(shù)同時(shí)傳參item3 ;
function getSiblings(node){
  var allChildren = node.parentNode.children;
  var array = {length:0};
  for(let i = 0;i < allChildren.length;i++){
    if(allChildren[i] !== node){
      array[array.length] = allChildren[i];
      array.length+=1;
    }
  }
return array;
}
getSiblings(item3);
  • 再封裝一個(gè)給節(jié)點(diǎn)添加多個(gè)class的函數(shù)
function addClass = function(node, classes){
  classes.forEach( (value) => node.classList.add(value) );
 };
addClass(item3, ['a','b','c']);

調(diào)用這個(gè)函數(shù)同時(shí)傳參,這樣我們就可以給item3添加3個(gè)class,分別為‘a(chǎn)’,‘b’,‘c’

2.命名空間

現(xiàn)在我們有兩個(gè)API了,但是它們看起來很分散,我們有什么辦法能讓這兩個(gè)API有關(guān)聯(lián)呢?
我們可以聲明一個(gè)變量 window.reChenDom = {};

window.reChenDom = {};

reChenDom.getSiblings = function(node){
  var allChildren = node.parentNode.children;
  var array = {length:0};
  for(let i = 0;i < allChildren.length;i++){
    if(allChildren[i] !== node){
      array[array.length] = allChildren[i];
      array.length+=1;
    }
  }
  return array;
};

reChenDom.addClass = function(node, classes){
  classes.forEach( (value) => node.classList.add(value) );
 };

reChenDom.getSiblings(item3);
reChenDom.addClass(item3, ['a','b','c']);

這就叫做命名空間,也是一種設(shè)計(jì)模式。命名空間是非常有必要的,如果沒有命名空間,有兩個(gè)缺點(diǎn):第一是別人不知道你的倉(cāng)叫什么,另一個(gè)是會(huì)不知不覺把全局對(duì)象給覆蓋了。

3.能不能把 node 放在前面

接下來第三個(gè)特點(diǎn),我們要用的時(shí)候特別麻煩,總是要:

reChenDom.getSiblings(item3);
reChenDom.addClass(item3, ['a','b','c']);

能不能像下面這樣每次使用都方便點(diǎn)呢

item3.getSiblings();
item3.addClass( ['a','b','c'] );

有兩種辦法:

  1. 直接改Node的原型(擴(kuò)展 Node 接口直接在 Node.prototype 上加函數(shù)):
Node.prototype.getSiblings = function(){
  var allChildren = this.parentNode.children;
  var array = {length:0};
  for(let i = 0;i < allChildren.length;i++){
    if(allChildren[i] !== this){
      array[array.length] = allChildren[i];
      array.length+=1;
    }
  }
  return array;
};

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

item3.getSiblings();
item3.addClass( ['a','b','c'] );

this就是getSiblings()和item3.addClass( ['a','b','c'] )被調(diào)用時(shí)前面的對(duì)象。
其實(shí)這個(gè)函數(shù)寫的不好,為什么呢?這樣寫是在改Node的屬性,如果有其他人也這樣寫就會(huì)被覆蓋。

4.把 Node2 改個(gè)名字吧

  1. 用jQuery自己構(gòu)造的一個(gè)函數(shù),調(diào)用Node版本:(新的接口 BetterNode,接上面的第二種方法)
window.jQuery = function(node){
  return {
    getSiblings : function(){
    var allChildren = node.parentNode.children;
    var array = {length:0};
    for(let i = 0;i < allChildren.length;i++){
      if(allChildren[i] !== node){
        array[array.length] = allChildren[i];
        array.length+=1;
      }
    }
    return array;
    },
    addClass : function( classes){
      classes.forEach( (value) => node.classList.add(value) );
    }
  }
}

var node2 = jQuery(item3);
node2.getSiblings();
node2.addClass( ['a','b','c'] );

這樣就不僅僅可以傳Node,也可以傳其它的,比如選擇器:‘#item3’,所以這個(gè)名字就不叫node了:

window.jQuery = function(nodeOrSelector){
  let node;
    if( typeof nodeOrSelector === 'string'){
      node = document.querySelector(nodeOrSelector);
    }else{
      node = nodeOrSelector;
  };

  return {
    getSiblings : function(){
      var allChildren = node.parentNode.children;
      var array = {length:0};
      for(let i = 0;i < allChildren.length;i++){
        if(allChildren[i] !== node){
          array[array.length] = allChildren[i];
          array.length+=1;
        }
      }
      return array;
      },
      addClass : function( classes){
        classes.forEach( (value) => node.classList.add(value) );
      }
    }
}

var node2 = jQuery('#item3');
node2.getSiblings();
node2.addClass( ['a','b','c'] );

在這里我們用到了閉包,addClass這個(gè)函數(shù)用到了node,這個(gè)node不是函數(shù)的內(nèi)部聲明,那這個(gè)node是哪聲明的呢?是在外面,如果一個(gè)函數(shù)用到了它外面的變量,那么node和這個(gè)匿名函數(shù)統(tǒng)稱為閉包。

我們的jQuery能不能再厲害一點(diǎn)呢?如果我想同時(shí)操作多個(gè)'li',給這些'li'添家class,怎么辦呢,這個(gè)時(shí)候就不能叫node了,要叫nodes,之前的結(jié)構(gòu)已經(jīng)不適用了。新結(jié)構(gòu):

window.jQuery = function (nodeOrSelector){
  let nodes = {};
    if( typeof nodeOrSelector === 'string'){
      var temp = document.querySelectorAll(nodeOrSelector);
      for(let i=0; i<temp.length; i++){
        nodes[i] = temp[i];
      }
      nodes.length = temp.length;
    }else if( nodeOrSelector instanceof Node){
      nodes = { 0:nodeOrSelector,length:1 };
    }

  nodes.addClass = function(classes){
    classes.forEach( (value) => {
      for(let i=0; i<nodes.length; i++){
        nodes[i].classList.add(value);
      };
    });
  };
  return nodes;
};

var nodes = jQuery('ul>li')
nodes.addClass( ['red'] );

5.再給個(gè) alias 吧

window.$ = function (nodeOrSelector){...}
var $nodes = $('ul>li')   //在變量前加上一個(gè) $, 防止變量弄混

那現(xiàn)在還可以添加幾個(gè)有用的jQuery的API,比如說獲取/設(shè)置元素的文本:

window.$ = function (nodeOrSelector){
  let $nodes = {};
    if( typeof nodeOrSelector === 'string'){
      var temp = document.querySelectorAll(nodeOrSelector);
      for(let i=0; i<temp.length; i++){
        $nodes[i] = temp[i];
      }
      $nodes.length = temp.length;
    }else if( nodeOrSelector instanceof Node){
      $nodes = { 0:nodeOrSelector,length:1 };
    }

  $nodes.addClass = function(classes){
    classes.forEach( (value) => {
      for(let i=0; i<$nodes.length; i++){
        $nodes[i].classList.add(value);
      };
    });
  };

  $nodes.text = function(text){
    if( text === undefined ){
      var texts = [];
      for( let i=0; i<$nodes.length; i++){
        texts.push( $nodes[i].textContent )
      }
      return texts;
    }else{
      for( let i=0; i<$nodes.length; i++){
        $nodes[i].textContent = text;
      }
    }
  }

  return $nodes;
};

var $nodes = $('ul>li')
$nodes.addClass( ['red'] );
$nodes.text('hi');    //如果不給參數(shù)說明是獲取text,如果給了一個(gè)參數(shù)說明是設(shè)置text

這樣我們就從封裝兩個(gè)函數(shù)到實(shí)現(xiàn)了簡(jiǎn)化版的jQueryd,添加class和獲取/設(shè)置文本的API

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容