第八章:节点模块

DOM节点操作占我们前端工作很大一部分,其节点的操作又占50%以上。由于选择器引擎的出现,让繁琐的元素选择简单化,并且一下子返回一大堆元素,这个情景时刻暗示着我们操作元素就像css为元素添加样式那样,一操作就操作一组元素。

一些大胆的API设计被提出来。当然我们认为时髦新颖的设计其实都是很久以前被忽略的设计或者其它领域的设计。例如:集化操作,这是数据库层里边的ROM就有的。链式操作,javascript对象基于原型的构造为它大开方便之门。信息密集的DSL,有rails这座高峰让大家崇拜。jQuery集众所长,让节点操作简单到极致。对于jQuery,每个方法重载的厉害。作为一个流行的产品,必须模仿着众。本章以mass框架的节点操作模块为主,其原因是mass分层做的比较好,node模块相当于jQuery2.0中的manipulation模块与traversing模块,node_fix则是兼容ie6-8的部分。(这里体现了AMD规范设计的优势)

我们来看DOM的操作包括哪些,CRUD(数据库所说的),C就是创建,在集化操作里,innerHTML可以满足我们一下创建多个节点的需求。

R,就是读取查找,如果解释查找的话,选择器引擎已经为我们做了,如果是读取,那么我们还可以将innerHTML,innerText,outerHTML这些属于元素内容的东西划归它处理

U,就是更新,innerHTML,innerText,outerHTML,这就出现一个问题,需要两个API来处理,还是合成一个。jQuery的RU结合的方式建议只用框架底层的API,到UI层面还是分开。底层这样做,可以保证API的数量少,使用门槛降低,高层的API,则数量非常庞杂,窗口、面板、跑马灯、拖放等都有一大堆方法,他们在每一个项目中调用的次数比底层API少多了。因此,我们不会专门去严谨,只有用到了才会去看。我们尽量做到语意化。java传统的getXXX setXXX addXXX removeXXX是我们的首选。

最后是D,移除,我们在jQuery中有3个API,remove detach empty,各有各的使用范围。因此,节点的操作是围着DOM树操作。既然是树,就有插入操作,jQuery划分为4个API,其实我们可以看做是IE的insertAdjacentXXX的强化版。还有clone,克隆允许,克隆缓存数据与事件

本章围绕mass的node与node_fix模块,jQuery的manipulation模块。
https://github.com/RubyLouvre/mass-Framework/blob/master/node.js
https://github.com/RubyLouvre/mass-Framework/blob/master/node_fix.js
https://github.com/jquery/jquery/blob/master/src/manipulation.js

一:节点的创建

浏览器提供了多种手段创建API。从流行度来看,依然是document.createElement,innerHTML,insertAdjacentHTML,createContextualFragment。

document.createElement基本不用说什么,它传入一个标签名,然后返回此类元素的节点。并且对于浏览器不支持的标签,也会返回(这也成为了ie6-ie8支持html5新标签的救命稻草)。ie6-ie8中,还有一种方法,能允许标签连同属性一起生成,比如document.createElement("<div id=aaa></div>"),此方法常见生生name属性的input与iframe元素。因为ie6-7下这两个元素只读,不允许修改。

    function createNameElement(type, name) {
        var element = null;
        //尝试ie方式
        try {
            element = document.createElement(‘<‘ + type + ‘ name="‘ + name + ‘">‘);
        } catch (e) {}

        if (!element || element.nodeName != type.toUpperCase()) {
            // non -ie
            element = document.createElement(type);
            element.name = name;
        }
        return element;
    }
    createNameElement("dd","aa"); //=> <dd></dd>  name = aa

innerHTML本来是IE的私有实现,在jQuery1.0就开始挖掘innerHTML了,这不但是innerHTML的创建效率比createElement快2-10倍不等,还因为innerHTML一下能生产一大堆节点。这与jQuery推崇的宗旨不谋而合,但innerHTML发生了兼容性的问题,比如IE会对用户字符串进行trimleft操作,本意是只能去除空白,但FF认为要忠于用户操作,单元位置要生成文本节点。

    var div = document.createElement("div");
    div.innerHTML = "<b>2</b><b>2</b>"
    console.log(div.childNodes.length); //ie6-8 3.其它浏览器4
    console.log(div.firstChild.nodeType); //ie6 1,其它3

