提供操作dom元素的append、remove等方法
// setGlobalEval.js define([ "../data/var/dataPriv" ],function(dataPriv){ // "globalEval"属性标记文档中已执行的sctipt节点 function setGlobalEval(elems,refElements){ var i=0, l=elems.length; for ( ; i<l; i++ ){ dataPriv.set( elems[i], "globalEval", !refElements || dataPriv.get(refElements[i],"globalEval") ); } } return setGlobalEval; }); // buildFragment.js define( [ "../core", "./var/rtagName",// 匹配html节点标签名 ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ) "./var/rscriptType",// ( /^$|\/(?:java|ecma)script/i ) "./wrapMap",// 获取option、tr、tbody包裹节点名称select、table/tbody、table "./getAll",// getAll(context,tag) 获取context下所有tagName为tag的节点 "./setGlobalEval"// script节点设置globalEval属性 ], function( jQuery, rtagName, rscriptType, wrapMap, getAll, setGlobalEval ) { var rhtml=/<|&#?\w+;/; // 以context为文档节点调用createDocumentFragment创建fragment,并将elems添加到fragment中 // 若elems下子节点存在于selection中,不予插入fragment,并且推送到ignored中 // elems若有script节点,且scripts为数组,scripts保存elems下script节点 function buildFragment(elems,context,scripts,selection,ignored){ var elem, tmp, tag, wrap, contains, j, fragment=context.createDocumentFragment(),// 创建虚拟的节点对象,不改变文档树的结构 nodes=[], i=0, l=elems.length; for ( ; i<l; i++ ){ elem=elems[i]; if ( elem || elem===0 ){ // elem本身为dom对象或dom对象数组 if ( jQuery.type(elem)==="object" ){ jQuery.merge(nodes,elem.nodeType ? [elem] : elem ); // 不带<字符串形式 }else if( !rhtml.test(elem) ){ nodes.push(context.createTextNode(elem)); // 将html转化成dom对象,插入文档中,用dom方法获取 } else { tmp=tmp || fragment.appendChild( context.createElement("div") ); // option、tr、tbody元素用select、table/tbody、table包裹 tag=( rtagName.exec(elem) || ["",""] )[1].toLowerCase(); wrap=wrapMap[tag] || wrapMap._default; tmp.innerHTML=wrap[1] + jQuery.htmlPrefilter(elem)+wrap[2]; // 用div、select等包裹后,lastChild获取实际添加的元素 j=wrap[0]; while ( j-- ){ tmp=tmp.lastChild; } // html字符串转化为dom对象形式,存储到nodes中 jQuery.merge(nodes,tmp.childNodes); tmp=fragment.firstChild; tmp.textContent=""; } } } // 清除fragment中元素后,使用遍历方式将nodes中元素推送到fragment中 fragment.textContent=""; i = 0; while ( (elem=nodes[i++]) ){ // 传入的elems本就是dom节点对象的形式,elem才可能在selection中,推入ignored里 if ( selection && jQuery.inArray(elem,selection)>-1 ){ if ( ignored ){ ignored.push(elem); } continue; } // elem本就在文档中,不是createDocumentFragment方法创建 contains=jQuery.contains(elem.ownerDocument,elem); // fragment中添加elem,tmp获取所有script节点 tmp=getAll(fragment.appendChild(elem),"script"); // script节点设置globalEval属性 if ( contains ){ setGlobalEval(tmp); } // script节点压入scripts数组中,前提是scripts存在且为数组 if ( scripts ){ j=0; while ( ( elem=tmp[j++] ) ){ if ( rscriptType.test(elem.type || "") ){ scripts.push(elem); } } } } return fragment; } return buildFragment; }); // mainpulation.js define([ "./core", "./var/concat", "./var/push", "./core/access", "./manipulation/var/rcheckableType", "./manipulation/var/rtagName", "./manipulation/var/rscriptType", "./manipulation/wrapMap", "./manipulation/getAll", // 文档中已执行的sctipt节点添加globalEval标记 "./manipulation/setGlobalEval", // 调用createDocumentFragment方法创建节点,包裹需要获取的节点 "./manipulation/buildFragment", // 兼容性问题,如webkit旧版在fragment中拷贝checked属性,ie11以下版本不拷贝textarea文本 "./manipulation/support", "./data/var/dataPriv", "./data/var/dataUser", "./data/var/acceptData", "./core/DOMEval", "./core/init", "./traversing", "./selector", "./event" ],function(jQuery,concat,push,access,rcheckableType,rtagName,rscriptType,wrapMap,getAll, setGlobalEval,buildFragment,support,dataPriv,dataUser,acceptData,DOMEval){ var rxhtmlTag=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, // Support: IE <=10 - 11, Edge 12 - 13 // In IE/Edge using regex groups here causes severe slowdowns. // See https://connect.microsoft.com/IE/feedback/details/1736512/ rnoInnerhtml = /<script|<style|<link/i, rchecked=/checked\s*(?:[^=]|=\s*.checked.)/i,// 判断元素带有checked属性 rscriptTypeMasked = /^true\/(.*)/, rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g; function manipulationTarget(elem,content){ if ( jQuery.nodeName(elem,"table") && jQuery.nodeName(content.nodeType!==11 ? content : content.firstChild,"tr") ){ return elem.getElementsByTagName("tbody")[0] || elem; } return elem; } // 修改type,插入操作时阻止脚本执行,避免后续插入被执行脚本阻塞 function disableScript(elem){ elem.type=(elem.getAttribute("type")!==null)+"/"+elem.type; return elem; } // 重设type,插入操作完成后执行脚本 function restoreScript(elem){ var match=rscriptTypeMasked.exec(elem.type); if ( match ){ elem.type=match[1]; }else{ elem.removeAttribute("type"); } return elem; } // 拷贝挂载在元素上的data属性,包含event事件 function cloneCopyEvent( src, dest ){ var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; if ( dest.nodeType!==1 ){ return; } // 拷贝私有数据,包括events事件,且绑定事件 if ( dataPriv.hasData(src) ){ pdataOld=dataPriv.access(src); pdataCur=dataPriv.set(dest,pdataOld); events=pdataOld.events; if ( events ){ delete pdataCur.handle; pdataCur.events={}; for ( type in events ){ for ( i=0, l=events[type].length; i<l; i++ ){ jQuery.event.add(dest,type,events[type][i]); } } } } // 拷贝公有数据,向外提供接口 if ( dataUser.hasData(src) ){ udataOld=dataUser.access(src); udataCur=jQuery.extend({},udataOld); dataUser.set(dest,udataCur); } } // 为input设置属性 function fixInput(src,dest){ var nodeName=dest.nodeName.toLowerCase(); if ( nodeName==="input" && rcheckableType.test(src.type) ){ dest.checked=src.checked; }else if( nodeName==="input" || nodeName==="textarea" ){ dest.defaultValue=src.defaultValue; } } // 插入节点 // 参数collection为$ele.append方法中的this关键字,表示目标节点 // 参数args为$ele.append方法的参数,需要插入文档的节点 // callback回调函数,以collection[i]、args获得的插入节点node、i为参数 // $ele.append方法中callback为将node插入到collection[i]里 // 参数ignored:带插入的节点args在collection,推送到ignored中 function domManip(collection,args,callback,ignored){ args=concat.apply([],args); var fragment, first, scripts, hasScripts, node, doc, i=0, l=collection.length, iNoClone=l-1, value=args[0], isFunction=jQuery.isFunction(value); if ( isFunction || ( l>1 && typeof value==="string" && !support.checkClone && rchecked.test(value) ) ){ return collection.each( function(index){ var self=collection.eq( index ); if ( isFunction ){ args[0]=value.call(this,index,self.html()); } domManip(self,args,callback,ignored); }); } if ( l ){ // 以collection[0].ownerDocument为文档调用createDocumentFragment创建fragment // 将args插入到fragment中 // 若args下子节点存在于collection中,不予插入fragment,并且推送到ignored中 fragment=buildFragment(args,collection[0].ownerDocument,false,collection,ignored); first=fragment.firstChild; if ( fragment.childNodes.length===1 ){ fragment=first; } if ( first || ignored ){ // 修改type属性,开发者插入的script节点中type不为空或"text/javascript",避免script执行 // 目的是,插入时不执行,插入操作完成决定执行与否 scripts=jQuery.map(getAll(fragment,"script"),disableScript); hasScripts=scripts.length; for ( ; i<l; i++ ){ node=fragment; if ( i!==iNoClone ){ node=jQuery.clone(node,true,true);// 拷贝对象,引用对象在文档中只可有一个 if ( hasScripts ){ jQuery.merge(scripts,getAll(node,"script")); } } // 插入到文档中 callback.call(collection[i],node,i); } if ( hasScripts ){ doc=scripts[scripts.length-1].ownerDocument; jQuery.map(scripts,restoreScript);// 重设type for ( i=0; i<hasScripts; i++ ){ node=scripts[i]; // 新插入script节点添加globalEval属性,表示插入时需要执行 // 通过createDocumentFragment创建的元素中插入script节点将不予执行 if ( rscriptType.test(node.type || "") && !dataPriv.access(node,"globalEval") && jQuery.contains(doc,node) ){ // 执行script节点的代码 if ( node.src ){ // Optional AJAX dependency, but won't run scripts if not present if ( jQuery._evalUrl ){ jQuery._evalUrl(node.src); } }else{ DOMEval(node.textContent.replace(rcleanScript,""),doc); } } } } } } return collection; } // 根据keepData保留元素的data数据,移除并清空节点 function remove(elem,selector,keepData){ var node, nodes=selector ? jQuery.filter(selector,elem) : elem, i=0; for ( ; (node=nodes[i])!=null; i++ ){ if ( !keepData && node.nodeType===1 ){ // 清除elems中挂载的事件处理函数并解绑,清除私有dataPriv以及公有dataUser jQuery.cleanData(getAll(node));// getAll(node)数组形式返回node } if ( node.parentNode ){ // jQuery.contains用于判断指定元素内是否包含另一个元素 if ( keepData && jQuery.contains(node.ownerDocument,node) ){ // setGlobalEval用"globalEval"属性赋值为true,记录sctipt节点中的语法已执行 setGlobalEval(getAll(node,"script"));// getAll(node,"script")获取node所有script节点 } node.parentNode.removeChild(node); } } return elem; } jQuery.extend( { htmlPrefilter: function( html ) { return html.replace( rxhtmlTag, "<$1></$2>" ); }, // 拷贝elem元素,dataAndEvents为真拷贝elem的事件,deepDataAndEvents为真拷贝elem及其子元素的事件 clone:function(elem,dataAndEvents,deepDataAndEvents){ var i, l, srcElements, destElements, clone=elem.cloneNode(true), inPage=jQuery.contains(elem.ownerDocument,elem); // Fix IE cloning issues if ( !support.noCloneChecked && ( elem.nodeType===1 || elem.nodeType===11 ) && !jQuery.isXMLDoc(elem) ){ destElements=getAll(clone); srcElements=getAll(elem); for ( i=0, l=srcElements.length; i<l; i++ ){ fixInput(srcElements[i],destElements[i]);// 设置input属性 } } if ( dataAndEvents ){ if ( deepDataAndEvents ){ srcElements=srcElements || getAll(elem);// 获取所有子元素 destElements=destElements || getAll(clone); for ( i=0, l=srcElements.length; i<l; i++ ) { // cloneCopyEvent拷贝挂载在元素上的data属性,包含event事件 cloneCopyEvent(srcElements[i],destElements[i]); } }else{ cloneCopyEvent(elem,clone); } } destElements=getAll(clone,"script"); if ( destElements.length > 0 ) { setGlobalEval(destElements,!inPage && getAll(elem,"script")); } return clone; }, // 清除elems中挂载的事件处理函数并解绑,清除私有dataPriv以及公有dataUser cleanData:function(elems){ var data,elem,type, special=jQuery.event.special, i=0; for ( ; (elem=elems[i])!==undefined; i++ ){ if ( acceptData(elem) ){ if ( (data=elem[dataPriv.expando]) ){ if ( data.events ){ for ( type in data.events ){ if ( special[type] ){ jQuery.event.remove(elem,type); } else { jQuery.removeEvent(elem,type,data.handle); } } } elem[dataPriv.expando]=undefined; } if ( elem[dataUser.expando] ){ elem[dataUser.expando]=undefined; } } } } }); jQuery.fn.extend({ // 从文档中移除元素,保留元素的绑定事件和data数据,返回元素的jquery对象,可以重新插入文档 detach:function(selector){ return remove(this,selector,true); }, // 从文档中移除元素,清空元素的绑定事件和data数据,返回元素的jquery对象,可以重新插入文档 remove:function(selector){ return remove(this,selector); }, // 通过elem.textContent赋值文本;jQuery.text取值操作,jQuery.text在sizzle模块定义 text:function(value){ return access(this,function(value){ return value===undefined ? jQuery.text(this) : this.empty().each(function(){ if ( this.nodeType===1 || this.nodeType===11 || this.nodeType===9 ){ this.textContent=value; } }); },null,value,arguments.length); }, // 插入文本或节点 append:function(){ return domManip(this,arguments,function(elem){ if ( this.nodeType===1 || this.nodeType===11 || this.nodeType===9 ){ var target=manipulationTarget(this,elem ); target.appendChild(elem); } } ); }, prepend:function(){ return domManip(this,arguments,function(elem){ if ( this.nodeType===1 || this.nodeType===11 || this.nodeType===9 ){ var target=manipulationTarget( this, elem); target.insertBefore(elem,target.firstChild); } } ); }, before:function(){ return domManip(this,arguments,function(elem){ if ( this.parentNode ){ this.parentNode.insertBefore(elem,this); } } ); }, after:function(){ return domManip(this,arguments,function(elem){ if ( this.parentNode ){ this.parentNode.insertBefore(elem,this.nextSibling); } } ); }, // 清空文本和Data数据 empty:function(){ var elem,i=0; for ( ; (elem=this[i])!=null; i++ ){ if ( elem.nodeType===1 ){ jQuery.cleanData(getAll(elem,false)); elem.textContent=""; } } return this; }, // 拷贝dom元素 clone:function(dataAndEvents,deepDataAndEvents){ dataAndEvents=dataAndEvents==null ? false : dataAndEvents; deepDataAndEvents=deepDataAndEvents==null ? dataAndEvents : deepDataAndEvents; return this.map(function(){ return jQuery.clone(this,dataAndEvents,deepDataAndEvents); } ); }, // 设置innerHtml html:function(value){ return access(this,function(value){ var elem=this[0] || {}, i=0, l=this.length; // html()返回值innerHTML if ( value===undefined && elem.nodeType===1 ) { return elem.innerHTML; } // 不含script等样式、脚本标签、tr等需要table包裹的标签 if ( typeof value==="string" && !rnoInnerhtml.test(value) && !wrapMap[ (rtagName.exec(value) || ["",""])[1].toLowerCase() ] ){ value=jQuery.htmlPrefilter(value); try { for ( ; i<l; i++ ){ elem=this[i] || {}; if ( elem.nodeType===1 ){ jQuery.cleanData( getAll(elem,false) ); elem.innerHTML=value; } } elem=0; }catch(e){} } if ( elem ) { this.empty().append(value); } },null,value,arguments.length); }, // 替换,替换前清空原节点的data数据 replaceWith:function(){ var ignored=[]; return domManip(this,arguments,function(elem){ var parent=this.parentNode; if ( jQuery.inArray(this,ignored)<0 ){// 传参arguments不是当前jquery元素 jQuery.cleanData(getAll(this)); if ( parent ){ parent.replaceChild(elem,this); } } },ignored); } }); // append方法参数为不在文档中的dom对象,appendTo参数为在文档中的对象 jQuery.each({ appendTo:"append", prependTo:"prepend", insertBefore:"before", insertAfter:"after", replaceAll:"replaceWith" },function(name,original){ jQuery.fn[name]=function(selector){ var elems, ret=[], insert=jQuery(selector), last=insert.length-1, i=0; for ( ; i<=last; i++ ){ elems=i===last ? this : this.clone(true); jQuery(insert[i])[original](elems); push.apply(ret,elems.get()); } return this.pushStack(ret); }; } ); return jQuery; });