jquery源码系列:append方法实现过程

no1:

// Define a local copy of jQuery
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );  //调用第二步init方法
},

no2:

jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
var match, elem, ret, doc;

// Handle $(""), $(null), or $(undefined)
if ( !selector ) {
return this;
}

// Handle $(DOMElement)
if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
}

// The body element only exists once, optimize finding it
if ( selector === "body" && !context && document.body ) {
this.context = document;
this[0] = document.body;
this.selector = selector;
this.length = 1;
return this;
}

// Handle HTML strings
if ( typeof selector === "string" ) {
// Are we dealing with HTML string or an ID?
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];

} else {
match = quickExpr.exec( selector );
}

// Verify a match, and that no context was specified for #id
if ( match && (match[1] || !context) ) {

// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
doc = ( context ? context.ownerDocument || context : document );

// If a single string is passed in and it's a single tag
// just do a createElement and skip the rest
ret = rsingleTag.exec( selector );

if ( ret ) {
if ( jQuery.isPlainObject( context ) ) {
selector = [ document.createElement( ret[1] ) ];
jQuery.fn.attr.call( selector, context, true );

} else {
selector = [ doc.createElement( ret[1] ) ];
}

} else {
ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
}

return jQuery.merge( this, selector );

// HANDLE: $("#id")
} else {
elem = document.getElementById( match[2] );

// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootjQuery.find( selector );
}

// Otherwise, we inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
}

this.context = document;
this.selector = selector;
return this;
}

// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector );

// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}

// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}

if ( selector.selector !== undefined ) {
this.selector = selector.selector;
this.context = selector.context;
}

return jQuery.makeArray( selector, this );
},

// Start with an empty selector
selector: "",

// The current version of jQuery being used
jquery: "1.7.1",

// The default length of a jQuery object is 0
length: 0,

// The number of elements contained in the matched element set
size: function() {
return this.length;
},

toArray: function() {
return slice.call( this, 0 );
},

// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get: function( num ) {
return num == null ?

// Return a 'clean' array
this.toArray() :

// Return just the object
( num < 0 ? this[ this.length + num ] : this[ num ] );
},

// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
var ret = this.constructor();

if ( jQuery.isArray( elems ) ) {
push.apply( ret, elems );

} else {
jQuery.merge( ret, elems );
}

// Add the old object onto the stack (as a reference)
ret.prevObject = this;

ret.context = this.context;

if ( name === "find" ) {
ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
} else if ( name ) {
ret.selector = this.selector + "." + name + "(" + selector + ")";
}

// Return the newly-formed element set
return ret;
},

// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
each: function( callback, args ) {
return jQuery.each( this, callback, args );
},

ready: function( fn ) {
// Attach the listeners
jQuery.bindReady();

// Add the callback
readyList.add( fn );

return this;
},

eq: function( i ) {
i = +i;
return i === -1 ?
this.slice( i ) :
this.slice( i, i + 1 );
},

first: function() {
return this.eq( 0 );
},

last: function() {
return this.eq( -1 );
},

slice: function() {
return this.pushStack( slice.apply( this, arguments ),
"slice", slice.call(arguments).join(",") );
},

map: function( callback ) {
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
},

end: function() {
return this.prevObject || this.constructor(null);
},

// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: push,
sort: [].sort,
splice: [].splice
};

no3: 进入append入口

jQuery.fn.extend({
text: function( text ) {
if ( jQuery.isFunction(text) ) {
return this.each(function(i) {
var self = jQuery( this );

self.text( text.call(this, i, self.text()) );
});
}

if ( typeof text !== "object" && text !== undefined ) {
return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
}

return jQuery.text( this );
},

wrapAll: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapAll( html.call(this, i) );
});
}

if ( this[0] ) {
// The elements to wrap the target around
var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);

if ( this[0].parentNode ) {
wrap.insertBefore( this[0] );
}

wrap.map(function() {
var elem = this;

while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
elem = elem.firstChild;
}

return elem;
}).append( this );
}

return this;
},

wrapInner: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapInner( html.call(this, i) );
});
}

return this.each(function() {
var self = jQuery( this ),
contents = self.contents();

if ( contents.length ) {
contents.wrapAll( html );

} else {
self.append( html );
}
});
},

wrap: function( html ) {
var isFunction = jQuery.isFunction( html );

return this.each(function(i) {
jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
});
},

unwrap: function() {
return this.parent().each(function() {
if ( !jQuery.nodeName( this, "body" ) ) {
jQuery( this ).replaceWith( this.childNodes );
}
}).end();
},