IE下有些元素的innerHTML是只读的,重写innerHTML会报错,这就导致我们在动态插入时,不能转求appendChild,insertBefore来处理:

来自MSDN:IE的innerHTML会忽略掉no-scope element.no-scope element是IE的内部概念,隐藏的很深。仅在MSDN说明注释节点是no-scope element,或在官方论坛中透露一点内容,script和style也是no-scope element。经过这么多年的挖掘,大致确认注释,style,script,link,meta,noscript表示功能性的标签为no-scope element,想要用innerHTML生成它们,必须在它们之前加上文字或其它标签。

//ie6 8
    var div = document.createElement("div");
    div.innerHTML = ‘<meta charset=utf-8 />‘; //0
    alert(div.childNodes.length)
    div.innerHTML = ‘x<meta charset=utf-8 />‘; //2
    alert(div.childNodes.length)

另外,一个周知的问题是innerHTML不会执行script标签里的脚本(其实也不然,如果浏览器支持script标签的defer属性,它就能执行。这个特性比较难检测,因此,jQuery一类的直接用正则把它里边的内容抽取出来,直接全局eval了)。 mass的思路是,反正innerHTML赋值后已经将它们转换为节点了,那么再将它们抽取出来,再用document.createElement("script")生成的节点代替就行了。

最后,就是一些元素不能单独作为div的子元素,比如td, th元素,需要在最外边包裹几层,才能放到innerHTML中解释,否则浏览器就会将其当成普通文本节点生成。这个是jQuery团队发现的,现在所有框架都借用此技术生成节点。如果把这些标签比做是标签,那么孵化它们出来的父元素就是胎盘。在w3c规范中,它们都是这样一组组分成不同的模块。

胚胎 胎盘
area map
param object
col tbody,table,colgroup
legend fieldset
option,optground select
thead,tfoot,tbody,colgroup table
tr table,tbody
td,th table,tbody,tr

一直以前,人们都是使用完整的闭合标签来包裹这些特殊的标签,直到人们发现浏览器会自动补全闭合标签后。

        var div = document.createElement("div");
        div.innerHTML = ‘<table><tbody><tr></tr></tbody></table>‘;
        alert(div.getElementsByTagName("tr").length); //1
        div.innerHTML = ‘<table><tbody><tr>‘;
        alert(div.getElementsByTagName("tr").length); //1

能自动补全的有body,colgrounp,dd,dt,head,html,li,optgroup,option,p,tbody,td,tfoot,th,thead,tr.在网速奇慢的年代是一个优化,也是吸引开发者到自己的阵营。

现在已经不推荐这样做,浏览器会固守规则,少写结束标签,很容易引起镶嵌错误,xhtml布道者就是抓住这一点死命抨击html4。

insertAdjacentHTML,也是IE的私有实现,dhtml的产物,比起其他的API,它具有灵活的插入方式。

(更多参考:http://www.cnblogs.com/ahthw/p/4309343.html)

他们一一对应jQuery的prepend,append,before,after。因此,用它来构造这几个方法,代码量会大大减少。但是实现的过程要比我们想象的复杂,我们可以另外一个insertAdjacentHTML来搞定,

insertAdjacentHTML兼容情况如下所示:

浏览器 chorme FF IE Opera Safari(webkit)
版本 1 8 4 7 4(527)

如果浏览器不支持insertAdjacentHTML,那么我们可以用下面介绍的crateContextualFragment来模拟(模拟函数略)。

createContextualFragment是FF推出来的私有实现,它是Range对象的一个实例方法,相当于insertAdjacentHTML直接将内容插入到DOM树,createContextualFragment则是允许我们将字符串转换为文档碎片,然后由你决定插入到哪里。

在著名的emberjs中,如果支持Range ,那么它的html,append,prepend,after等方法都用createContextualFragment与deleteContents实现。createContextualFragment和insertAdjacentHTML一样,要字符串遵循HTML的嵌套规则。

此外,我们还可以用document.write来创建内容,但我们动态添加节点时多发生在dom树建完之后,因此不太合适,这里就不展开了。

之后我们看看mass是怎么实现的,它的结构与jQuery一样,通过两个构造器与一个原型实现无new实例化,这样我们的链式操作就不会被new关键字打断。

    function $(a, b) { //第一个构造器
        return new $.fn.init(a, b); //第二个构造器
    }
    //将原型对象放到一个名字更短更好记的属性名中
    //这是jQuery人性化的体现,也方便扩展原型方法
    $.fn = $.prototype = {
        init : function (a, b) {
            this.a = a;
            this.b = b;
        }
    }
    //共用一个原型
    $.fn.init.prototype = $.fn;

    var a = $(1, 2)
    console.log(a instanceof $);
    console.log(a instanceof $.fn.init)

上面的这个结构非常重要,所有jQuery风格的类库框架都沿袭它实现的链式操作

根据jQuery官方的介绍,它包含9种不同的传参方法

jQuery(selector[,context])
jQuery(element)
jQuery(elementArray)
jQuery(object)
jQuery(jQuery object)
jQuery()
jQuery(html,[ownerDocument])
jQuery(html,attributes)
jQuery(callback)

按功能来分,它大致分为3种:选择器domparserdomReady

由于重载的太多了,因此基本上号称jQuery-compatible的类库框架都没有实现它所有重载。如果抛开这些细节,我们不难发现,除了最后的domReady,其它一切目的不过是想获取要操作的节点罢了。为了更方便的操作,这些节点与实例通过数字进行并联,构成一个类数组对象,因此,你会看到它绑定了push,unshift,pop,shift,splice,sort,reverse,each,map等数组方法,让它看起来就是一个数组。

labor相当于jQuery的pushStack,用于构建下一个类数组对象,比如map,lt,gt,eq等方法就是内部调用它来实现,但jQuery的pushStack远没有这么简单,它还有一个prevObject属性,保存着上次操作的对象。链式操作越多,被引用不能释放的东西就越多,或者处于未来只能使用querySelectorAll做选择器的考量,它们都是好东西。工作业务中,只高亮表格偶数行这一需要也很频繁。因此,做成一个独立的方法是明智的选择。

mass而根据用户传入字符生成一堆节点功能则是由parseHTML方法实现的。parseHTML是一个复杂的方法,它对不同的浏览器做了分级处理,对于ie6-8,框架还会夹在node_fix模块,里边有fixparseHTML,为它打补丁(此处有征对ie6 8的修复方法)。

二.节点的插入

原生的DOM接口是非常简单的,参数类型确定,不会重载,每次只能处理一个元素节点;而jQuery式的方法则相反,虽然名字短,但参数类型复杂,过度重载,对于插入这样的写操作,是进行批处理的。

为了简化处理逻辑,jQuery的做法是统统转换为文档碎片,然后将它复制到与当前jQuery对象里面包含的节点集合相同的个数,一个个插入。

<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<script type="text/javascript">
window.onload = function () {
    var a = document.createElement("span")
    a.innerHTML = "span";
    $("div").append(a)
}
</script>

为了提高性能,合理利用高级api,mass的做法是能用createContextualFragment就用createContextualFragment.能用insertAdjacentHTML的就用insertAdjacentHTML,否则就转化为文档碎片,通过appendChild,insertBefore插入,这意味着里边分支会很复杂,我们需要搞个适配器,让它尽可能地分流。

至与API的命名,将沿袭jQuery的那几个名字,append,prepend,before,after与replace。值的一提的是,由于这几个方法太受欢迎,w3c在DOM4决定原生的支持它们。参数可以是字符串与DOM节点。

mass的这5个方法都是通过manipulate方法实现。

    "append,prepend,before,after,replace".replace($.rword, function(method) {
        $.fn[method] = function(item) {
            return manipulation (this, method, item, this.ownerDocument);
        };
        $.fn[method + "To"]  = function(item) {
            $(item, this.ownerDocument) [method] (this);
            return this
        }
    })

mass的makeFragment函数,这里涉及到两个重要的知识点:NodeList的循环操作,文档碎片的复制

NodeList看起来像数组,但它在插入节点时会立刻改变长度

<div id="test">
     <a href="http://www.baidu.com/">link</a>
</div>
<script type="text/javascript">
window.onload = function () {
    var els = document.getElementsByTagName("a");
    var div = document.getElementById("test");
    for (var i = 0; i < els.length; i++) {
        var ele = document.createElement("a");
        ele.setAttribute("href", "http://www.google.com/");
        ele.appendChild(document.createTextNode("new link"));
        div.appendChild(ele);//添加一个新的链接
    }
}
</script>

上面讲陷入死循环,我们在循环它时,我们最好将它的length保存到一个变量中, 然后比较是否中断循环。

第二个是碎片对象的复制问题,我们大可以使用原生的cloneNode(true),但在IE下,attachEvent绑定的事件会跟着被复制。由于不是我们框架绑定事件,那么再移除时就无法找到对应的引用了。

除此之外,jQuery提供wrap,wrapAll,wrappInner这三种特殊的插入操作。

wrap为当前元素提供了一个共同的父节点,此父节点将动态插入到远节点的父亲底下。这个我们可以轻松在IE下用neo.applyElement(old,"outside")实现。

wrapAll则是为一堆元素提供了一个共同的父节点,插入到第一个元素的父亲节点下,其它元素则统统挪到新节点底下。

wrapInner是为当前的元素插入一个新节点,然后将它之前的孩子挪到新节点底下,这个我们可以在IE下轻松用neo.applyElement(old,"inside")实现

这样看来,applyElement真是很强大,可以在标准浏览器扩展一下,让它应用的更广。

    if (!document.documentElement.applyElement && typeof HTMLElement !== "undefined") {
        HTMLElement.prototype.removeNode = function(deep) {
            if (this.parentNode) {
                if (!deep) {
                    var fragment;
                    var range = this.ownerDocument.createRange();
                    range.selectNodeContents(this);
                    fragment = range.extractContents();
                    range.setStartBefore(this);
                    range.insertNode(fragment);
                    range.detach()
                }
                return this.parentNode.removeChild(this);
            }
            if (!deep) {
                var range = this.ownerDocument.createRange();
                range.selectNodeContents(this);
                range.deleteContents();
                range.detach()
            }
            return this;
        }
    HTMLElement.prototype.applyElement = function(newNode, where) {
        newNode = newNode.removeNode(false);

        switch ((where || ‘outside‘).toLowerCase()) {
            case ‘inside‘ :
            var fragment;
            var range = this.ownerDocument.createRange();
            range.selectNodeContents(this);
            range.surroundContents(newNode);
            range.detach();
            beark;

            case ‘outside‘ :
            var range = this.ownerDocument.createRange();
            range.selectNode(this);
            range.surroundContents(newNode);
            range.detach();
            beark;

            default :
            throw new Error (‘DOMException.NOT_SUPPORTED_ERR(9)‘)
        }
        return newNode;
    }
    }

三.节点的复制

IE下对元素的复制与innerHTML一样,存在许多bug,非常著名的就是IE会多复制attachEvent事件。另外,根据测算,标准浏览器的cloneNode,只会复制元素写在标签内的属性与通过setAttribute设置的属性,而IE6-IE8还支持通过node.aaa = ‘xxx‘设置的属性复制。

如果是这样还好办,但IE在复制时不但会多复制一些,还会少复制一些,这让程序员不好处理。mass和jQuery一样,支持两个参数,第一个是复制节点,但不复制数据与事件。默认为false.第二个决定如何复制它的子孙,默认是遵循参数一。

    $.fn.clone = function (dataAndEvent, deepDataAndEvents) {
        dataAndEvent = dataAndEvent == null ? false :dataAndEvents;
        deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
        return this.map(function() {
            return cloneNode(this, dataAndEvents, deepDataAndEvents);
        })
    }

可以看出,此方法只对参数进行处理,具体操作由cloneNode执行。

    function cloneNode (node, dataAndEvents, deepDataAndEvents) {
        if (node.nodeType === 1) {
            var neo = $.fixCloneNode(node), //复制元素attibutes
                src, neos, i;
            if (dataAndEvents) {
                $.mergeData(neo, node); //复制数据与事件
                if (deepDataAndEvents) {//处理子孙的复制
                    src = node[TAGS] ("*");
                    neos = neo[TAGS] ("*");
                    for (i = 0; src[i], i++) {
                        $.mergeData(neos[i], src[i]);
                    }
                }
            }
            src = neos = null;
            return neo;
        } else {
            return node.cloneNode(true);
        }
    }

cloneNode是做了分层设计的,如果在标准浏览器中fixCloneNode只是一个标准的cloneNode(true).关于更多fixCloneNode模块,请移步mass 的node_fix模块。详见fixNode函数。

这这个里,不得不提是mootools团队挖掘出来的mergeAttributes hack.早些年,为了在ie6-ie8中不复制attachEvent,jQuery被逼动用outerHTML来生成新的节点。

    //应该是jquery 1.4 manipulation模块
    clone : function (events) {
        var ret = this.map(function() {
            if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
                var html = this.outerHTML, ownerDocument = this.ownerDocument;
                if (!html) {
                    var div = ownerDocument.createElement("div");
                    div.appendChild( this.cloneNode(true) );
                    html = div.innerHTML;
                }

                return jQuery.clean([html.replace(rinlinejQuery, "")
                    .replace(rleadingWhitespace, "")], ownerDocument)[0];
            } else {
                return this.cloneNode(true)
            }
        })

        //复制事件
        if (events === true) {
            cloneCopyEvent( this, ret);
            cloneCopyEvent( this.find("*"), ret.find("*"));
        }
        return ret;
    }

即便这样,还是很臃肿,唯一的解决之道就是时间(等事件淘汰IE6等这些古老的浏览器,或者项目主管非常有魄力的支持新锐浏览器)。在zopto等手机框架。把这些数据都存储在data-*属性中。直接用一个cloneNode(true)就搞定节点的复制。

    clone : function() {
        return this.map( function() {
            return this.cloneNode(true)
        } )
    }
    //zepto

四.节点的移除

浏览器提供了很多节点移除的方法,常见的有removeChild、removeNode,动态创建一个元素节点或文档碎片,再appendChild,创建Range对象选中目标节点后,deleteContents。

removeNode是IE的私有实现,opera也实现了此方法。它的作用是将目标节点从文档树中删除,返回目标节点。它有一个参数,为布尔值,其默认值为false:即仅删除目标节点,保留子节点。true时同removeChild的用法。

deleteContents是比较偏门的API,兼容性差。

removeChild在IE6-7中存在内存泄露的问题,与IE的CG回收比较失败引起的。由于太底层,不展开说了。给出EXT的解决方案,像EXT这样庞大的UI库,所有的节点都是动态生成,因此是非常重视CG回收的。

    var removeNode = IE6 || IE7 ? function() {
        var d ;//这里的ie6还ie7自己来实现
        return function(node) {
            if (node && node.tagName != ‘BODY‘) {
                d = d || document.createElement(‘DIV‘);
                d.appendChild(node);
                d.innerHTML = ‘‘;
            }
        }
    }() : function(node) {
        if (node && node.parentNode && node.tagName != ‘BODY‘) {
            node.parentNode.removeChild(node);
        }
    }

为什么这么写,因为在IE6-8中存在一个叫DOM超空间(DOM hyperspace)的概念,当元素移出DOM树,但有javascript关联时元素不会消失,它被保留在一个叫超空间的地方。《PPK读javascript》一书中指出,可以用是否存在parentNode来判定元素是否才超空间。

    window.onload = function () {
        var div = document.createElement("div");
        alert(div.parentNode) //null
        document.body.removeChild(document.appendChild(div));
        alert(div.parentNode); //ie6-8 object,其它的为null
        if (div.parentNode) {
            alert (div.parentNode.nodeType); // => 11 文档碎片
        }
    }

上一个的alert出null , 这个所有的浏览器都一样,因此有时我们误以为可以当做节点是否在DOM的基准。但当元素插入DOM树再移出时,就有差异了。ie6-ie8弹出的是一个文档碎片。因此,可以想象ie的性能为什么这么差了,ie为自以为这样可以重复使用元素,但通常用户移出了就不管,因此,久而久之,内存允许了这么多碎片。加之其他的问题,就很容易造成泄露

我们看一下,innerHTML清除元素会怎么样。

<body><div id="test"></div></body>
<script type="text/javascript">
    window.onload = function() {
        var div = document.getElementById(‘test‘);
        document.body.innerHTML = ‘‘;
        alert(div.parentNode); //null
    }
</script>

结果在IE下也是null,但这也不能说明innerHTML就比removeChild好。我们继续来一个实验

    window.onload = function() {
        var div1 = document.getElementById(‘test1‘);
        div1.parentNode.removeChild(div1);
        alert(div1.id + ":" + div1.innerHTML); // test1:test1

        var div2 = document.getElementById(‘test2‘);
        div2.parentNode.innerHTML = "";
        alert(div2.id + ":" + div2.innerHTML); //(ie)test2:
    }

这时我们就发现,当用removeChild移出节点时,原来的元素结构没有发生变化,但在innerHTML时,ie6-ie8会直接清空里边的内容,只剩下空壳。而标准浏览器则与removeChild保持一致

