You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 这段代码的方法是jQuery里面最基本的方法了。很熟悉的感觉jQuery.fn.extend({text: function(text){// text为函数, 则each调用函数之后的值if(jQuery.isFunction(text)){returnthis.each(function(i){varself=jQuery(this);self.text(text.call(this,i,self.text()));});}// 为元素插入文本节点if(typeoftext!=="object"&&text!==undefined){returnthis.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));}// 返回第一个元素的文本信息returnjQuery.text(this);},wrapAll: function(html){// 传入方法则为每个元素用方法执行后的结果执行warpAllif(jQuery.isFunction(html)){returnthis.each(function(i){jQuery(this).wrapAll(html.call(this,i));});}if(this[0]){// The elements to wrap the target around// 先获取包裹元素的副本varwrap=jQuery(html,this[0].ownerDocument).eq(0).clone(true);// 如果有第一个匹配元素有父元素,则将包裹元素插入到第一个元素的前面if(this[0].parentNode){wrap.insertBefore(this[0]);}// 找到wrap最内层的元素,然后把当前元素插进去wrap.map(function(){varelem=this;while(elem.firstChild&&elem.firstChild.nodeType===1){elem=elem.firstChild;}returnelem;}).append(this);}returnthis;},wrapInner: function(html){// 累if(jQuery.isFunction(html)){returnthis.each(function(i){jQuery(this).wrapInner(html.call(this,i));});}// 遍历当前元素// 如果当前元素有子元素则子元素调用wrapAll// 否则直接把wrap插入当前元素returnthis.each(function(){varself=jQuery(this),contents=self.contents();if(contents.length){contents.wrapAll(html);}else{self.append(html);}});},wrap: function(html){varisFunction=jQuery.isFunction(html);// 在匹配元素的每个元素执行wrapAllreturnthis.each(function(i){jQuery(this).wrapAll(isFunction ? html.call(this,i) : html);});},unwrap: function(){// 为每个匹配元素的父元素执行replaceWidth, 用子元素直接替代它,就相当于解除包裹了returnthis.parent().each(function(){if(!jQuery.nodeName(this,"body")){jQuery(this).replaceWith(this.childNodes);}}).end();},append: function(){// 调用domManip完成dom的转换,然后再执行appendChild完成插入操作returnthis.domManip(arguments,true,function(elem){if(this.nodeType===1){this.appendChild(elem);}});},prepend: function(){// 完成在头部插入returnthis.domManip(arguments,true,function(elem){if(this.nodeType===1){this.insertBefore(elem,this.firstChild);}});},before: function(){// 在指定元素前面插入元素if(this[0]&&this[0].parentNode){returnthis.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this);});}elseif(arguments.length){varset=jQuery.clean(arguments);set.push.apply(set,this.toArray());returnthis.pushStack(set,"before",arguments);}},after: function(){// 在指定元素的后方插入元素if(this[0]&&this[0].parentNode){returnthis.domManip(arguments,false,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});}elseif(arguments.length){varset=this.pushStack(this,"after",arguments);set.push.apply(set,jQuery.clean(arguments));returnset;}},// keepData is for internal use only--do not document// 先移除后代元素和关联的数据和事件,以防止内存泄漏remove: function(selector,keepData){for(vari=0,elem;(elem=this[i])!=null;i++){if(!selector||jQuery.filter(selector,[elem]).length){if(!keepData&&elem.nodeType===1){jQuery.cleanData(elem.getElementsByTagName("*"));jQuery.cleanData([elem]);}if(elem.parentNode){elem.parentNode.removeChild(elem);}}}returnthis;},// 移除所有后代// 也要先移除数据和事件// 不过最后有个监测措施empty: function(){for(vari=0,elem;(elem=this[i])!=null;i++){// Remove element nodes and prevent memory leaksif(elem.nodeType===1){jQuery.cleanData(elem.getElementsByTagName("*"));}// Remove any remaining nodeswhile(elem.firstChild){elem.removeChild(elem.firstChild);}}returnthis;},// 负责决定两个参数,是否复制元素本身的数据和事件// 是否复制后代的元素和事件clone: function(dataAndEvents,deepDataAndEvents){dataAndEvents=dataAndEvents==null ? false : dataAndEvents;deepDataAndEvents=deepDataAndEvents==null ? dataAndEvents : deepDataAndEvents;returnthis.map(function(){returnjQuery.clone(this,dataAndEvents,deepDataAndEvents);});},// rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,html: function(value){// 如果没有传入值,那么表示读取第一个元素innerHTMLif(value===undefined){returnthis[0]&&this[0].nodeType===1 ?
this[0].innerHTML.replace(rinlinejQuery,"") :
null;// See if we can take a shortcut and just use innerHTML// 在不需要任何修正的情况下}elseif(typeofvalue==="string"&&!rnoInnerhtml.test(value)&&(jQuery.support.leadingWhitespace||!rleadingWhitespace.test(value))&&!wrapMap[(rtagName.exec(value)||["",""])[1].toLowerCase()]){value=value.replace(rxhtmlTag,"<$1></$2>");try{for(vari=0,l=this.length;i<l;i++){// Remove element nodes and prevent memory leaks// 遍历当前所有匹配元素,尝试把内部的所有元素的数据缓存都删掉,然后重设他们的innerHTMLif(this[i].nodeType===1){jQuery.cleanData(this[i].getElementsByTagName("*"));this[i].innerHTML=value;}}// If using innerHTML throws an exception, use the fallback method}catch(e){// 前面出了问题,直接调用empty方法,然后再插入新值this.empty().append(value);}}elseif(jQuery.isFunction(value)){// 如果发现value是函数,则each为每个匹配元素调用html方法this.each(function(i){varself=jQuery(this);self.html(value.call(this,i,self.html()));});}else{this.empty().append(value);}returnthis;},// 替换元素replaceWith: function(value){// 当前有元素且元素有父元素if(this[0]&&this[0].parentNode){// Make sure that the elements are removed from the DOM before they are inserted// this can help fix replacing a parent with child elements// 如果value是函数 则便利当前匹配元素,继续用value执行后的结果调用replaceWithif(jQuery.isFunction(value)){returnthis.each(function(i){varself=jQuery(this),old=self.html();self.replaceWith(value.call(this,i,old));});}// 如果value不是方法、不是字符串可能是dom活着jQuery对象,则先把value从文档中移除掉if(typeofvalue!=="string"){value=jQuery(value).detach();}// 先把元素移除掉,再用value插入、可能是通过后面兄弟前面插入、也可能是通过父亲直接最后插入returnthis.each(function(){varnext=this.nextSibling,parent=this.parentNode;jQuery(this).remove();if(next){jQuery(next).before(value);}else{jQuery(parent).append(value);}});}else{// 有length表示有匹配元素,但第一个元素没有父元素,执行this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value )// 否则直接意味着当前没有匹配元素 直接返回this本身// 第一种情况,直接拿value构建一个新的jQuery对象返回returnthis.length ?
this.pushStack(jQuery(jQuery.isFunction(value) ? value() : value),"replaceWith",value) :
this;}},detach: function(selector){returnthis.remove(selector,true);},// 被多个插入方法调用的基本方法// 就是转换dom元素,并且调用真正的回调函数,把转换后的dom元素插入进去// checked="checked" or checked// rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, domManip: function(args,table,callback){varresults,first,fragment,parent,value=args[0],scripts=[];// 浏览器能否正常复制含有checked参数的dom元素// 迭代执行来解决这个问题if(!jQuery.support.checkClone&&arguments.length===3&&typeofvalue==="string"&&rchecked.test(value)){returnthis.each(function(){jQuery(this).domManip(args,table,callback,true);});}// 如果发现args数组内是函数,则遍历当前匹配的元素集合,将执行后的数据继续执行domManipif(jQuery.isFunction(value)){returnthis.each(function(i){varself=jQuery(this);args[0]=value.call(this,i,table ? self.html() : undefined);self.domManip(args,table,callback);});}if(this[0]){parent=value&&value.parentNode;// If we're in a fragment, just use that instead of building a new one// 这里有个坑, jQuery.support没有测试parentNode, 所以这个if永远不会执行if(jQuery.support.parentNode&&parent&&parent.nodeType===11&&parent.childNodes.length===this.length){results={fragment: parent};}else{// 调用buildFragment将字符串转为dom元素// 顺便把script提取出来了results=jQuery.buildFragment(args,this,scripts);}fragment=results.fragment;if(fragment.childNodes.length===1){first=fragment=fragment.firstChild;}else{first=fragment.firstChild;}if(first){// 查看当前元素是不是trtable=table&&jQuery.nodeName(first,"tr");for(vari=0,l=this.length,lastIndex=l-1;i<l;i++){callback.call(table ?
root(this[i],first) :
this[i],// Make sure that we do not leak memory by inadvertently discarding// the original fragment (which might have attached data) instead of// using it; in addition, use the original fragment object for the last// item instead of first because it can end up being emptied incorrectly// in certain situations (Bug #8070).// Fragments from the fragment cache must always be cloned and never used// in place.// 如果该dom是可缓存的,则总是插入它的副本// 如果当前含有多个匹配的dom元素,则前面插入副本,最后一个插入本身results.cacheable||(l>1&&i<lastIndex) ?
jQuery.clone(fragment,true,true) :
fragment);}}// 如果有提取到script标签,则执行该标签if(scripts.length){jQuery.each(scripts,evalScript);}}returnthis;}});// 修正table的tbody,// 返回tbody元素functionroot(elem,cur){returnjQuery.nodeName(elem,"table") ?
(elem.getElementsByTagName("tbody")[0]||elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
elem;}
jQuery.extend({clone: function(elem,dataAndEvents,deepDataAndEvents){varsrcElements,destElements,i,// rnoshimcache = new RegExp("<(?:" + nodeNames + ")", "i"),// 浏览器支持html5元素 或者不包含html5元素 调用原生的cloneNode、否则调用shimCloneNode// IE<=8 does not properly clone detached, unknown element nodesclone=jQuery.support.html5Clone||!rnoshimcache.test("<"+elem.nodeName) ?
elem.cloneNode(true) :
shimCloneNode(elem);// 如果浏览器不支持事件复制、或者不能正确复制checked状态// 则要借助cloneFixAttributes方法if((!jQuery.support.noCloneEvent||!jQuery.support.noCloneChecked)&&(elem.nodeType===1||elem.nodeType===11)&&!jQuery.isXMLDoc(elem)){// IE copies events bound via attachEvent when using cloneNode.// Calling detachEvent on the clone will also remove the events// from the original. In order to get around this, we use some// proprietary methods to clear the events. Thanks to MooTools// guys for this hotness.cloneFixAttributes(elem,clone);// Using Sizzle here is crazy slow, so we use getElementsByTagName instead// getAll = getElementsByTagName || querySelectorAllsrcElements=getAll(elem);destElements=getAll(clone);// Weird iteration because IE will replace the length property// with an element if you are cloning the body and one of the// elements on the page has a name or id of "length"// 为每一个字元素都执行cloneFixAttributes修正方法for(i=0;srcElements[i];++i){// Ensure that the destination node is not null; Fixes #9587if(destElements[i]){cloneFixAttributes(srcElements[i],destElements[i]);}}}// Copy the events from the original to the clone// 复制事件 按需求 是否深层复制事件// cloneCopyEventif(dataAndEvents){cloneCopyEvent(elem,clone);if(deepDataAndEvents){srcElements=getAll(elem);destElements=getAll(clone);for(i=0;srcElements[i];++i){cloneCopyEvent(srcElements[i],destElements[i]);}}}srcElements=destElements=null;// Return the cloned setreturnclone;},});
cloneFixAttributes 方法
functioncloneFixAttributes(src,dest){varnodeName;// We do not need to do anything for non-Elementsif(dest.nodeType!==1){return;}// clearAttributes\mergeAttributes仅在IE678下支持, 会清除掉元素的属性和attachEvent的事件,但mergeAttributes只会把属性复制,不会复制事件// clearAttributes removes the attributes, which we don't want,// but also removes the attachEvent events, which we *do* wantif(dest.clearAttributes){dest.clearAttributes();}// mergeAttributes, in contrast, only merges back on the// original attributes, not the eventsif(dest.mergeAttributes){dest.mergeAttributes(src);}nodeName=dest.nodeName.toLowerCase();// IE6-8 fail to clone children inside object elements that use// the proprietary classid attribute value (rather than the type// attribute) to identify the type of content to display// 如果一个元素师object,那么直接原封的文本复制即可if(nodeName==="object"){dest.outerHTML=src.outerHTML;}elseif(nodeName==="input"&&(src.type==="checkbox"||src.type==="radio")){// 如果是input、cehcekbox、radio则复制其checked、value// IE6-8 fails to persist the checked state of a cloned checkbox// or radio button. Worse, IE6-7 fail to give the cloned element// a checked appearance if the defaultChecked value isn't also setif(src.checked){dest.defaultChecked=dest.checked=src.checked;}// IE6-7 get confused and end up setting the value of a cloned// checkbox/radio button to an empty string instead of "on"if(dest.value!==src.value){dest.value=src.value;}// IE6-8 fails to return the selected option to the default selected// state when cloning options// 修正option的selected状态}elseif(nodeName==="option"){dest.selected=src.defaultSelected;// IE6-8 fails to set the defaultValue to the correct value when// cloning other types of input fields}elseif(nodeName==="input"||nodeName==="textarea"){dest.defaultValue=src.defaultValue;}// Event data gets referenced instead of copied if the expando// gets copied toodest.removeAttribute(jQuery.expando);}
cloneCopyEvent 方法
functioncloneCopyEvent(src,dest){// 检查src元素是否有事件if(dest.nodeType!==1||!jQuery.hasData(src)){return;}// 先将原始数据缓存复制到目标元素上vartype,i,l,oldData=jQuery._data(src),curData=jQuery._data(dest,oldData),events=oldData.events;// 如果原始数据中有事件数据// 先将目标元素上面的事件数据清空掉if(events){deletecurData.handle;curData.events={};// 然后将原始数据中的事件一个一个遍历添加到新的目标元素上for(typeinevents){for(i=0,l=events[type].length;i<l;i++){jQuery.event.add(dest,type+(events[type][i].namespace ? "." : "")+events[type][i].namespace,events[type][i],events[type][i].data);}}}// make the cloned public data object a copy from the originalif(curData.data){curData.data=jQuery.extend({},curData.data);}}
chapter10 DOM操作
对DOM的操作在jQuery中有插入、删除、复制、替换、包裹元素五类,基于原生的insertBefore、appendChild、removeChild、cloneNode4个方法,其中replaceChild没有用到,替换元素则是基于已经实现的删除与插入来完成。
DOM操作部分代码并不是特别复杂,只有插入那块用到了之前分析过的一两段复杂的构建html的代码,复制部分需要对兼容性修正,其余部分都是利用原生方法封装为更方便易用的接口。
cloneFixAttributes 方法
cloneCopyEvent 方法
The text was updated successfully, but these errors were encountered: