JavaScript入门(五)——jQuery库

一、前言

简短的叙说一下,实习多少个原理,观念,其实写过多事物,思想算是最重大的。

1、目的:将写壹个最为节点的树形目录布局,如下图

奥门新浦京官方网站 1

jQuery

JS最成千上万采用的库,五分四-十分之八以上的网址使用。jQuery的意见:Write Less, Do
More。

优点:

  • 消亡浏览器差别:你不须求本人写冗长的代码来针对区别的浏览器来绑定事件,编写AJAX等代码;
  • 简练的操作DOM的措施:写$(‘#test’卡塔尔(قطر‎断定比document.getElementById(‘test’卡塔尔来得简洁;
  • 自在完成动画、订正CSS等各个操作。

脚下jQuery有1.x、2.x和3.x,前三个本子现已不复更新。2.x移除了对IE
6、7、8的帮忙,代码更简短。接纳哪位版本首要在于你是或不是想扶植IE 6~8。

下载地址是:http://jquery.com/download/。仅仅是四个.js文件。有未压缩(uncompressed,jquery-x.x.x.js)和压缩(minified,jquery-x.x.x.min.js)五个版本。

哪些兑现三个 Virtual DOM 及源码解析

步骤:

1、你的下载 插件  ztree。然后安顿在你的项目中。

<script src="__PUBLIC__/js/jquery-1.4.4.min.js"></script> 
<script src="__PUBLIC__/js/jquery.ztree.core-3.5.js"></script>

2、相关CSS

<link rel="stylesheet" href="__PUBLIC__/css/zTreeStyle/zTreeStyle.css" type="text/css"> 
<link rel="stylesheet" href="__PUBLIC__/css/zTree.css" type="text/css">

上述CSS 和JS 以你本身的为准。

3、目录布局DIV

<div class="content_wrap"  style="background:#666;"> 
    <div class="zTreeDemoBackground left"> 
        <ul id="treeDemo" class="ztree"></ul> 
    </div> 
</div> 
<div class="content-text" id="text"></div>

4、本身独立js中的代码

<SCRIPT  src="__PUBLIC__/js/ztreeonload.js"></SCRIPT>

里头写的相干职能及布局!

//配置项 
var setting = { 
     isSimpleData : true,              //数据是否采用简单 Array 格式,默认false  性   
     showLine : true,                  //是否显示节点间的连线   
     checkable : true,    
     callback: { 
         onClick: zTreeOnClick       
     } 
 }; 

 var zNodes;//数据变量 

 //ajax提交数据,请求后台PHP处理返回出目录结构json数据 
 $.ajax({ 
     url:"/admin.php/Ztree", 
     type: "get", 
     async: false, 
     dataType:"json",   
     success: function (data) { 
             //alert(data); 
             zNodes=data;    //将请求返回的数据存起来 
              //alert(zNodes); 
     }, 
     error: function (){//请求失败处理函数   
         alert('请求失败');   
     },   
 }) 

 //初始化ztree目录结构视图! 
 $(document).ready(function(){ 
     //alert("111"); 
     $.fn.zTree.init($("#treeDemo"), setting, zNodes); 
 });

5、后台PHP 递归算法,从数据库中搜寻目录构造并且调换 JSON数据

地方:如4中,AJAX所哀告的
【/admin.php/Ztree】作者这里是用的ThinkPHP框架,所以url是以此样子,以你和煦的接口文件为准!

<?php 
            //父节点数组 
            $arr=array(); 
            $arr_str0 = array("name" =>'函数库查询','children'=>$this->SelectSon(1));       //父节点  Pid=1; 
            $arr_str1 = array("name" =>'数据库查询','children'=>$this->SelectSon(2));       //父节点  Pid=2; 

            array_push($arr, $arr_str0); 
            array_push($arr, $arr_str1);//这里是2个父节点。 

            echo(json_encode($arr)); //这是最后返回给页面,也就是返回给AJAX请求后所得的返回数据 JSON数据 
?> 

//这里仅仅是一个方法,一个调用SelectSon()方法,返回一个数组集合!但其中用的是递归! 
<?php 
        //查找子节点        Pid=父节点ID 
        private function SelectSon($Pid){ 

            $m=M('ztree'); 

            if(($info=$m->where("Pid='$Pid'")->select())) //查找该父ID下的子ID 
            { 
                $data=array(); 
                for ($i=0; $i < count($info) ; $i++)  
                {  
                    $da=array("name" =>$info[$i]['name'],'children'=>$this->SelectSon($info[$i]['id']));  //递归算法! 

                    array_push($data, $da);//加入子节点数组 
                }; 

                return $data;//一次性返回子节点数组,他们成为同级子节点。 
            } 
            else 
            { 
                return null; 
            } 

        } 
?>

留意:由于本身是用的thinkphp框架。所以在措施调用上
有个别不一样,纯PHP文件中,思路应该是同等的,

率先是: 写三个数组。二个父节点的数组。

附带:  写叁个主意,传递的参数是
父节点的ID,查询其子节点,在子节点中查询现在,用递归的措施三番一遍查找子节点的子节点,直到最后查询实现之后,重回数组给调用方法的父节点数组。然后再

echo(json_encode($arr));

转码成 JSON 将其出口,以便于AJAX异步访谈,获得JSON数据。

拿到之后,回到刚刚的JS效能代码中,直接带头化树引得布局,将其JSON数据传入OK。

使用jQuery

奥门新浦京官方网站,只需选拔<script>引进进来。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>

</body>
    <script>
        alert('jQuery版本:' + $.fn.jquery);
    </script>
</html>

Virtual DOM算法

总结:

其利害攸关思量分2步走。第一步,是怎么样能把目录生成出来。先测量试验时,能够用静态数据。近似于

var node=[ 
    {name:'父节点',children:[{name:'子节点',children:null},{name:'同级子节点',children:null}]} 
] 

先分析一下,这串数据,他有什么规律。你就会发现。其实很有规律。无限节点,其实就是每个json中,有children,而且 
还有同级子节点。

你先用固定数据 生成目录构造今后

你就能够早先思忖,动态的向node传目录布局的多少了。正是大家前面所谓的
AJAX伏乞 PHP获得JSON数据,

PHP管理中,作者用的是递归算法,再次回到JSON数据。及实现了。目录布局。

哦对了。

$m=M('ztree');

那句代码是thinkphp 实例化 数据操作对象的。

用来查询数据库中,节点是还是不是存在。就是存在子节点,就重回给子节点数组,有几个就加入子节点数组中,查询完了。然后二回性重回,他们就成了同级子节点了

$符号

$是jQuery最有名的符号。其独具功用都封装在此个全局变量中,$是官方的变量名,它就是jQuery的外号。

alert(window.jQuery);   // jQuery(selector, context)...
alert(window.$);
alert($ === jQuery);
alert(typeof $);    // function

就此,$便是四个函数,函数也是指标,$还会有不菲性质。

比方您看看的$不是临近于jQuery(selector, context)...,那是因为JS压缩工具会对函数及参数改名,压缩之后大概变为function (a,b){return new n.fn.init(a,b)}

绝大许多场合下,我们向来用$,若是很倒霉地$被侵吞了,况且不能够改,大家就让jQuery把$交出来(调用jQuery.noConflict()),进而只可以接纳jQuery。

alert($);   // jQuery(selector, context)...
jQuery.noConflict();
alert($);   // undefined
alert(jQuery);  // jQuery(selector, context)...

   
web页面有叁个应和的DOM树,在价值观支付页面时,每一遍页面供给被更新时,都亟待手动操作DOM来进展更新,不过我们通晓DOM操作对质量来讲是杰出不友好的,会耳濡目染页面包车型客车重排,进而影响页面的性能。因而在React和VUE2.0+引进了虚构DOM的定义,她俩的原理是:把下马看花的DOM树转变到javascript对象树,也便是编造DOM,每一遍数据供给被更新的时候,它会转换二个新的捏造DOM,并且和上次生成的虚构DOM进行对照,对发生变化的多寡做批量立异。—(因为操作JS对象会越来越快,更简便,比操作DOM来讲卡塔尔。
我们领略web页面是由二个个HTML成分嵌套组合而成的,当我们采纳javascript来汇报这么些要素的时候,那一个要素得以总结的被代表成纯粹的JSON对象。

选择器

这是jQuery的核心,类似于$('#dom-id')。初志是为了幸免麻烦的getElementById和getElementsByTagName代码。有了jQuery,能够非常的慢稳固到叁个或多个DOM节点。

无论是提一句,table的标准写法如下。注意<table>的子结点是<tbody>。

<table>
        <tbody>
            <tr>
                <td>1</td>
                <td>2</td>
                <td>3</td>
            </tr>
            <tr>
                <td>4</td>
                <td>5</td>
                <td>6</td>
            </tr>
            <tr>
                <td>7</td>
                <td>8</td>
                <td>9</td>
            </tr>
        </tbody>
    </table>

举个例子如下HTML代码:

按ID查找

类似于var div = $('#abc');,重回的是jQuery对象。jQuery对象相像数组,它的每种成分都以三个引用了DOM节点的对象。如果存在,则形如[<div id="abc">...</div>],不然是个空数组[]

jQuery的七个亮点正是:jQuery的选取器不会重临undefined大概null,因而不必决断if
(div === undefinedState of Qatar。

jQuery对象和DOM对象时期能够相互转化

诚如意况下,无需拿到和操作DOM对象,而是径直操作jQuery对象,若是您有叁个DOM对象,简单地利用$(aDomObject)包装成jQuery对象就足以行使jQuery
API了。

var div = $('#abc');
alert(div);     // [object Object]
var divDom = div.get(0);    // [object HTMLDivElement],DOM对象
alert(divDom);  
var div2 = $(divDom);
alert(div2);    // [object Object]
<div id="container" class="container">
   <ul id="list">
     <li class="item">111</li>
     <li class="item">222</li>
     <li class="item">333</li>
   </ul>
   <button class="btn btn-blue"><em>提交</em></button>
</div>

按tag查找

类似于var ps = $('p');

var ps = $('p');
alert(ps.length);   // 返回页面上所有的p标签个数

上面是真实的DOM树布局,大家得以应用javascript中的json对象来代表的话,产生如下:

按class查找

var c1 = $('.red'); // 查找class="red"的节点
var c2 = $('.red.green'); // 查找class同时包含red和green的节点,不能有空格。 class="red green" class="blue green red"这些都命中
var element = {
      tagName: 'div',
      props: {   // DOM的属性
        id: 'container',
        class: 'container'
      },
      children: [
        {
          tagName: 'ul',
          props: {
            id: 'list'
          },
          children: [
            {tagName: 'li', props: {class: 'item'}, children: ['111']},
            {tagName: 'li', props: {class: 'item'}, children: ['222']},
            {tagName: 'li', props: {class: 'item'}, children: ['333']}
          ]
        },
        {
          tagName: 'button',
          props: {
            class: 'btn btn-blue'
          },
          children: [
            {
              tagName: 'em',
              children: ['提交']
            }
          ]
        }
      ]
   };

按属性查找

节点往往有许多品质,依据属性查找恐怕更便于,举例在表单中。

var email = $('[name=email]');  // 命中<??? name="email">
var pwd = $('[type=password]'); // 命中<??? type="password">
var i = $('[items="A B"]');     // 命中<??? items="A B">

当属性值包括空格等特殊字符,供给选取引号括起来。

按属性查找还能动用前缀查找也许后缀查找。进一层切合通过class属性查找,且不受class富含多个名称的熏陶

var icons = $('[name^=icon]');  // 命中name属性值以icon开头的
var withs = $('[name$=with]'); // 命中name属性值以with结尾的

var icons2 = $('[class^="icon-"]');    // 命中class属性值包含至少一个以"icon-"开头的 class="icon-clock", class="abc icon-home"都会命中!

于是大家得以应用javascript对象表示DOM的信息和构造,当状态更换的时候,重新渲染那个javascript对象的布局,然后能够运用新渲染的目的树去和旧的树去相比,记录两颗树的反差,两颗树的差异正是大家要求对页面真正的DOM操作,然后把她们利用到确实的DOM树上,页面就收获更新。视图的万事布局确实全渲染了,不过最终操作DOM的时候,只改造不一样的地方。
就此大家得以总括一下 Virtual DOM算法:
1.
用javascript对象协会来表示DOM树的协会,然后用这一个树营造四个确实的DOM树,插入到文书档案中。
2.
当状态改换的时候,重新组织大器晚成颗新的目的树,然后利用新的目的树与旧的靶子树进行自己检查自纠,记录两颗树的差别。

整合查找

var emailInput = $('input[name=email]');  // 只命中<input>里面的name为email的
var red = $('td.red');  // 只命中<td>里面的class包含red的
  1. 把记录下来的反差用到步骤1所营造的确实的DOM树上。视图就立异了。

多项采用器

多项选用器就是把五个选项器用,构成起来一块选。

var pdiv = $('p,div');  // 命中所有的<p>和<div>
var redgreen = $('p.red,p.green');  // 命中<p>class包含red或者包含green的

选出来的要素是依照它们在HTML中现身的顺序排列的,并且不会有双重成分。例如,<p class="red green">不会被地方的$('p.red,p.green')采用五次。

算法实现:
2-1 使用javascript对象模拟DOM树。
使用javascript来代表三个DOM节点,犹如上JSON的多寡,大家只必要记录它的节点类型,属性和子节点就能够。

层级采纳器

层级接收器(Descendant
Selector),DOM的构造便是层级布局,所以我们常常要基于层级关系举行抉择。

七个DOM成分具备层级关系,不供给是向来的上下级,只借使祖上就可以!使用相仿于$('ancestor descendant')慎选,层级间接选举拔空格。

var lis = $('ul.lang li');  // 选择所有的<li>

// 下面的方法都可以选择<li class="lang-javascript">JavaScript</li>
var js1 = $('ul.lang li.lang-javascript');
var js2 = $('div.testing li.lang-javascript');
alert(js1 === js2); // false,两个不同的jQuery对象
alert(js1.get(0) === js2.get(0)); // true,但是里面的DOM是同一个

很分明,层级选用器比单个选拔器减少了增选范围,因为首先要定位祖先(越多情形下是它的父结点!)节点,才干选择相应的子节点,定位更纯粹。

例如,$('form[name=upload] input');只接收name属性值是upload的form里面包车型大巴input。因为页面往往有成都百货上千form。

多层选取器同样支撑,比如$('form.test p input');

element.js 代码如下:

子采取器(Child Selector)

类似于$('parent>child')。节制了层级关系必需是老爹和儿子关系,<child>节点必需是<parent>节点的直属子节点。

function Element(tagName, props, children) {
  this.tagName = tagName;
  this.props = props;
  this.children = children;
}
Element.prototype.render = function() {
  var el = document.createElement(this.tagName);
  var props = this.props;
  // 遍历子节点,依次设置子节点的属性
  for (var propName in props) {
    var propValue = props[propName];
    el.setAttribute(propName, propValue);
  }
  // 保存子节点
  var childrens = this.children || [];
  // 遍历子节点,使用递归的方式 渲染
  childrens.forEach(function(child) {
    var childEl = (child instanceof Element) ? child.render() // 如果子节点也是虚拟DOM,递归构建DOM节点
      : document.createTextNode(child);    // 如果是字符串的话,只构建文本节点
    el.appendChild(childEl);
  });
  return el;
};
module.exports = function(tagName, props, children) {
  return new Element(tagName, props, children);
}

过滤器(Filter)

过滤器日常不单独接受,它日常附加在选用器上,扶植大家更规范地稳定成分。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div class="testing">
    <ul class="lang">
        <li class="lang-javascript">JavaScript</li>
        <li class="lang-python">Python</li>
        <li class="lang-lua">Lua</li>
    </ul>
</div>
</body>
    <script>
        var a = $('ul.lang li'); // 选出JavaScript、Python和Lua 3个节点

        var b = $('ul.lang li:first-child'); // 仅选出JavaScript
        var c = $('ul.lang li:last-child'); // 仅选出Lua
        var d = $('ul.lang li:nth-child(2)'); // 选出第N个元素,N从1开始
        var e = $('ul.lang li:nth-child(even)'); // 选出序号为偶数的元素
        var f = $('ul.lang li:nth-child(odd)'); // 选出序号为奇数的元素
    </script>
</html>

入口index.js代码如下:

表单相关

转为表单设计的选用器。

  • :input:能够筛选<input><textarea><select><button>
  • :file:能够选用<input type="file">,和input[type=file]一样;
  • :checkbox:能够筛选复选框,和input[type=checkbox]一样;
  • :radio:能够选择单选框,和input[type=radio]一样;
  • :focus:能够选用当前输入主旨的因素,举个例子把光标放到一个<input>上,用$('input:focus')就足以选出?;
  • :checked:接受当前勾上的单选框和复选框,用那些选取器能够立刻获得顾客挑选的花色,如$('input[type=radio]:checked')
  • :enabled:能够筛选能够健康输入的<input><select>等,相当于未有灰掉的输入;
  • :disabled:和:enabled赶巧相反,接纳那么些不可能输入的。

别的,jQuery还应该有非常多灵光的选用器,譬喻,选出可以知道的或隐匿的成分:

$('div:visible'); // 所有可见的div
$('div:hidden'); // 所有隐藏的div
var el = require('./element');

var element = el('div', {id: 'container', class: 'container'}, [
  el('ul', {id: 'list'},[
    el('li', {class: 'item'}, ['111']),
    el('li', {class: 'item'}, ['222']),
    el('li', {class: 'item'}, ['333']),
  ]),
  el('button', {class: 'btn btn-blue'}, [
    el('em', {class: ''}, ['提交'])
  ])
]);

var elemRoot = element.render();
document.body.appendChild(elemRoot);

检索和过滤和照耀

获取叁个jQuery对象后,还是能以那个目的为原则,举办搜寻和过滤。

展开页面就可以见到成效。

查找(find)

最布衣蔬食的查找是在有些节点的全体子节点中找出,使用find(卡塔尔(قطر‎方法,它自个儿又吸取三个随机的接纳器。

风流罗曼蒂克经要从今今后时此刻节点最首发展查找,使用parent(卡塔尔方法。

对此坐落于同生机勃勃层级的节点,能够透过next(State of Qatar和prev(卡塔尔方法。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <ul class="lang">
        <li class="js dy">JavaScript</li>
        <li class="dy">Python</li>
        <li id="swift">Swift</li>
        <li class="dy">Scheme</li>
        <li name="haskell">Haskell</li>
    </ul>
</div>
</body>
    <script>
        var ul = $('ul.lang'); // 获得<ul>
        var dy = ul.find('.dy'); // 获得JavaScript, Python, Scheme
        var swf = ul.find('#swift'); // 获得Swift
        var hsk = ul.find('[name=haskell]'); // 获得Haskell

        var parent = swf.parent();  // <ul>
        var a = swf.parent('div.red');  // 从父结点开始向上查找,直到找到符合的立即返回

        var py = swf.prev();    // Python
        var js = swf.prev('.js');   // JavaScript,从左兄弟找,找到一个立即返回

        var sch = swf.next();   // Scheme
        swf.next('[name=haskell]'); // Haskell
    </script>
</html>

2-2 比较两颗设想DOM树的出入及差别的地点实行dom操作
奥门新浦京官方网站 2
地点的div只会和同生机勃勃层级的div相比,第二层级的只会和第二层级的对照,那样的算法的复杂度能够到达O(nState of Qatar.
只是在事实上代码中,会对新旧两颗树进行三个纵深优先的遍历,因而种种节点都会有贰个标记。如下图所示:
奥门新浦京官方网站 3
在遍历的过程中,每一次遍历到贰个节点就把该节点和新的树举行自己检查自纠,假若有异样的话就记下到叁个指标里面。

过滤(filter)和映射(map)

和函数式编制程序的map、filter相像,jQuery对象也可能有周边的办法。

filter(卡塔尔国方法能够过滤掉不合乎选拔器条件的节点。盛传选取器或许或许传播三个函数,要特别注意函数内部的this被绑定为DOM对象,不是jQuery对象

map(卡塔尔国方法把三个jQuery对象蕴含的多少DOM节点转变为其余对象。

八个jQuery对象如若带有了趋之若鹜一个DOM节点,first(卡塔尔国、last(卡塔尔和slice(State of Qatar方法能够重临一个新的jQuery对象,把没有必要的DOM节点去掉。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <ul class="lang">
        <li class="js dy">JavaScript</li>
        <li class="dy">Python</li>
        <li id="swift">Swift</li>
        <li class="dy">Scheme</li>
        <li name="haskell">Haskell</li>
    </ul>
</div>
</body>
    <script>
        var langs = $('ul.lang li');
        var dys = langs.filter('.dy');  // JavaScript, Python, Scheme

        var js = langs.filter(function () {
            return this.innerHTML.startsWith('J');
        });
        alert(js.get(0).innerHTML); // JavaScript

        // 数组map之后还是数组
        var arr = langs.map(function () {
            return this.innerHTML;
        }).get();   // 要调用get()才能返回数组
        alert(arr);  // string数组:JavaScript,Python,Swift,Scheme,Haskell


        var js = langs.first(); // JavaScript,相当于$('ul.lang li:first-child')
        var haskell = langs.last(); // Haskell, 相当于$('ul.lang li:last-child')
        var sub = langs.slice(2, 4); // Swift, Scheme, 参数和数组的slice()方法一致
    </script>
</html>

这几天大家来看下小编的目录下
有啥文件;然后分别对每种文件代码实行解读,看看做了怎么样职业,旧的捏造dom和新的伪造dom是什么样相比较的,且是怎么着翻新页面的如下目录:
目录构造如下:

操作DOM

后边提升部分浏览器有innerText,有的不帮忙innerContent,今后有了jQuery,全体用到同生龙活虎的操作API。

vdom  ---- 工程名
|   | ---- index.html  html页面
|   | ---- element.js  实例化元素组成json数据 且 提供render方法 渲染页面
|   | ---- util.js     提供一些公用的方法
|   | ---- diff.js     比较新旧节点数据 如果有差异保存到一个对象里面去
|   | ---- patch.js    对当前差异的节点数据 进行DOM操作
|   | ---- index.js    页面代码初始化调用

修改Text和HTML

jQuery的API设计丰裕抢眼,无参数调用text(卡塔尔(قطر‎和html(卡塔尔国方法分别获得节点的文书和原始HTML文本。传入参数就改成设置文本和HTML文本。

var book = $('#test-ul li[name=book]');
alert(book.text());  // Java & JavaScript
alert(book.html());  // Java & JavaScript

book.text('Hello & Tim');
book.html('Hello & Tim');

八个jQuery对象可以包涵0个或随便个DOM对象,它的艺术实际上会效用在相应的种种DOM节点上

jQuery的另二个独特之处正是,就算选取器未有回去任何DOM节点,调用jQuery对象的秘诀照旧不会报错!!!省去了汪洋的论断!!!

率先是 index.js文件 页面渲染完成后 产生如下html布局 

修改CSS

jQuery对象有“批量操作”的性状使得订正css十一分利于。用法:css('name', 'value')

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <ul id="test-css">
        <li class="lang dy">JavaScript</li>
        <li class="lang">Java</li>
        <li class="lang dy">Python</li>
        <li class="lang">Swift</li>
        <li class="lang dy">Scheme</li>
    </ul>
</div>
</body>
    <script>
        // 高亮显示动态语言
        $('#test-css li.dy>span').css('background-color', '#ffd351').css('color', 'red');
    </script>
</html>

上边直接使用了链式调用,因jQuery对象的具备办法都回来一个jQuery对象(只怕是新的也恐怕是自己)。

jQuery对象的css(卡塔尔(قطر‎方法帮忙下边包车型客车写法。

var div = $('#test-div');
div.css('color'); // 获取CSS属性
div.css('color', '#336699'); // 设置CSS属性
div.css('color', ''); // 清除CSS属性,同样返回jQuery对象
alert(div.css('color'));    // 清除后返回undefined

为了和JavaScript保持后生可畏致,CSS属性能够用’background-color’和’backgroundColor’三种格式。

css(卡塔尔方法将成效于DOM节点的style属性,具有最高优先级。尽管要修正class属性,能够用jQuery提供的下列方法.

var div = $('#test-div');
div.hasClass('highlight'); // false, class是否包含highlight
div.addClass('highlight'); // 添加highlight这个class
div.removeClass('highlight'); // 删除highlight这个class
<div id="container">
  <h1 style="color: red;">simple virtal dom</h1>
  <p>the count is :1</p>
  <ul>
    <li>Item #0</li>
  </ul>
</div>

来得和隐敝DOM

要藏匿叁个DOM,使用css(卡塔尔(قطر‎设置CSS的display属性为none。掩盖DOM节点并未有改换DOM树的构造,它只影响DOM节点的显得。

要显得那些DOM需求先过来原有的display属性,因而需要先记下来本来的display属性到底是block依旧inline照旧其他值。

有力的jQuery直接提供show(State of Qatar和hide(卡塔尔国方法。大家绝不关怀它是什么保存从前的display属性的。

设若发生转移后,形成如下构造 

获取DOM信息

利用jQuery的秘诀而不供给针对区别浏览器编写特定代码。

alert($(window).width());
alert($(window).height());

alert($(document).width());
alert($(document).height());

var div = $('#text-div');
alert(div.width()); 
alert(div.height());
div.width(400);         // 设置CSS属性 width: 400px,是否生效要看CSS是否有效
div.height('300px');    // 设置CSS属性 height: 200px,是否生效要看CSS是否有效

attr(State of Qatar和removeAttr(State of Qatar方法用于操作DOM节点的品质。

prop(卡塔尔方法和attr(卡塔尔(قطر‎相仿,不过HTML5明显有后生可畏种属性在DOM节点中得以未有值,唯有现身与不现身三种。

<input id="test-radio" type="radio" name="test" checked value="1">等价于<input id="test-radio" type="radio" name="test" checked="checked" value="1">

相通选拔第豆蔻年华种写法,适用于radio和checkbox,别的,<select>里面的<option>使用selected。

attr(卡塔尔和prop(State of Qatar对于属性checked管理的重临值分歧。attr(卡塔尔国重回checked或许undefined,prop重返布尔。由此prop看起来越来越好。

实则,单选框、复选框和<select>,管理时最佳利用is(‘checked’State of Qatar可能is(‘selected’State of Qatar。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div id="test-div" style="width: 300px; height: 200px">Hello</div>
    <input id="test-radio" type="radio" name="test" checked value="1">哈哈
</div>
</body>
    <script>
        var div = $('#test-div');
        alert(div.attr('id'));      // test-div
        alert(div.attr('name'));    // undefined
        div.attr('name', 'div1'); 
        div.removeAttr('name'); 
        alert(div.attr('name'));    // undefined

        var radio = $('#test-radio');
        alert(radio.attr('checked'));   // checked,没有就返回undefined
        alert(radio.prop('checked'));   // true,没有就返回false

        alert(radio.is(':checked'));    // true,没有就返回false
    </script>
</html>
<div id="container">
  <h1 style="color: blue;">simple virtal dom</h1>
  <p>the count is :2</p>
  <ul>
    <li>Item #0</li>
    <li>Item #1</li>
  </ul>
</div>

操作表单

对此表单成分,使用val(卡塔尔方法获得和设置相应的value属性。等价于前边的attr(卡塔尔国和prop(卡塔尔。

能够看见 新旧节点页面数据的修正,h1标签从属性 颜色从乙卯革命
变为天青,p标签的文件发生更动,ul新扩充了生机勃勃项因素li。
大旨的原理是:先渲染出页面数据出来,生成第一个模板页面,然后使用电磁打点计时器会转移一个新的页面数据出来,对新旧两颗树进行一个纵深优先的遍历,因而各种节点都会有贰个符号。
然后调用diff方法比较对象新旧节点遍历举行相比,寻找两岸的例外的地点存入到多个目的里面去,最终通过patch.js找寻目的区别之处,分别实行dom操作。

修改DOM结构

index.js代码如下:

添加DOM

除了前边的直接的html(),还足以采取append(卡塔尔(قطر‎、prepend(卡塔尔、after(卡塔尔和before(State of Qatar。

除了收受字符串,append(State of Qatar还足以流传原始的DOM对象,jQuery对象和函数对象。

盛传函数时,要求回到五个字符串、DOM对象或然jQuery对象。

因为jQuery的append(卡塔尔国可能效果于大器晚成组DOM节点,独有传入函数工夫针对每一种DOM生成不同的子节点,即依据index参数。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div id="test-div">
        <ul>
            <li id="js">JavaScript</li>
            <li>Python</li>
            <li>Swift</li>
        </ul>
    </div>
</div>
</body>
    <script>
        // 字符串形式
        var ul = $('#test-div>ul');
        ul.append('<li>Haskell</li>');

        // 添加DOM对象
        var ps = document.createElement('li');
        ps.innerHTML = 'Pascal';
        ul.append(ps);

        // 添加jQuery对象
        ul.append($('#js'));    // 已经存在的则移动!不新增

        ul.append(function (index, html) {  // 这里的index是ul的!
            return '<li>Language - ' + index + '</li>';
        });
    </script>
</html>

append(State of Qatar把DOM增添到终极,prepend(卡塔尔则把DOM加多到最前。

当心,要是要增添的DOM节点已经存在于HTML文书档案中,它会率先从文书档案移除,然后再增添,也正是说,用append(卡塔尔(قطر‎,你可以活动叁个DOM节点(即移动到最后,同理,prepend(卡塔尔移动到最前。)

倘使要把新节点插入到钦命地点,用after(State of Qatar或before(State of Qatar方法。

var el = require('./element');
var diff = require('./diff');
var patch = require('./patch');

var count = 0;
function renderTree() {
  count++;
  var items = [];
  var color = (count % 2 === 0) ? 'blue' : 'red';
  for (var i = 0; i < count; i++) {
    items.push(el('li', ['Item #' + i]));
  }
  return el('div', {'id': 'container'}, [
    el('h1', {style: 'color: ' + color}, ['simple virtal dom']),
    el('p', ['the count is :' + count]),
    el('ul', items)
  ]);
}

var tree = renderTree()
var root = tree.render()
document.body.appendChild(root)
setInterval(function () {
  var newTree = renderTree()
  var patches = diff(tree, newTree)
  console.log(patches)
  patch(root, patches)
  tree = newTree
}, 1000);

除去节点

调用remove(卡塔尔国。借使带有多少个DOM节点,则批量去除了。

执行 var tree = renderTree()方法后,会调用element.js,

事件

jQuery屏蔽了差异浏览器对事件管理的代码差距,便于写出宽容性好的代码。

jQuery对象的on方式用来绑定八个事件,传入事件名称和呼应的处理函数就可以。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <a id="test-link" href="#">点我</a>
</div>
</body>
    <script>
        var a = $('#test-link');
        a.on('click', function () {
            alert('Hello!');
        });
    </script>
</html>

上面包车型大巴更简单更广阔的写法。二种写法完全平等,猛烈提出使用第两种。

var a = $('#test-link');
a.click(function () {
    alert('Hello!');
});
  1. 各类遍历子节点(从内到向外调拨运输用State of Qatar依次为 li, h1, p, ul,
    li和h1和p有三个文本子节点,由此遍历实现后,count就十分1,
    可是遍历ul的时候,因为有五个子节点li,由此 count += 1;
    所以调用完结后,ul的count等于2.
    据此会对各种element属性增添count属性。对于最外层的container容器就是对每种子节点的次第扩张,h1子节点默以为1,循环完结后
    +1;由此变为2, p节点默感到1,循环达成后
    +1,因而也变为2,ul为2,循环完结后
    +1,因而变为3,因而container节点的count=2+2+3 = 7;

jQuery能够绑定的平地风波

  • 鼠标事件

    • click: 鼠标单击时接触
    • dblclick:鼠标双击时接触
    • mouseenter:鼠标步入时接触
    • mouseleave:鼠标移出时接触
    • mousemove:鼠标在DOM内部移动时接触
    • hover:鼠标步向和抽离时触发七个函数,也便是mouseenter加上mouseleave。
  • 键盘事件
    键盘事件仅功用在这里时此刻火爆的DOM上,常常是<input><textarea>

    • keydown:键盘按下时接触
    • keyup:键盘放手时触发
    • keypress:按一次键后接触。
  • 别的交事务件

    • focus:当DOM得到主旨时接触
    • blur:当DOM失去大旨时接触
    • change:当<input><select><textarea>的内容改造时接触
    • submit:当<form>交给时接触
    • ready:当页面被载入并且DOM树落成早先化后触发

注意,ready仅成效于document对象。ready事件在DOM完毕起头化后触发,且只触发三回,特别符合编写其余开首化代码

上面包车型大巴代码未有生效,因为JS推行时,<form>尚未加载到浏览器中。那个时候的jQuery对象是空数组。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
    <script>
        var form = $('#test-form');
        alert(form.length);     // 0,form还未加载到浏览器中
        form.submit(function () {
            alert('submit!');
        });
    </script>
</head>
<body>
    <form id="test-form"></form>
</div>
</body>
</html>

于是上述的起头化代码应该放在document的ready事件管理函数中,以保证DOM完全起先化后再进行。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
    <script>
        $(document).ready(function () {
            var form = $('#test-form');
            alert(form.length);     // 1
            form.submit(function () {
                alert('submit!');   // 没有提交按钮,这里暂时不能执行...
            });
        });
    </script>
</head>
<body>
    <form id="test-form"></form>
</div>
</body>
</html>

ready事件选拔特别普遍,上边的代码可用以简化成上边!

$(function () {
    var form = $('#test-form');
    alert(form.length);     // 1
    form.submit(function () {
        alert('submit!');   // 没有提交按钮,这里暂时不能执行...
    });
});

举凡肖似于$(function () {...})的都是document对象的ready事件管理函数。

还要,完全可以屡次绑定事件处理函数,它们会挨个实行!

$(function () {
    alert('init - 1');
})
$(function () {
    alert('init - 2');
})
$(function () {
    alert('init - 3');
})

element.js部分代码如下:

事件参数

持有事件都会传来Event对象作为参数。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
    <script>
        $(function () {
            $('#test-div').mousemove(function (e) {
                $('#test-span').text('pageX = ' + e.pageX + ', pageY = ' + e.pageY);
            });
        })
    </script>
</head>
<body>
    <div id="test-div" style="width: 200px; height: 100px; border: red solid 1px"></div>

</div>
</body>
</html>
function Element(tagName, props, children) {
  if (!(this instanceof Element)) {
    // 判断子节点 children 是否为 undefined
    if (!utils.isArray(children) && children !== null) {
      children = utils.slice(arguments, 2).filter(utils.truthy);
    }
    return new Element(tagName, props, children);
  }
  // 如果没有属性的话,第二个参数是一个数组,说明第二个参数传的是子节点
  if (utils.isArray(props)) {
    children = props;
    props = {};
  }
  this.tagName = tagName;
  this.props = props || {};
  this.children = children || [];
  // 保存key键 如果有属性 保存key,否则返回undefined
  this.key = props ? props.key : void 0;
  var count = 0;

  utils.each(this.children, function(child, i) {
    // 如果是元素的实列的话
    if (child instanceof Element) {
      count += child.count;
    } else {
      // 如果是文本节点的话,直接赋值
      children[i] = '' + child;
    }
    count++;
  });
  this.count = count;
}

注销绑定

运用相符于off(‘click’, function卡塔尔扫除绑定的事件管理函数。

function hello() {
    alert('Hello!');
}
$('#test-link').click(hello);
// 10s后解除绑定
setTimeout(function () {
    $('#test-link').off('click', hello);
}, 10000);

只顾上边的消亡无效,因为五个无名函数不是同叁个函数,即使代码相仿。

$('#test-link').click(function () {
    alert('Hello!');
});

$('#test-link').off('click', function () {
    alert('Hello!');
});

使用off('click')能够二回性移除已绑定到click事件的具备管理函数。

同理,无参数调用off()二回性移除已绑定的富有事件类型的管理函数。

oldTree数据最终成为如下:

事件触发条件

急需介怀的是,事件的触发总是由客户操作引发的。举例监察和控制文本框的内容改变,使用JS去修改文本框内容将不会触发事件。

如若必必要通过代码触发,供给调用须求无参数调用jQuery对象的change(State of Qatar。x.change()等价于x.trigger('change'),即trigger()的简写。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
    <script>
        $(function () {
            var input = $('#test-text');
            input.change(function () {
                alert('text changed!');
            });

            input.val('change it!');
            // 下面两句等价
            input.change();
            // input.trigger('change');
        });
    </script>
</head>
<body>
    <input type="text" id="test-text">
</div>
</body>
</html>
var oldTree = {
  tagName: 'div',
  key: undefined,
  count: 7,
  props: {id: 'container'},
  children: [
    {
      tagName: 'h1',
      key: undefined
      count: 1
      props: {style: 'colod: red'},
      children: ['simple virtal dom']
    },
    {
      tagName: 'p',
      key: undefined
      count: 1
      props: {},
      children: ['the count is :1']
    },
    {
      tagName: 'ul',
      key: undefined
      count: 2
      props: {},
      children: [
        {
          tagName: 'li',
          key: undefined,
          count: 1,
          props: {},
          children: ['Item #0']
        }
      ]
    },
  ]
};

浏览器安全限制

浏览器中,某个代码只好由客户触发,比如window.open()

<script>
    $(function () {
        window.open('1.txt');
    });
</script>

浏览器会拦截上面的代码。

上边包车型大巴代码里面包车型客车500ms后自行施行的以至也实施了?!

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <input type="button" id="btn1" value="按钮1">
    <input type="button" id="btn2" value="按钮2">
</div>
</body>
    <script>
        function pop() {
            ('call');
            window.open('/');
        }

        $('#btn1').click(function () {
            pop();    // 放在用户的点击事件里面,允许
        });

        $('#btn2').click(function () {
            // 并未立即执行pop,延迟由JS代码执行
            setTimeout(pop, 100);   // 貌似也自动打开了啊
        });
    </script>
</html>

电磁打点计时器 实行 var newTree =
renderTree(卡塔尔后,调用方法步骤依然和第一步同样:
2. 依次遍历子节点(从内到向外调拨运输用卡塔尔(قطر‎依次为 li, h1, p, ul,
li和h1和p有一个文本子节点,因而遍历完毕后,count就等于1,因为有2个子成分li,count都为1,因而ul每回遍历依次在本来的底蕴上加1,由此遍历达成第4个li时候,ul中的count为2,当遍历完成第贰个li的时候,ul的count就为4了。由此ul中的count为4.
对此最外层的container容器正是对各类子成分依次增加。
所以 container节点的count = 2 + 2 + 5 = 9;

动画

JS完毕动漫的规律:每间距生龙活虎段时间去改进DOM成分的样式。

看起来大致,手写JS完结动漫是很复杂的,使用jQuery封装的动漫,只需大器晚成行代码。

newTree数据最后成为如下数据:

show/hide

无参数调用show(卡塔尔国和hide(State of Qatar将显得和遮盖成分,toggle(State of Qatar将依据当前事态调节展现如故掩盖,动漫形式都以从左上角逐渐开展或减弱的。传入二个时间参数(单位:ms),正是卡通片,即在指如时期内显示或潜伏。也足以是slowfast等字符串。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div id="div1" style="width: 300px; height: 200px; background-color: blue"></div>
</div>
</body>
    <script>
        $('#div1').hide(3000);
        $('#div1').show('slow');
        $('#div1').toggle('fast');
    </script>
</html>
var newTree = {
  tagName: 'div',
  key: undefined,
  count: 9,
  props: {id: 'container'},
  children: [
    {
      tagName: 'h1',
      key: undefined
      count: 1
      props: {style: 'colod: red'},
      children: ['simple virtal dom']
    },
    {
      tagName: 'p',
      key: undefined
      count: 1
      props: {},
      children: ['the count is :1']
    },
    {
      tagName: 'ul',
      key: undefined
      count: 4
      props: {},
      children: [
        {
          tagName: 'li',
          key: undefined,
          count: 1,
          props: {},
          children: ['Item #0']
        },
        {
          tagName: 'li',
          key: undefined,
          count: 1,
          props: {},
          children: ['Item #1']
        }
      ]
    },
  ]
}

slideUp / slideDown

卡通情势是在笔直方向渐渐张开或收缩。slideToggle(卡塔尔国同toggle(State of Qatar。参数同show(卡塔尔(قطر‎/hide(卡塔尔/toggle(卡塔尔(قطر‎。

var patches = diff(oldTree, newTree);

fadeIn / fadeOut

动漫效果是淡入淡出。通过不断设置DOM成分的opacity质量来兑现,fadeToggle(State of Qatar同理。

调用diff方法能够相比较新旧两棵树节点的多少,把两颗树的不及节点寻找来。(注意,查看diff相比数据的艺术,找到分歧的节点,能够查阅那篇小说diff算法卡塔尔(قطر‎如下调用代码:

自定义动画

使用animate(State of Qatar达成自由动漫效果。传入的参数就是DOM成分最后的CSS状态和时间,jQuery在岁月段内连发调度CSS直到到达我们设定的值。

animate(卡塔尔国还足以再传播一个函数,当动漫停止时,该函数将被调用。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div id="div1" style="width: 300px; height: 200px; background-color: blue"></div>
</div>
</body>
    <script>
        $('#div1').animate({
            opacity: 0.25,
            width: '150px',
            height: '100px'
        }, 3000, function () {
            alert('动画结束!');
            // 注意,this指DOM对象,需要包装成jQuery对象
            $(this).css('opacity', '1.0').css('width', '300px').css('height', '200px');
        });
    </script>
</html>
function diff (oldTree, newTree) {
  var index = 0;
  var patches = {};
  deepWalk(oldTree, newTree, index, patches);
  return patches;
}

串行动漫

jQuery的动画效果仍为能够串行实行,通过delay(卡塔尔国方法还足以兑现暂停,进而能够达成更复杂的卡通效果,而代码却一定轻巧。

因为动漫须要实行豆蔻年华段时间,所以jQuery必得不断再次回到新的Promise对象本事继续实行操作。容易地把动漫封装在函数中是远远不足的。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div id="div1" style="width: 300px; height: 200px; background-color: blue; display: none"></div>
</div>
</body>
    <script>
        $('#div1').slideDown(2000)  // 为了看到效果,先display: none
            .delay(1000)
            .animate({
                width: '150px',
                height: '100px'
            }, 2000)
            .delay(1000)
            .animate({
                width: '300px',
                height: '200px'
            }, 2000);
    </script>
</html>

实行deepWalk如下代码:

缘何有个别动漫未有效果

那是因为jQuery动漫的准则是稳步改变CSS的值,如height从100px逐渐变为0。可是不菲不是block性质的DOM成分,对它们设置height根本就不起成效,所以动漫也就不曾效果。

别的,jQuery也未有落到实处对background-color的卡通片效果,用animate()设置background-color也从不效果与利益。这种气象下能够动用CSS3的transition落成动漫效果。

function deepWalk(oldNode, newNode, index, patches) {
  var currentPatch = [];
  // 节点被删除掉
  if (newNode === null) {
    // 真正的DOM节点时,将删除执行重新排序,所以不需要做任何事
  } else if(utils.isString(oldNode) && utils.isString(newNode)) {
    // 替换文本节点
    if (newNode !== oldNode) {
      currentPatch.push({type: patch.TEXT, content: newNode});
    }
  } else if(oldNode.tagName === newNode.tagName && oldNode.key === newNode.key) {
    // 相同的节点,但是新旧节点的属性不同的情况下 比较属性
    // diff props
    var propsPatches = diffProps(oldNode, newNode);
    if (propsPatches) {
      currentPatch.push({type: patch.PROPS, props: propsPatches});
    }
    // 不同的子节点 
    if (!isIgnoreChildren(newNode)) {
      diffChildren(
        oldNode.children,
        newNode.children,
        index,
        patches,
        currentPatch
      )
    }
  } else {
    // 不同的节点,那么新节点替换旧节点
    currentPatch.push({type: patch.REPLACE, node: newNode});
  }
  if (currentPatch.length) {
    patches[index] = currentPatch;
  }
}

AJAX

选择jQuery写AJAX,不唯有毫无思考浏览器的宽容性,并且代码十一分简短。

  1. 看清新节点是或不是为null,假设为null,表明节点被剔除掉。
    2.
    剖断新旧节点是还是不是为字符串,假若为字符串表达是文本节点,况兼新旧五个公文节点区别的话,存入数组里面去,如下代码:

ajax()

直接调用全局对象$的ajax(url, settings)。settings对象设置如下:

  • async:是不是异步实践AJAX诉求,默感觉true,纯属不要内定为false
  • method:发送的Method,缺省为’GET’,可钦定为’POST’、’PUT’等
  • contentType:发送POST央求的格式,私下认可值为’application/x-www-form-urlencoded;
    charset=UTF-8’,也能够钦点为’text/plain’、’application/json’
  • data:发送的数码,可以是字符串、数组或object。假使是GET央浼,data将被撤换来query附加到USportageL上,若是是POST乞求,依据contentType把data体系化成合适的格式
  • headers:发送的额外的HTTP头,必需是四个object
  • dataType:选择的数据格式,能够钦赐为’html’、’xml’、’json’、’text’等,缺省景色下基于响应的Content-Type预计。

回调函数管理回来的多少和失误时的响应后边是行使Promise对象(ES6引进),jQuery里面也是有个肖似的jqXHR目标,因而能够运用链式写法管理回调。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <textarea id="response-text" rows="5"></textarea>
    <button type="button" id="btn">获取</button>
</body>
<script>
    function ajaxLog(s) {
        var txt = $('#response-text');
        txt.val(txt.val() + 'n' + s);
    }

    $('#btn').click(function () {
        $('#response-text').val('');

        $.ajax('https://www.liaoxuefeng.com/api/categories', {  // 不能跨越,暂时会报错
            dataType: 'json'
        }).done(function (data) {
            ajaxLog('成功,收到数据:' + JSON.stringify(data));
        }).fail(function (xhr, status) {
            ajaxLog('失败:' + xhr.status + ',原因:' + status);
        }).always(function () {
            ajaxLog('请求完成:无论成功与否都会执行。');
        });
    });
</script>
</html>

对此常用的AJAX操作,jQuery提供了帮手的艺术。

   currentPatch.push({type: patch.TEXT, content: newNode});
   patch.TEXT 为 patch.js里面包车型大巴 TEXT = 3;content属性为新节点。

get()

处理GET请求。

var jqxhr = $.get('/path/to/resource', {
    name: 'Bob Lee',
    check: 1
});

其次个参数假若是object,jQuery自动把它成为query string下一场加到UTucsonL前面,实际的U奥迪Q5L是:/path/to/resource?name=Bob%20Lee&check=1。实际上我们无需关怀U奥德赛L是怎么编码的。

3.
只要新旧tagName相通的话,並且新旧节点的key相像的话,继续相比新旧节点的习性,如下代码:

post()

拍卖POST央求,和get(State of Qatar相仿,不过传入的第叁个参数暗中认可被系列化为application/x-www-form-urlencoded,即事实上的多少name=Bob%20Lee&check=1作为POST的body被发送。

var jqxhr = $.post('/path/to/resource', {
    name: 'Bob Lee',
    check: 1
});
var propsPatches = diffProps(oldNode, newNode);

getJSON()

是因为JSON用得更加的广阔,所以jQuery也提供了getJSON(卡塔尔(قطر‎方法来相当的慢通过GET获取三个JSON对象。

var jqxhr = $.getJSON('/path/to/resource', {
    name: 'Bob Lee',
    check: 1
}).done(function (data) {
    // 此时data已经是JSON对象了
});

diffProps方法的代码如下:

安然节制

jQuery的AJAX完全封装的是JS的AJAX操作,所以它的乌海范围和前边讲的用JavaScript写AJAX完全风流洒脱致。

要是急需动用JSONP,能够在ajax(State of Qatar中装置jsonp: 'callback',让jQuery完结JSONP跨域加载数据。

function diffProps(oldNode, newNode) {
      var count = 0;
      var oldProps = oldNode.props;
      var newProps = newNode.props;
      var key,
        value;
      var propsPatches = {};
      // 找出不同的属性值
      for (key in oldProps) {
        value = oldProps[key];
        if (newProps[key] !== value) {
          count++;
          propsPatches[key] = newProps[key];
        }
      }
      // 找出新增属性
      for (key in newProps) {
        value = newProps[key];
        if (!oldProps.hasOwnProperty(key)) {
          count++;
          propsPatches[key] = newProps[key];
        }
      }
      // 如果所有的属性都是相同的话
      if (count === 0) {
        return null;
      }
      return propsPatches;
   }

扩展

jQuery内置的法子永久不恐怕满意全部的需要。在jQuery的幼功上编写制定插件,能够免止双重代码,升高代码可维护性。

给jQuery对象绑定一个新方式是由此扩大$.fn对象达成的。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div id="test-highlight1">
        <p>什么是jQuery</p>
        <p>jQuery是目前最流行的JavaScript库。</p>
    </div>
</body>
<script>
    $.fn.highlight1 = function () {
        // 注意,此时的this是jQuery对象!
        this.css('backgroundColor', '#FFFCEB').css('color', '#D85030');
        return this;
    };

    $('#test-highlight1 span').highlight1();
</script>
</html>

在乎,函数内部的this在调用时被绑定为jQuery对象,所以函数内部代码能够健康调用全体jQuery对象的点子。

我们在自定义的highlight1(卡塔尔函数最终return this,是为了帮助链式调用,就好像jQuery相通。

自定义的highlight1(卡塔尔国还足以收起参数,让调用者钦赐高亮的颜色。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div id="test-highlight2">
        <p>什么是jQuery</p>
        <p>jQuery是目前最流行的JavaScript库。</p>
    </div>
</body>
<script>
    $.fn.highlight2 = function (options) {
        // 巧妙地使用短路运算检查输入
        var bgcolor = options && options.backgroundColor || '#fffceb';
        var color = options && options.color || '#D85030';
        this.css('backgroundColor', bgcolor).css('color', color);
        return this;
    };

    $('#test-highlight2 span').highlight2({
        backgroundColor: '#00a8e6',
        color: '#FFFFFF'
    });
</script>
</html>

这里,高超的采用短路运算,合作||和&&运算符,能够检查输入只怕设置暗中同意值

另大器晚成种形式是接受jQuery提供的有倾囊相助方法$.extend(target, obj1, obj2, ...),它把后边的三个obj对象的属性归拢到第叁个target对象中,蒙受同名属性,总是接收靠后的靶子的值,也正是越未来优先级越高。

由此我们传入私下认可值到背后的object对象如obj1里面,假设急需自定义参数,在再前边传出options。明显,那几个暗许值能够放在$.fn.highlight对象上面,表示归于它的默许值。有了那几个暗中同意值,客户也可以设置并改革成自个儿偏爱的默许值

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div id="test-highlight">
        <p>什么是jQuery</p>
        <p>jQuery是目前最流行的JavaScript库。</p>
    </div>
</body>
<script>
    $.fn.highlight = function (options) {
        // 合并默认值和用户设定值
        var opts = $.extend({}, $.fn.highlight.defaults, options);
        this.css('backgroundColor', opts.backgroundColor).css('color', opts.color);

        return this;
    };

    // 设置默认值
    $.fn.highlight.defaults = {
        backgroundColor: '#fff8de',
        color: '#d85030'
    };


    // 用户也可以修改成自己偏好的默认值!
    $.fn.highlight.defaults.color = '#fff';
    $.fn.highlight.defaults.backgroundColor = '#000';
    // 无参调用
    $('#test-highlight span').highlight();
</script>
</html>

diffProps代码深入剖析如下:

编纂二个jQuery插件的规范化

  1. $.fn绑定函数,完结插件的代码逻辑;
  2. 插件函数最终要return this;以支撑链式调用;
  3. 插件函数要有暗中认可值,绑定在$.fn.<pluginName>.defaults上;
  4. 顾客在调用时可传唱设定值以便覆盖私下认可值。
for (key in oldProps) {
   value = oldProps[key];
   if (newProps[key] !== value) {
      count++;
      propsPatches[key] = newProps[key];
   }
}

本着一定成分的扩张

jQuery对象的略略措施只可以功用在特定DOM元素上,举个例子submit()格局只可以针对form。大家可以依赖jQuery的filter()对特定DOM成分扩展方法。

例如说上边的必要:要给具备指向外链的超链接加上跳转提醒。

<html>
<head>
    <script src="jquery-2.2.4.js"></script>
</head>
<body>
    <div id="test-external">
        <p>百度:<a href="http://www.baidu.com" target="_blank">百度网址</a></p>
        <p>谷歌:<a href="https://www.google.com" target="_blank">谷歌</a></p>
        <p>本地:<a href="/hello.txt" target="_blank">hello.txt</a></p>
    </div>
</body>
<script>
    $.fn.external = function () {
        // 这里的this是jQuery对象
        // 返回each()返回结果,以支持链式调用!
        return this.filter('a').each(function () {
            // 这里的this是DOM元素本身!
            var a = $(this);
            var url = a.attr('href');
            if (url && url.indexOf('http://') === 0 || url.indexOf('https://') === 0) {
                // 不重置href的话,confirm对话框关闭后,链接依然照常打开
                a.attr('href', '#')
                  // 不移除target,confirm点击取消后,会在新窗口打开a.href的网址
                  .removeAttr('target')
                  .click(function () {
                    if (confirm('您确定要前往' + url + "?")) {
                        window.open(url);
                    }
                  });
            }
        });
    };

    $('#test-external a').external();
</script>
</html>

编纂的恢弘方法要力所能致扶植链式调用具有暗中同意值过滤特定成分,使得扩大方法看起来和jQuery本人的方法未有怎么差别。

如上代码是
推断旧节点的属性值是还是不是在新节点中找到,假使找不到的话,count++;
把新节点的属性值赋值给 propsPatches 存款和储蓄起来。

for (key in newProps) {
   value = newProps[key];
   if (!oldProps.hasOwnProperty(key)) {
      count++;
      propsPatches[key] = newProps[key];
   }
}

如上代码是
决断新节点的习性是不是能在旧节点中找到,如若找不到的话,count++;
把新节点的属性值赋值给 propsPatches 存款和储蓄起来。

if (count === 0) {
   return null;
}
return propsPatches;

最后只要count
等于0的话,表达具备属性都以相同的话,所以不需求做别的变动。不然的话,重临新扩大的性质。

若是有 propsPatches 的话,试行如下代码:

if (propsPatches) {
   currentPatch.push({type: patch.PROPS, props: propsPatches});
}

故而currentPatch数组里面也可能有对应的翻新的特性,props正是亟需创新的属性对象。

继续代码:

// 不同的子节点 
if (!isIgnoreChildren(newNode)) {
   diffChildren(
     oldNode.children,
     newNode.children,
     index,
     patches,
     currentPatch
   )
}
function isIgnoreChildren(node) {
  return (node.props && node.props.hasOwnProperty('ignore'));
}

如上代码推断子节点是还是不是大器晚成致,diffChildren代码如下:

function diffChildren(oldChildren, newChildren, index, patches, currentPatch) {
    var diffs = listDiff(oldChildren, newChildren, 'key');
    newChildren = diffs.children;

    if (diffs.moves.length) {
      var recorderPatch = {type: patch.REORDER, moves: diffs.moves};
      currentPatch.push(recorderPatch);
    }

    var leftNode = null;
    var currentNodeIndex = index;
    utils.each(oldChildren, function(child, i) {
      var newChild = newChildren[i];
      currentNodeIndex = (leftNode && leftNode.count) ? currentNodeIndex + leftNode.count + 1 : currentNodeIndex + 1;
      // 递归
      deepWalk(child, newChild, currentNodeIndex, patches);
      leftNode = child;
    });
  }

如上代码:var diffs = listDiff(oldChildren, newChildren, ‘key’卡塔尔;
新旧节点根据key来相比较,近日key为undefined,所以diffs 为如下:

diffs = {
    moves: [],
    children: [
      {
        tagName: 'h1',
        key: undefined
        count: 1
        props: {style: 'colod: blue'},
        children: ['simple virtal dom']
      },
      {
        tagName: 'p',
        key: undefined
        count: 1
        props: {},
        children: ['the count is :2']
      },
      {
        tagName: 'ul',
        key: undefined
        count: 4
        props: {},
        children: [
          {
            tagName: 'li',
            key: undefined,
            count: 1,
            props: {},
            children: ['Item #0']
          },
          {
            tagName: 'li',
            key: undefined,
            count: 1,
            props: {},
            children: ['Item #1']
          }
        ]
      }
    ]
  };

newChildren = diffs.children;
oldChildren数据如下:

oldChildren = [
    {
      tagName: 'h1',
      key: undefined
      count: 1
      props: {style: 'colod: red'},
      children: ['simple virtal dom']
    },
    {
      tagName: 'p',
      key: undefined
      count: 1
      props: {},
      children: ['the count is :1']
    },
    {
      tagName: 'ul',
      key: undefined
      count: 2
      props: {},
      children: [
        {
          tagName: 'li',
          key: undefined,
          count: 1,
          props: {},
          children: ['Item #0']
        }
      ]
    }
  ];

接着正是遍历 oldChildren, 第二次遍历时 leftNode 为null,由此currentNodeIndex = currentNodeIndex + 1 = 0 + 1 = 1;
不是第三次遍历,那么leftNode都为上三遍遍历的子节点,由此不是首先次遍历的话,那么
currentNodeIndex = currentNodeIndex + leftNode.count + 1; 
然后递归调用 deepWalk(child, newChild, currentNodeIndex, patches卡塔尔(قطر‎;
方法,接着把child赋值给leftNode,leftNode = child;

由此直接递归遍历,最后把不等同的节点 会存款和储蓄到 current帕特ch
数组内。最后实践

if (currentPatch.length) {
   patches[index] = currentPatch;
}

把相应的currentPatch 存款和储蓄到 patches对象之中的应和项,最后就赶回
patches对象。

  1. 回来到index.js
    代码内,把两颗分歧等的树节点的领到出来后,须要调用patch.js方法传进;把不均等的节点应用到确实的DOM上.
    分化样的节点 patches数据如下:

    patches = {

     1: [{type: 2, props: {style: 'color: blue'}}],
     4: [{type: 3, content: 'the count is :2'}],
     5: [
           { 
               type: 1, 
               moves: [
                 { index: 1, 
                    item: {
                       tagName: 'li',
                       props: {},
                       count: 1,
                       key: undefined,
                       children: ['Item #1']
                     }
                 }
               ]
            }
         ]
     }
    

如下代码调用:
patch(root, patches);
实行patch方法,代码如下:

function patch(node, patches) {
  var walker = {index: 0};
  deepWalk(node, walker, patches);
}

deepWalk 代码如下:

function deepWalk(node, walker, patches) {
   var currentPatches = patches[walker.index];
      // node.childNodes 返回指定元素的子元素集合,包括HTML节点,所有属性,文本节点。
   var len = node.childNodes ? node.childNodes.length : 0;
   for (var i = 0; i < len; i++) {
      var child = node.childNodes[i];
      walker.index++;
      // 深度复制 递归遍历
      deepWalk(child, walker, patches);
   }
   if (currentPatches) {
      applyPatches(node, currentPatches);
   }
}

1.
第三次调用patch的章程,root正是container的节点,由此调用deepWalk方法,因而var currentPatches = patches[0] = undefined,
var len = node.childNodes ? node.childNodes.length : 0; 因而 len = 3;
很醒目该子节点的长短为3,因为子节点有 h1, p, 和ul元素;

  1. 接下来举办for循环,获取该父节点的子节点,因而首先身形节点为 h1
    因素,walker.index++; 因而walker.index = 1; 再张开递归 deepWalk(child,
    walker, patches卡塔尔(قطر‎; 那个时候子节点为h1, walker.index为1, 因而拿到currentPatches = patches[1]; 获取值,再赢得 h1的子节点的尺寸,len = 1;
    然后再for循环,获取child为文本节点,那时候 walker.index++;
    所以那时walker.index 为2, 在调用deepwalk方法递归,由此再持续获得currentPatches = patches[2]; 值为undefined,再拿到len = 0;
    因为文本节点么有子节点,所以for循环跳出,所以推断currentPatches是还是不是有值,因为此时currentPatches 为undefined,所以递归截至,再再次回到到
    h1成分上来,所以currentPatches = patches[1]; 所以有值,所以调用
    applyPatches(卡塔尔方法来更新dom成分。
  1. 大浪涛沙循环 i, 那时i = 1; 获取子节点 child =
    p成分,walker.index++,那时walker.index = 3, 继续调用 deepWalk方法,获取
    var currentPatches = patches[walker.index] = patches[3]的值,var len
    = 1; 因为p成分下有叁个子节点(文本节点State of Qatar,再进for循环,这个时候walker.index++; 因而walker.index = 4; child这个时候为文本节点,在调用
    deepwalk方法的时候,再一次获得得var currentPatches = patches[walker.index] =
    patches[4]; 再实践len 代码的时候 len = 0;由此跳出for循环,判别current帕特ches是还是不是有值,有值的话,更新对应的DOM元素。
  1. 接轨循环i = 2; 获取子节点 child = ul成分,walker.index++;
    当时walker.index = 5; 在调用deepWalk方法递归,因而再赢得 var
    currentPatches = patches[walker.index] = patches[5]; 然后len = 1,
    因为ul元素下有四个li成分,在三番若干次for循环遍历,获取子节点li,这时walker.index++;
    walker.index = 6; 再递归调用deepwalk方法,再拿走var currentPatches =
    patches[walker.index] = patches[6]; len = 1;
    因为li的要素下有叁个文书节点,再开展for循环,这时child为文本节点,walker.index++;那个时候walker.index
    = 7; 再实践 deepwalk方法,再获得 var current帕特ches =
    patches[walker.index] = patches[7]; 当时 len =
    0了,由此跳出for循环,判别当前的currentPatches是不是有值,没有,就跳出,然后再重临ul成分,获取该自身li的时候,walker.index
    等于5,由此var currentPatches = patches[walker.index] = patches[5];
    然后决断 currentPatches是或不是有值,有值就开展更新DOM成分。

最后便是 apply帕特ches 方法立异dom成分了,如下代码:

function applyPatches(node, currentPatches) {
  utils.each(currentPatches, function(currentPatch) {
    switch (currentPatch.type) {
      case REPLACE:
        var newNode = (typeof currentPatch.node === 'string') 
          ? document.createTextNode(currentPatch.node)
          : currentPatch.node.render();
        node.parentNode.replaceChild(newNode, node);
        break;
      case REORDER:
        reorderChildren(node, currentPatch.moves);
        break;
      case PROPS: 
        setProps(node, currentPatch.props);
        break;
      case TEXT:
        if(node.textContent) {
          node.textContent = currentPatch.content;
        } else {
          // ie bug
          node.nodeValue = currentPatch.content;
        }
        break;
      default:
        throw new Error('Unknow patch type' + currentPatch.type);
    }
  });
}

推断项目,替换对应的性质和节点。
最终就是对子节点实行排序的操作,代码如下:

// 对子节点进行排序
function reorderChildren(node, moves) {
  var staticNodeList = utils.toArray(node.childNodes);
  var maps = {};
  utils.each(staticNodeList, function(node) {
    // 如果是元素节点
    if (node.nodeType === 1) {
      var key = node.getAttribute('key');
      if (key) {
        maps[key] = node;
      }
    }
  })
  utils.each(moves, function(move) {
    var index = move.index;
    if (move.type === 0) {
      // remove Item
      if (staticNodeList[index] === node.childNodes[index]) {
        node.removeChild(node.childNodes[index]);
      }
      staticNodeList.splice(index, 1);
    } else if(move.type === 1) {
      // insert item
      var insertNode = maps[move.item.key] 
        ? maps[move.item.key].cloneNode(true)
        : (typeof move.item === 'object') ? move.item.render() : document.createTextNode(move.item);
      staticNodeList.splice(index, 0, insertNode);
      node.insertBefore(insertNode, node.childNodes[index] || null);
    }
  });
}

遍历moves,决断moves.type
是等于0依然至极1,等于0的话是删除操作,等于1的话是骤增操作。举例以后moves值形成如下:

moves = {
  index: 1,
  type: 1,
  item: {
    tagName: 'li',
    key: undefined,
    props: {},
    count: 1,
    children: ['#Item 1']
  }
};

node节点 正是 ‘ul’成分,var staticNodeList =
utils.toArray(node.childNodesState of Qatar;
把ul的旧子节点li转成Array格局,由于未有质量key,所以一向跳到上面遍历代码来,遍历moves,获取某风度翩翩项的索引index,推断move.type
等于0 依然十二分1,
前段时间十三分1,是新添生机勃勃项,不过从未key,由此调用move.item.render(卡塔尔(قطر‎;
渲染完后,对staticNodeList数组里面包车型地铁旧节点的li项从第二项开头插入节点li,然后执行node.insertBefore(insertNode,
node.childNodes[index] || null卡塔尔(قطر‎;
node便是ul父节点,insertNode节点插入到
node.childNodes[1]的前方。因而把在其次项的先头插入第生龙活虎项。
查看github上源码

发表评论

电子邮件地址不会被公开。 必填项已用*标注