您的位置:首页 > 移动开发

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

2015-05-31 23:06 393 查看
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 };

};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: