摘要:它的作用是是用来处理等操作方法的参数的,统一将其处理为类型节点,并交由函数处理,即上图的。作用将返回的插入到的内部末尾。方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。
前言:这篇我们倒着讲
1、有这样一个页面:
注意:不要 append(test1 ),规范写法是 append( )test1
2、像之前的文章一样,我们自定义 append() 方法
let ajQuery={} jQuery.each({ //例:"Test
" //源码6011行-6019行 // 在被选元素的结尾插入指定内容 /*append的内部的原理,就是通过创建一个文档碎片,把新增的节点放到文档碎片中,通过文档碎片克隆到到页面上去,目的是效率更高*/ append: function(nodelist, arguments) { //node是由domManip处理得到的文档碎片documentFragment,里面包含要插入的DOM节点 let callbackOne=function( node ) { console.log(node,"node149") //this指的就是$("xxx") //1:元素节点,11:DocumentFragment,9:document if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { //table插入tr的额外判断 //target默认情况是selector,即document.querySelectorAll(".inner") let target = manipulationTarget( this, node ) console.log(target,node.childNodes,"node147") //append的本质即使用原生appendChild方法在被选元素内部的结尾插入指定内容 target.appendChild( node ); } } console.log(nodelist,arguments,"this120") return domManip( nodelist, arguments, callbackOne ); }, }, function(key, value) { ajQuery[key] = function(nodelist, arguments) { console.log(nodelist,"nodelist128") return value(nodelist, arguments); } } )
3、可以看到,append() 内部调用了 domManip 的方法,接下来重点介绍下该方法
(1)什么是 domManip ?
domManip() 是 jQuery DOM 的核心函数。dom 即 Dom 元素,Manip 是Manipulate 的缩写,连在一起就是 Dom 操作的意思。
(2)它的作用是?
domManip() 是用来处理 $().append(xxx)、$().after(xxx) 等操作 DOM 方法的参数的,统一将其处理为 DOM 类型节点,并交由 callback 函数处理,即上图的 callbackOne。
注意: 本文暂不考虑参数包含 的情况,如:
ajQuery.append(innerArr,"alert("append执行script")")
4、domManip() 的三个参数:nodelist, arguments, callbackOne
nodelist:即 document.querySelectorAll(".inner")
arguments:即字符串 " callbackOne:回调函数,在 nodelist、arguments 被相应逻辑处理后会返回一个文档碎片documentFragment,该方法会对 该文档碎片进行处理 注意:domMainp 函数讲解在 第 8 点。 5、callbackOne() 作用: 将 domManip 返回的 documentFragment 插入到 selector 的内部末尾。 也就是说 $().append() 的本质是 DOM节点.appendChild(处理过的documentFragment(里面包含插入的DOM节点)) 源码: 6、callbackOne() 中的函数:manipulationTarget() 作用: 额外判断,当选择器是table,并且插入的元素是tr时,会查找到table下的tbody,并返回tbody 源码: 7、manipulationTarget() 中的函数:nodeName() 作用: 判断两个参数的nodename是否相等 源码: 8、jQueryDOM 核心函数:domManip() 作用: 将传入的参数(dom节点元素、字符串、函数)统一转化为符合要求的DOM节点 源码: 解析: 我们可以看到在 目标节点的个数 >=1 的情况下(if(l){xxx}), **注意: 关于 documentFragment,请看文章: jQuery之documentFragment 9、domManip() 中的函数 buildFragment() 作用: 创建文档碎片 源码: 解析: (1)创建文档碎片 documentFragment (2)在 待插入的元素存在的情况下,先在 documentFragment 内部插入 标签 创建div是为了处理innerHTML的缺陷(IE会忽略开头的无作用域元素),所以让所有的元素都被div元素给包含起来,包括script,style等无作用域的元素 (3)但是 比如 (4)documentFragment 在成功添加完子元素后,再卸磨杀驴,去掉包裹的节点,如上例的 (5)最后返回 处理好的文档碎片 fragment 10、rtagName 作用: 匹配div不支持的标签,如 tr、td等。 源码: 11、wrapMap 作用: div 不支持的标签表 源码: 12、jQuery.htmlPrefilter() 作用: 标签转换为闭合标签,如 源码: 13、综上,当我调用了$(".inner").append(" 14、本篇文章的所有代码 github: https://github.com/AttackXiao... 代码: (完) 文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。 转载请注明本文地址:https://www.ucloud.cn/yun/103075.html 摘要:起初选择先看源码而不是的原因也简单作为每个前端会用的第一个框架,虽然过时,但却又如此普及,所以想看看它是咋设计的。
showImg(https://segmentfault.com/img/remote/1460000019663680);
起初选择先看jQuery源码而不是react的原因也简单:jQuery作为每个前端会用的第一个框架,虽然过时,但却又如此普及,所以想看看它是咋设计... 摘要:效果预览来自的效果预览引用了,已被墙,请谨慎预览转载声明这篇文章其实源自的博客。主要内容整个效果都建立在的里面。为了弥补这个缺陷,我决定改成在函数中对进行操作。重点来了,是和初始位置的相对位置。就是元素的初始值。
效果预览
来自codepen的效果预览:(引用了angularjs,已被墙,请谨慎预览)http://codepen.io/flybywind/pen/aNjxJa
转载声明... 摘要:效果预览来自的效果预览引用了,已被墙,请谨慎预览转载声明这篇文章其实源自的博客。主要内容整个效果都建立在的里面。为了弥补这个缺陷,我决定改成在函数中对进行操作。重点来了,是和初始位置的相对位置。就是元素的初始值。
效果预览
来自codepen的效果预览:(引用了angularjs,已被墙,请谨慎预览)http://codepen.io/flybywind/pen/aNjxJa
转载声明... 阅读 827·2019-08-30 15:54 阅读 3300·2019-08-29 15:33 阅读 2686·2019-08-29 13:48 阅读 1185·2019-08-26 18:26 阅读 3315·2019-08-26 13:55 阅读 1412·2019-08-26 10:45 阅读 1146·2019-08-26 10:19 阅读 286·2019-08-26 10:16test1 "
//node是由domManip处理得到的文档碎片documentFragment,里面包含要插入的DOM节点
let callbackOne=function( node ) {
console.log(node,"node149")
//this指的就是$("xxx")
//1:元素节点,11:DocumentFragment,9:document
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
//table插入tr的额外判断
//target默认情况是selector,即document.querySelectorAll(".inner")
let target = manipulationTarget( this, node )
console.log(target,node.childNodes,"node147")
//append的本质即使用原生appendChild方法在被选元素内部的结尾插入指定内容
target.appendChild( node );
}
}
//源码5724行-5733行
//额外判断,当选择器是table,并且插入的元素是tr时,会查找到table下的tbody,并返回tbody
//this, node
function manipulationTarget( selector, node ) {
console.log(node.childNodes,node.firstChild,"node73")
// 如果是table里面插入行tr
if ( nodeName( selector, "table" ) &&
nodeName( node.nodeType !== 11 ? node : node.firstChild, "tr" ) ) {
return jQuery( selector ).children( "tbody" )[ 0 ] || selector
}
return selector
}
//源码2843行-2847行
//判断两个参数的nodename是否相等
function nodeName( selector, name ) {
return selector.nodeName && selector.nodeName.toLowerCase() === name.toLowerCase();
}
//源码5597行-5586行
//作用是将传入的参数(dom节点元素、字符串、函数)统一转化为符合要求的DOM节点
//例:$(".inner").append("
")
//nodelist即$(".inner")
//args即Test
function domManip( nodelist, args, callback ) {
console.log(nodelist,args,"ignored5798")
//数组深复制成新的数组副本
//源码是:args = concat.apply( [], args ),这里没有用arguments,而是传参就改了
let argsArr = []
argsArr.push(args)
console.log(argsArr,"args31")
//l 长度,比如类名为.inner的li有两组,2
let fragment,
first,
node,
i = 0,
//l 长度,比如类名为.inner的li有两组,2
l = nodelist.length,
iNoClone = l - 1
//l=2
console.log(l,"lll45")
if ( l ) {
console.log(argsArr,nodelist[0].ownerDocument,nodelist,"firstChild40")
//argsArr:Test
//nodelist[0].ownerDocument:目标节点所属的文档
fragment = buildFragment(argsArr,nodelist[0].ownerDocument,false,nodelist );
first=fragment.firstChild
console.log(fragment.childNodes,"firstChild42")
//即test1
if (first) {
//=====根据nodelist的长度循环操作========
for ( ; i < l; i++ ) {
console.log(node,fragment.childNodes,"childNodes49")
node = fragment;
if ( i !== iNoClone ) {
/*createDocumentFragment创建的元素是一次性的,添加之后就不能再操作了,
所以需要克隆iNoClone的多个节点*/
node = jQuery.clone( node, true, true );
}
console.log(nodelist[i], node.childNodes,"node50")
//call(this,param)
callback.call( nodelist[i], node);
}
//====================
}
}
console.log(nodelist,"nodelist58")
return nodelist
}test1
调用了 buildFragment() 方法,该方法作用是 创建文档碎片documentFragment,以便高效地向 目标节点 插入元素,然后根据 目标节点个数 循环地调用 callback 方法,即调用 原生 appendChild 方法插入元素。
由于 createDocumentFragment 创建的元素是一次性的,添加之后就成只读的了,所以需要克隆 createDocumentFragment创建的元素,以便再次操作。** //源码4857行-4945行
/*创建文档碎片,原因是一般情况下,我们向DOM中添加新的元素或者节点,DOM会立刻更新。
如果向DOM添加100个节点,那么就得更新100次,非常浪费浏览器资源。
解决办法就是:我们可以创建一个文档碎片(documentFragment),
documentFragment类似于一个小的DOM,在它上面使用innerHTML并在innerHTML上插入多个节点,速度要快于DOM(2-10倍),
比如:先将新添加的100个节点添加到文档碎片的innerHTML上,再将文档碎片添加到DOM上。*/
//args, collection[ 0 ].ownerDocument, false, collection
function buildFragment( arr, context, truefalse, selection ) {
let elem,tmp, nodes = [], i = 0, l = arr.length,wrap,tag,j
// createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。
//相当于document.createDocumentFragment()
let fragment = context.createDocumentFragment()
//l=1
console.log(l,"l87")
//==============
for ( ; i < l; i++ ) {
//"
"
elem = arr[ i ];
console.log(i,elem,"elem90")
if ( elem || elem === 0 ) {
/*创建div是为了处理innerHTML的缺陷(IE会忽略开头的无作用域元素),
让所有的元素都被div元素给包含起来,包括script,style等无作用域的元素*/
tmp=fragment.appendChild( context.createElement( "div" ) )
//就是匹配div不支持的标签,如 tr、td等
/*不支持innerHTML属性的元素,通过正则多带带取出处理*/
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
/*作用就是利用wrapMap让不支持innerHTML的元素通过包装wrap来支持innerHTML*/
//ie对字符串进行trimLeft操作,其余是用户输入处理
//很多标签不能多带带作为DIV的子元素
/*td,th,tr,tfoot,tbody等等,需要加头尾*/
wrap = wrapMap[ tag ] || wrapMap._default // tr: [ 2, "", "
" ]
console.log(wrap,"wrap152")
//将修正好的element添加进innerHTML中
//jQuery.htmlPrefilter:标签转换为闭合标签,如 -->
/*div不支持tr、td所以需要添加头尾标签,如
xxxx
*/
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
// 因为warp被包装过,需要找到正确的元素父级
j = wrap[ 0 ]; //2
while ( j-- ) {
tmp = tmp.lastChild;
}
//temp:
//tmp.childNodes:tr
//nodes:[]
//jQuery.merge:将两个数组合并到第一个数组中
jQuery.merge( nodes, tmp.childNodes );
}
}
//================
// Remove wrapper from fragment
fragment.textContent = "";
//需要将i重置为0
i=0
while ( ( elem = nodes[ i++ ] ) ) {
fragment.appendChild( elem )
}
console.log(fragment.childNodes,"fragment105")
return fragment;
}
let fragment = context.createDocumentFragment()
标签,会被 wrap 转为 ,再成功添加到 documentFragment 的 innerHTML 中。
test1 let rtagName = ( /<([a-z][^/ >x20
f]+)/i )
let wrapMap = {
// Support: IE <=9 only
option: [ 1, "" ],
// XHTML parsers do not magically insert elements in the
// same way that tag soup parsers do. So we cannot shorten
// this by omitting or other required elements.
thead: [ 1, "
", "
" ],
col: [ 2, "
" ],
tr: [ 2, "", "
" ],
td: [ 3, "
" ],
_default: [ 0, "", "" ]
};
// Support: IE <=9 only
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
", " -->
htmlPrefilter: function( html ) {
return html.replace( rxhtmlTag, "<$1>$2>" );
}
")后,jQuery内部发生的事件如下test1
相关文章
jQuery源码解析系列一目录
angularjs特效之分散的字符串--解析compile link $compile $inter
angularjs特效之分散的字符串--解析compile link $compile $inter
发表评论
0条评论
skinner
男|高级讲师
TA的文章
阅读更多
2018年值得期待11个Javascript动画库
前端学习记录(CSS篇)
css揭秘笔记——视觉效果
datatables增加跳转指定页功能, datatable自定义分页
2019 再聊移动端 300ms 延迟及 fastClick 原理解析
手写实现promise
http协议汇总
标准库(三)包装对象