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');
相关文章
相关标签/搜索