manipulation.js源码

提供操作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;
});

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值