append: function() {

return this.domManip(arguments, true, function( elem ) {

if ( this.nodeType === 1 ) {
this.appendChild( elem );
}
});
},

prepend: function() {
return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 ) {
this.insertBefore( elem, this.firstChild );
}
});
},

before: function() {
if ( this[0] && this[0].parentNode ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this );
});
} else if ( arguments.length ) {
var set = jQuery.clean( arguments );
set.push.apply( set, this.toArray() );
return this.pushStack( set, "before", arguments );
}
},

after: function() {
if ( this[0] && this[0].parentNode ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this.nextSibling );
});
} else if ( arguments.length ) {
var set = this.pushStack( this, "after", arguments );
set.push.apply( set, jQuery.clean(arguments) );
return set;
}
},

// keepData is for internal use only--do not document
remove: function( selector, keepData ) {
for ( var i = 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 );
}
}
}

return this;
},

empty: function() {
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
}

// Remove any remaining nodes
while ( elem.firstChild ) {
elem.removeChild( elem.firstChild );
}
}

return this;
},

clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},

html: function( value ) {
if ( value === undefined ) {
return this[0] && this[0].nodeType === 1 ?
this[0].innerHTML.replace(rinlinejQuery, "") :
null;

// See if we can take a shortcut and just use innerHTML
} else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {

value = value.replace(rxhtmlTag, "<$1></$2>");

try {
for ( var i = 0, l = this.length; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
if ( 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) {
this.empty().append( value );
}

} else if ( jQuery.isFunction( value ) ) {
this.each(function(i){
var self = jQuery( this );

self.html( value.call(this, i, self.html()) );
});

} else {
this.empty().append( value );
}

return this;
},

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
if ( jQuery.isFunction( value ) ) {
return this.each(function(i) {
var self = jQuery(this), old = self.html();
self.replaceWith( value.call( this, i, old ) );
});
}

if ( typeof value !== "string" ) {
value = jQuery( value ).detach();
}

return this.each(function() {
var next = this.nextSibling,
parent = this.parentNode;

jQuery( this ).remove();

if ( next ) {
jQuery(next).before( value );
} else {
jQuery(parent).append( value );
}
});
} else {
return this.length ?
this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
this;
}
},

detach: function( selector ) {
return this.remove( selector, true );
},

domManip: function( args, table, callback ) {
var results, first, fragment, parent,
value = args[0],
scripts = [];

// We can't cloneNode fragments that contain checked, in WebKit
if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
return this.each(function() {
jQuery(this).domManip( args, table, callback, true );
});
}

if ( jQuery.isFunction(value) ) {
return this.each(function(i) {
var self = 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
if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
results = { fragment: parent };

} else {
results = jQuery.buildFragment( args, this, scripts );  //构造一个新实例
}

fragment = results.fragment;

if ( fragment.childNodes.length === 1 ) {
first = fragment = fragment.firstChild;
} else {
first = fragment.firstChild;
}

if ( first ) {
table = table && jQuery.nodeName( first, "tr" );

for ( var i = 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.
results.cacheable || ( l > 1 && i < lastIndex ) ?
jQuery.clone( fragment, true, true ) :
fragment
);
}
}

if ( scripts.length ) {
jQuery.each( scripts, evalScript );
}
}

return this;
}
});

//buildFragment 方法

jQuery.buildFragment = function( args, nodes, scripts ) {
var fragment, cacheable, cacheresults, doc,
first = args[ 0 ];

// nodes may contain either an explicit document object,
// a jQuery collection or context object.
// If nodes[0] contains a valid object to assign to doc
if ( nodes && nodes[0] ) {
doc = nodes[0].ownerDocument || nodes[0];
}

// Ensure that an attr object doesn't incorrectly stand in as a document object
// Chrome and Firefox seem to allow this to occur and will throw exception
// Fixes #8950
if ( !doc.createDocumentFragment ) {
doc = document;
}

// Only cache "small" (1/2 KB) HTML strings that are associated with the main document
// Cloning options loses the selected state, so don't cache them
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
// Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
first.charAt(0) === "<" && !rnocache.test( first ) &&
(jQuery.support.checkClone || !rchecked.test( first )) &&
(jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {

cacheable = true;

cacheresults = jQuery.fragments[ first ];
if ( cacheresults && cacheresults !== 1 ) {
fragment = cacheresults;
}
}

if ( !fragment ) {
fragment = doc.createDocumentFragment();
jQuery.clean( args, doc, fragment, scripts );
}

if ( cacheable ) {
jQuery.fragments[ first ] = cacheresults ? fragment : 1;
}

return { fragment: fragment, cacheable: cacheable };
};

时间: 2024-10-21 00:06:38

jquery源码系列:append方法实现过程的相关文章

jQuery源码解读之removeAttr()方法分析

 这篇文章主要介绍了jQuery源码解读之removeAttr()方法分析,较为详细的分析了removeAttr方法的实现技巧,非常具有实用价值,需要的朋友可以参考下     本文较为详细的分析了jQuery源码解读之removeAttr()方法.分享给大家供大家参考.具体分析如下: 扩展jQuery原型对象的方法: 代码如下: jQuery.fn.extend({ //name,传入要DOM元素要移除的属性名. removeAttr: function( name ) {   //使用jQue

jQuery源码解读之hasClass()方法分析

 这篇文章主要介绍了jQuery源码解读之hasClass()方法,以注释形式较为详细的分析了hasClass()方法的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下     本文较为详细的分析了jQuery源码解读之hasClass()方法.分享给大家供大家参考.具体分析如下:   代码如下: jQuery.fn.extend({ hasClass: function( selector ) { //将要检查的类名selector赋值给className, l为选择器选择的当前要检查的j

jQuery源码解读之addClass()方法分析

 这篇文章主要介绍了jQuery源码解读之addClass()方法,注释形式较为详细的分析了addClass()方法的实现技巧与相关注意事项,具有一定参考借鉴价值,需要的朋友可以参考下     本文较为详细的分析了jQuery源码解读之addClass()方法.分享给大家供大家参考.具体分析如下: 给jQuery原型对象扩展addClass功能,jQuery.fn就是jQuery.prototype 代码如下: jQuery.fn.extend({ /* 可以看出这是一个函数名叫addClass

jQuery源码解读之removeClass()方法分析

 这篇文章主要介绍了jQuery源码解读之removeClass()方法,以注释形式较为详细的分析了removeClass()方法的实现技巧与使用注意事项,需要的朋友可以参考下     本文较为详细的分析了jQuery源码解读之removeClass()方法.分享给大家供大家参考.具体分析如下: removeClass()方法和addClass()差别不大.这就来看看: 代码如下: jQuery.fn.extend({ removeClass: function( value ) { var c

jQuery源码解读之hasClass()方法分析_jquery

本文较为详细的分析了jQuery源码解读之hasClass()方法.分享给大家供大家参考.具体分析如下: 复制代码 代码如下: jQuery.fn.extend({     hasClass: function( selector ) { //将要检查的类名selector赋值给className, l为选择器选择的当前要检查的jQuery对象数组的长度.         var className = " " + selector + " ",          

jQuery源码解读之removeClass()方法分析_jquery

本文较为详细的分析了jQuery源码解读之removeClass()方法.分享给大家供大家参考.具体分析如下: removeClass()方法和addClass()差别不大.这就来看看: 复制代码 代码如下: jQuery.fn.extend({     removeClass: function( value ) {         var classes, elem, cur, clazz, j, finalValue,             i = 0,             len

jQuery源码解读之addClass()方法分析_jquery

本文较为详细的分析了jQuery源码解读之addClass()方法.分享给大家供大家参考.具体分析如下: 给jQuery原型对象扩展addClass功能,jQuery.fn就是jQuery.prototype 复制代码 代码如下: jQuery.fn.extend({ /* 可以看出这是一个函数名叫addClass的插件方法. */     addClass: function( value ) {         var classes, elem, cur, clazz, j, finalV

jQuery源码解读之removeAttr()方法分析_jquery

本文较为详细的分析了jQuery源码解读之removeAttr()方法.分享给大家供大家参考.具体分析如下: 扩展jQuery原型对象的方法: 复制代码 代码如下: jQuery.fn.extend({ //name,传入要DOM元素要移除的属性名.     removeAttr: function( name ) { //使用jQuery.fn对象,即jQuery原型对象的each方法遍历当前选择器选择的jQuery对象数组,并返回该jQuery对象以便链式调用.         return

jQuery源码-美元$的若干种使用方法

本文还可在github上查看,可点击这里~~~~ jQuery源码-美元$的若干种使用方法 学习jQuery源码,第一步是了解jQuery整体核心代码结构.第二步,当然就是了解无比强大无所不能的美元$.根据平常使用jQuery的经验,你会发现,几乎所有的语句都是以美元开头的,比如: $(function(){ console.log('dom ready 啦!!'); }); 又比如: $('#casper').addClass('handsome'); 当然还有其他...翻开jQuery的源码