DOM操作包括append、prepend、before、after、replaceWith、appendTo、prependTo、insertBefore、insertAfter、replaceAll。其核心处理函数是domManip。
DOM操作函数中后五种方法使用的依然是前面五种方法,源码
浏览器原生的插入节点的方法有两个:appendChild和inserBefore,jQuery利用这两个方法拓展了如下方法
jQuery.fn.append使用this.appendChild( elem )
jQuery.fn.prepend使用this.insertBefore( elem,this.firstChild )
jQuery.fn.before使用this.parentNode.insertBefore( elem,this );
jQuery.fn.after使用this.parentNode.insertBefore( elem,this.nextSibling );
jQuery.fn.replaceWith 使用this.parentNode.insertBefore( elem,this.nextSibling);
看一个例子的源码(jQuery.fn.append)
根据上面的源码。猜测domManip的作用是遍历当前jQuery对象所匹配的元素,然后每个元素调用传入的回调,并将要插入的节点(如果是字符串那么需要创建文档碎片节点)作为传入的回调的参数;并执行传入的回调。
接下来分析domManip,看猜测是否正确。dom即Dom元素,Manip是Manipulate的缩写,连在一起的字面意思就是就是Dom操作。
args 待插入的DOM元素或HTML代码 table 是否需要修正tbody,这个变量是优化的结果 callback 回调函数,执行格式为callback.call( 目标元素即上下文,待插入文档碎片/单个DOM元素 ) 先看流程,再看细节 第一步,变量初始化。其中iNoClone在后面会用到,如果当前的jQuery对象所匹配的元素不止一个(n > 1)的话,意味着构建出来的文档碎片需要被n用到,则需要被克隆(n-1)次,加上碎片文档本身才够n次使用;value 是第一个参数args的第一个元素,后面会对value是函数做特殊处理; 第二步,处理特殊下要将当前jQuery对象所匹配的元素一一调用domManip。这种特殊情况有两种:第一种,如果传入的节点是函数(即value是函数)则需要当前jQuery对象所匹配的每个元素都将函数计算出的值作为节点代入domManip中处理。第二种,webkit下,我们不能克隆文含有checked的文档碎片;克隆的文档不能重复使用,那么只能是当前jQuery对象所匹配的每个元素都调用一次domManip处理。 第三步,处理正常情况,使用传入的节点构建文档碎片,并插入文档中。这里面构建的文档碎片就需要重复使用,区别于第二步的处理。这里面需要注意的是如果是script节点需要在加载完成后执行。顺着源码顺序看一下过程 构建文档碎片
分离出其中的script,这其中有一个函数disableScript更改了script标签的type值以确保安全,原来的type值是"text/javascript",改成了"true/text/javascript"或"false/text/javascript"
文档碎片插入页面
执行script,分两种情况,远程的使用ajax来处理,本地的直接执行。
jQuery.fn.text
最终执行value === undefined ? jQuery.text( this ) : this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
其中jQuery.text = Sizzle.getText;
jQuery.fn.html
函数使用jQuery.access来处理
如果没有参数表示是取值
如果不能使用innerHTML或使用不成功(抛出异常),则使用备用方法append
用包裹裹住当前jQuery对象
注意:当前jQuery对象匹配的元素最好只有一个,如果有多个的话不推荐使用,这种情况慎用,后面举例可以看到。
简单的例子,原DOM为(后面都使用这个例子)
慎用:如果当前jQuery所匹配的元素不止一个,例如原DOM执行$('div').wrapAll(“
”)后结果DOM变成看到结果了吧,本来#center是#ss的父节点,结果变成了#ss的兄弟节点。
jQuery.fn.wrapInner(在每个匹配元素的所有子节点外部包裹指定的HTML结构)
处理步骤:
遍历jQuery对象数组,获取每个元素包含的内容(所有子节点)contents,然后使用warpAll包裹住contents
contents.wrapAll( html );
} else {
self.append( html );
}
});
还是使用上面的例子中的原DOM,执行$('div').wrapInner('
')后结果DOM变成jQuery.fn.wrap(在每个匹配元素外部包裹指定的HTML结构)
对jQuery的每个元素分别使用wrapAll包裹一下
行$('div').wrap('
')后结果DOM变成
jQuery.fn.unwrap(移除每个匹配元素的父元素)
使用replaceWith用匹配元素父节点的所有子节点替换匹配元素的父节点。当然了父节点是body/html/document肯定是移除不了的
jQuery.fn.remove(从文档中移除匹配的元素)
你还可以使用选择器进一步缩小移除的范围,只移除当前匹配元素中符合指定选择器的部分元素。
与detach()相比,remove()函数会同时移除与元素关联绑定的附加数据( data()函数 )和事件处理器等(detach()会保留)。
可以看到其中有一个重要的函数cleanData,该方法是用来清除缓存:遍历每一个节点元素,对每一个节点元素做一下处理:
1.获取当前元素对应的缓存
2.如果有绑定事件,则遍历解绑事件
3.如果jQuery.event.remove没有移除cache,则手动移除cache。其中IE需要做一些兼容处理,而且最终会将删除历史保存如core_deletedIds中
jQuery.fn.detach
jQuery.fn.empty(清空每个匹配元素内的所有内容(所有子节点))
函数将会移除每个匹配元素的所有子节点(包括文本节点、注释节点等所有类型的节点),会清空相应的缓存数据。