打个比喻,IE下,removeChild掰断树枝,但树枝可以再次使用。而innerHTML就是把所需树枝拔下来烧掉。鉴于IE下内存管理这么失败,能这么干净的清除节点正是我们寻找的方法!所有EXT从1.0到4.0,此方法没有什么大改变。

对于jQuery这样的框架类库来说。估计很难走这条路,它已经被自己的缓存系统绑架了(移除节点时需要逐个监测元素,从缓存系统中逐个移除对于的缓冲体,否则会造成浏览器宕机)。不过最不好的是,jQuery通过类数组结构与preObject困住节点的方式,造成了jQuery即便是使用innerHTML,元素节点在IE下还是位于DOM超空间中。

jQuery在性能上没有优势,于是在移除节点上造势 。它提供了三种移除节点的方式。remove,移除节点的同时从数据缓存上移除对应的数据empty,只清空元素的内部,相当于IE下的removeChild(false)detach,移除节点但不清除数据。前两种好理解。但为什么要创建第deatch方法呢?

从我们的工作业务看,DOM操作远不止这些,还有UI交互,样式渲染等。但后者都是基于前者上运作。

纯粹的javascript操作不会带来什么消耗,95%以上能耗是DOM操作引起的。出于性能考虑,我们最佳的做法是在设置样式前,将元素移出DOM树,处理完再插回来。

但绝大多数操作DOM的方法都与数据缓存方法关联在一起,若用remove方法,会让它们无法进行数据清理工作,导致内存泄露。而detach就是基于此需要而设计的。从设计理念来看,有点像数据库操作的事务。deatch开始一下,就开始一连串DOM操作,就算怎么操作,也不会伤及DOM树的其它元素。最后conmmit(append)一下,将最后的结果显示出来。

下面是实现过程

    "remove, empty, detach".replace($.rword, function() {
        $.fn[method] = function() {
            var isRemove = method !== "empty";
            for (var i = 0, node; node = this[i++];) {
                if (node.nodeType === 1) {
                    //移除匹配操作
                    var array = $.slice(node[TGAS]("*")).concat(isRemove ? node : []);
                    if (method !== "detach") {
                        array.forEach(cleanNode);
                    }
                }
                if (isRemove) {
                    if (node.parentNode) {
                        node.parentNode.removeChild(node);
                    }
                } else {
                    while (node.firstChild) {
                        node.removeChild(node.firstChild);
                    }
                }
            }
            return this;
        }
    });

如果我们的框架没有像jQuery那样引入庞大的数据缓存系统,而是像zopto.js那样通过h5的data-*来缓存数据,那么许多东西都可以简化了。这也意味着我们打算不兼容ie6 7 8,那么就可以使用deleteContens或textContent;

例如,我们事先一个清空元素内部的API:

方法1

    function clearChild (node) { //node可以是元素节点或文档碎片
        while (node.firstChild) {
            node.removeChild(node.firstChild)
        }
        return node
    }

方法2使用deleteContents,创建一个Range对象,然后通过setStartBefore,setEndAfter选择边界,最后清空它们的节点

    var deleteRange = document.createRange();
    function clearChild(node) { //node可以是元素节点或碎片
        deleteRange.setStartBefore(node.firstChild)
        deleteRange.setEndAfter(node.lastChild)
        deleteRange.deleteContents();
        return node
    }

方法3:使用textContent .textContent是W3C版本中的innerText. 在较新的浏览器里兼容性特别好。并且同时存在于元素节点与文档碎片中。

    function clearChild (node) { //node可以是元素节点或碎片
        node.texrContent = "";
        return node;
    }

本文未完结,由于本章篇幅比较长,请关注本文更新

以下将更新:

五. innerHTML , innerText, outerHTML的处理
六. 一些奇葩的元素节点

上一章: 第七章:选择器引擎  下一章: 第九章: 数据缓存系统

时间: 2024-10-12 14:43:35

第八章:节点模块的相关文章

python基础第十八章-------------路径模块:os.path

路径模块:os.path 导包:import os abspath(路径·):将相对路径转换成绝对路径(路径可以随便写) import os# res=os.path.abspath('1.py')# print(res)会输出:D:\SZC\1.py basename(路径):获取路径的主体部分(路径可以随便写) # res=os.path.basename('D:\\SZC\\2.py')# print(res)会输出:2.py dirname(路径):获取路径的路径部分(路径可以随便写)

Swoole源码学习记录(十)——Factory模块(下)

Swoole版本:1.7.5-stable 本章将分析FactoryProcess.c中剩下的函数,这些函数用于操作worker.manager以及writer.这些函数提供了最核心的进程创建.管理等功能,是Swoole的master-worker结构的基石. 先从worker相关的函数开始(manager相关函数基本都涉及操作worker进程).在FactoryProcess.c中一共声明了4个操作函数,分别是: static int swFactoryProcess_worker_loop(

configparser模块——用于生成和修改常见配置文档

配置文档格式 1 [DEFAULT] 2 ServerAliveInterval = 45 3 Compression = yes 4 CompressionLevel = 9 5 ForwardX11 = yes 6 7 [bitbucket.org] 8 User = hg 配置文档文件格式 解析配置文件:查询 1 #-*- coding:utf-8 -*- 2 #解析配置文件 3 import configparser 4 config = configparser.ConfigParse

第七章:选择器引擎

jQuery凭借选择器风靡全球,各大框架类库都争先开发自己的选择,一时间内选择器变为框架的标配 早期的JQuery选择器和我们现在看到的远不一样.最初它使用混杂的xpath语法的selector.第二代转换为纯css的自定义伪类,(比如从xpath借鉴过来的位置伪类)的sizzle,但sizzle也一直在变,因为他的选择器一直存在问题,一直到JQuery1.9才搞定,并最终全面支持css3的结构伪类. 2005 年,Ben Nolan的Behaviours.js 内置了闻名于世的getEleme

docker swarm集群及其UI部署

一.规划 ①swarm01作为manager节点,swarm02和swarm03作为worker节点. # cat /etc/hosts 127.0.0.1   localhost 192.168.139.175  swarm01  192.168.139.176  swarm02  192.168.139.177  swarm03 ②配置SSH免密登陆 # ssh-keygen -t rsa -P '' # ssh-copy-id -i .ssh/id_rsa.pub [email prote

如何养成每天敲代码的习惯

之前看的一篇国外大牛写下的东东,具体链接忘记了. 1.利用最小化的时间写好代码 强迫自己每天花不少于半个小时来写代码(这真不是一件容易的事,尤其还得回忆前一天写了什么),在工作日期间一般不超过一个小时,但是周末会多花一些时间来做业余项目. 2.让写代码成为习惯 不必关心排名,做业余项目是自己的事,不是为了取悦别人而做. 3.与不良情绪作斗争 在开始实施每天写代码的计划前期,常常会焦虑项目没有很大的进展,毕竟业余项目没有时间限制.但是要知道这样的不良情绪一点帮助都没有,反而会阻碍进度.所以与不良情

TinyOS---传感

本篇文章的传感都以TelosB节点为例,其中捎带MicaZ节点. 说到传感,默认的传感组件是DmeoSensorC组件.在TelosB里,这个组件关联到VoltageC组件,该组件是采集节点模块内部的电压值.若想将DemoSensorC组件关联其他如ConstantC常数组件或者SineC正弦函数组件,则只需修改 components new VoltageC as DemoSensorC中的VoltageC为对应的组件即可.这是修改节点默认传感组件的方法. 但是,在一般使用节点模块时,需要采集

以太网和Zigbee的家居信息采集系统

0 引言 上世纪80 年代发达国家率先提出智能化住宅概念,智能化住宅对家居智能系统的发展起很大促进作用,同时家居信息采集作为住宅智能化起核心作用的一部分,推动着智能化家居的发展.家居信息采集系统预定完成的目标是让使用家庭更方便.更舒适.更安全,同时也符合环境保护的标准.     上世纪90 年代我国首次提出家居信息采集设计思想,同时发展迅速.     小区智能化的定义为:利用计算机.通讯网络.自动化技术和射频卡技术,通过高效的信号传输网络,将多元化信息和管理服务.物业管理与安防技术集成,为小区的

mysql5.6-gtid-半同步-ssl-mha-keepalived方案

Mysql5.6简介 在MySQL 5.5发布两年后,Oracle宣布MySQL 5.6正式版发布,首个正式版版本号为MySQL 5.6.10.在MySQL 5.5中使用的是InnoDB作为默认的存储引擎,而MySQL 5.6则对InnoDB引擎进行了改造,提供全文索引能力,使InnoDB适合各种应用场景.   1.运行环境配置及安装 1.1.部署环境 系统:CentOS6.4 (64位)最小化安装 1.2.下载地址及版本 http://dev.mysql.com/downloads/mysql