Jquery-1.9.1 source code analysis series (11) DOM operations, jquery-1.9.1dom

Source: Internet
Author: User
Tags delete cache

Jquery-1.9.1 source code analysis series (11) DOM operations, jquery-1.9.1dom

DOM operations include append, prepend, before, after, replaceWith, appendTo, prependTo, insertBefore, insertAfter, and replaceAll. Its core processing function is domManip.

The last five methods in DOM operation functions are still the first five methods. Source Code:

JQuery. each ({appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith"}, function (name, original) {jQuery. fn [name] = function (selector) {var elems, I = 0, ret = [], insert = jQuery (selector), last = insert. length-1; for (; I <= last; I ++) {elems = I === last? This: this. clone (true); jQuery (insert [I]) [original] (elems); // modern browsers call apply to treat jQuery objects as arrays, but older ie versions need to be used. get () core_push.apply (ret, elems. get ();} return this. pushStack (ret );};});

  There are two native insert nodes in the browser: appendChild and inserBefore. jQuery expands the following methods by using these two methods:

JQuery. fn. append use this. appendChild (elem)

JQuery. fn. prepend use this. insertBefore (elem, this. firstChild)

JQuery. fn. before use this. parentNode. insertBefore (elem, this );

JQuery. fn. after use this. parentNode. insertBefore (elem, this. nextSibling );

JQuery. fn. replaceWith use this. parentNode. insertBefore (elem, this. nextSibling );

Let's look at the source code of an example (jQuery. fn. append)

  append: function() {      return this.domManip(arguments, true, function( elem ) {        if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {          this.appendChild( elem );        }      });    }

According to the source code above. Guess that domManip is used to traverse the elements matching the current jQuery object, and then each element calls the passed callback and inserts the node (if it is a string, you need to create a File Fragment node) as the input callback parameter, and execute the input callback.

Next, analyze domManip to see if the guess is correct. Dom is a Dom element. Manip is the abbreviation of Manipulate. the literal meaning of a combination is Dom operation.

A. domManip: function (args, table, callback) Parsing

The DOM element or HTML code to be inserted by args.

Whether the table needs to modify the tbody. This variable is the result of optimization.

Callback function. The execution format is callback. call (the target element is the context and the File Fragment to be inserted/a single DOM element)

First look at the process, then look at the details

Step 1: Initialize the variable. INoClone will be used later. If the current jQuery object matches more than one element (n> 1), it means that the constructed file fragments need to be used by n, the object needs to be cloned (n-1) times and added to the fragment file itself for n times. value is the first element of the first args parameter, and special processing will be performed on the value as a function;

var first, node, hasScripts,  scripts, doc, fragment,  i = 0,  l = this.length,  set = this,  iNoClone = l - 1,  value = args[0],  isFunction = jQuery.isFunction( value );

Step 2: In special cases, call domManip one by one for elements matching the current jQuery object. There are two special cases: first, if the input node is a function (that is, the value is a function) then, each element of the current jQuery object must use the value calculated by the function as a node to be substituted into domManip for processing. Second, in webkit, we cannot clone file fragments containing checked. cloned documents cannot be reused. Therefore, only domManip can be called once for each element matching the current jQuery object.

// In webkit, we cannot clone file fragments containing checked if (isFunction |! (L <= 1 | typeof value! = "String" | jQuery. support. checkClone |! Rchecked. test (value) {return this. each (function (index) {var self = set. eq (index); // if args [0] is a function, the execution function returns the result to replace the original args [0] if (isFunction) {args [0] = value. call (this, index, table? Self.html (): undefined);} self. domManip (args, table, callback );});}

Step 3: Normally, use the input node to build the file fragments and insert them into the file. The document fragments constructed here need to be reused, which is different from the second step. Note that the script node must be executed after loading. Check the process in the source code order.

  Build document fragments

fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );first = fragment.firstChild;if ( fragment.childNodes.length === 1 ) {  fragment = first;}

The script is separated. One of these functions changes the type value of the script tag to ensure security. The original type value is "text/javascript ", changed to "true/text/javascript" or "false/text/javascript"

scripts = jQuery.map( getAll( fragment, "script" ), disableScript );hasScripts = scripts.length;

Document fragment insert page

for ( ; i < l; i++ ) {  node = fragment;  if ( i !== iNoClone ) {    node = jQuery.clone( node, true, true );    // Keep references to cloned scripts for later restoration    if ( hasScripts ) {      jQuery.merge( scripts, getAll( node, "script" ) );    }  }  callback.call(    table && jQuery.nodeName( this[i], "table" ) ?    findOrAppend( this[i], "tbody" ) :    this[i],    node,    i    );}

Execute scripts in two cases: Remote ajax processing and local direct execution.

If (hasScripts) {doc = scripts [scripts. length-1]. ownerDocument; // Reenable scripts jQuery. map (scripts, restoreScript); // Insert the first file to execute the executable script for (I = 0; I 

B. dom operation Expansion

JQuery. fn. text

jQuery.fn.text: function( value ) {  return jQuery.access( this, function( value ) {    return value === undefined ?    jQuery.text( this ) :    this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );  }, null, value, arguments.length );}

Final execution value === undefined? JQuery. text (this): this. empty (). append (this [0] & this [0]. ownerDocument | document). createTextNode (value ));

JQuery. text = Sizzle. getText;

JQuery.fn.html

Functions are processed using jQuery. access.

  jQuery.fn.html: function( value ) {      return jQuery.access( this, function( value ) {...}, null, value, arguments.length );    }

If no parameter exists, it indicates a value.

if ( value === undefined ) {  return elem.nodeType === 1 ?  elem.innerHTML.replace( rinlinejQuery, "" ) :  undefined;}

Otherwise, check whether the content can be added with innerHTML. Click to see compatibility issues

// Check whether we can take a shortcut. You only need to use innerHTML // The code script to be executed | style | link and so on. innerHTML // htmlSerialize cannot be used: make sure that the link node can be correctly serialized using innerHTML, which must be used in IE's packaging element // leadingWhitespace: IE strips. innerHTML must start with a blank space. // if (typeof value = "string "&&! RnoInnerhtml. test (value) & (jQuery.support.html Serialize |! Rnoshimcache. test (value) & (jQuery. support. leadingWhitespace |! RleadingWhitespace. test (value ))&&! WrapMap [(rtagName.exe c (value) | ["", ""]) [1]. toLowerCase ()]) {value = value. replace (rxhtmlTag, "<$1> </$2>"); try {for (; I <l; I ++) {// remove element nodes and cache, prevent Memory leakage elem = this [I] |{}; if (elem. nodeType = 1) {jQuery. cleanData (getAll (elem, false); elem. innerHTML = value ;}} elem = 0; // if an exception is thrown using innerHTML, use the alternative method} catch (e ){}}

If you cannot use innerHTML or fail to use it (throwing an exception), use the alternative append method.

// Use the append method to add the node if (elem) {this. empty (). append (value);} jQuery. fn. wrapAll (wrap all matching elements with a single tag) process: if the input parameter is a function, the function result is passed in if (jQuery. isFunction (html) {return this. each (function (I) {jQuery (this ). wrapAll (html. call (this, I) ;}}create a package layer // obtain The package tag the elements to wrap The target aroundvar wrap = jQuery (html, this [0]. ownerDocument ). eq (0 ). clone (true); if (this [0]. parentNode) {wrap. insertBefore (this [0]);}

Wrap the current jQuery object with a package

wrap.map(function() {  var elem = this;  while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {    elem = elem.firstChild;  }   return elem;}).append( this );

Note: currently, it is best to have only one element to match with jQuery objects. If there are multiple elements, this is not recommended. In this case, use it with caution. You can see it in the following example.

In a simple example, the original DOM is (this example is used later)

<Div id = 'center' class = "center"> <div id = 'ss' class = "center"> <input type = 'submit 'id = 'left' class = "left"> </div> <div class = "right"> I am right </div> $ ('# Center '). after wrapAll ("<p> </p>, dom becomes <p> <div id = "center" class = "center"> <div id = "ss" class = "center"> <input type = "submit" id = "left" class = "left"> </div> </p> <div class = "right"> I am right </div>

Use with caution: If jQuery matches more than one element, for example, if the original DOM executes $ ('div '). wrapAll ("<p> </p>"), the result DOM becomes

<P> <div id = "center" class = "center"> </div> <div id = "ss" class = "center"> <input type = "submit" id = "left" class = "left"> </div> <div class = "right"> I am right </div> </p>

Now we can see the result. # center is # ss's parent node, and the result is changed to # ss's sibling node.

JQuery. fn. wrapInner (specify the HTML structure for external packages on all child nodes of each matching element)

Procedure:

If the input parameter is a function, the function result is passed in.

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

Traverse the array of jQuery objects, get the contents of each element (all child nodes), and wrap the contents with warpAll.

return this.each(function() {  var self = jQuery( this ),  contents = self.contents();  if ( contents.length ) {    contents.wrapAll( html );  } else {    self.append( html );  }});

Or use the original DOM in the above example, execute $ ('div '). wrapInner (' <p> </p> ') and the result DOM becomes

<Div id = "center" class = "center"> <p> <div id = "ss" class = "center"> <p> <input type = "submit" id = "left" class = "left"> </p> </div> <div class = "right"> <p> I am right </p> </div>

JQuery. fn. wrap (specify the HTML structure in the external package of each matching element)

Use wrapAll to wrap each element of jQuery.

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

Row $ ('div '). wrap (' <p> </p> ') and the result DOM becomes

<P> <div id = "center" class = "center"> <p> <div id = "ss" class = "center"> <input type = "submit" id = "left" class = "left"> </div> </p> <div class = "right"> I am right </div> </p>

JQuery. fn. unwrap (remove the parent element of each matching element)

ReplaceWith is used to replace the parent node of the matching element with all the child nodes of the matching element parent node. Of course, the parent node is body/html/document and cannot be removed.

Return this. parent (). each (function () {if (! JQuery. nodeName (this, "body") {jQuery (this ). replaceWith (this. childNodes );}}). end (); run $ ('div '). wrap () the DOM becomes <div id = "ss" class = "center"> <input type = "submit" id = "left" class = "left"> </div> <div class = "right"> I am right </div>
  

JQuery. fn. remove (remove matching elements from the document)

You can also use the selector to further narrow down the removal scope and only remove some elements that match the specified selector in the current matching element.

Compared with detach (), the remove () function removes the additional data () function) and event processor (detach () associated with the element) at the same time ).

For (; (elem = this [I])! = Null; I ++) {if (! Selector | jQuery. filter (selector, [elem]). length> 0) {// The keepData parameter passed in by detach is true. if (! KeepData & elem. nodeType = 1) {// clear the cache jQuery. cleanData (getAll (elem);} if (elem. parentNode) {if (keepData & jQuery. contains (elem. ownerDocument, elem) {setGlobalEval (getAll (elem, "script");} elem. parentNode. removeChild (elem );}}}

We can see that there is an important function cleanData, which is used to clear the cache: traverse each node element and process each node element:

1. Get the cache corresponding to the current element

id = elem[ internalKey ];data = id && cache[ id ];

2. If there is a binding event, traverse the unbinding event

If (data. events) {for (type in data. events) {if (special [type]) {jQuery. event. remove (elem, type); // This is a shortcut to avoid jQuery. event. remove overhead} else {jQuery. removeEvent (elem, type, data. handle );}}}

3. If jQuery. event. remove does not remove the cache, manually remove the cache. IE requires some compatibility processing, and will eventually save the deletion history as in core_deletedIds.

// When jQuery. event. when the remove operation does not remove the cache, remove cacheif (cache [id]) {delete cache [id]; // IE does not allow the use of delete to delete expando features from the node, // The removeAttribute function can also be used for file nodes; // we must handle all these cases, if (deleteExpando) {delete elem [internalKey];} else if (typeof elem. removeAttribute! ==Core_strundefined) {elem. removeAttribute (internalKey);} else {elem [internalKey] = null;} core_deletedIds.push (id );}

JQuery. fn. detach

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

JQuery. fn. empty (clear all content in each Matching Element (all subnodes ))

The function removes all child nodes (including text nodes, comment nodes, and Other types) that match the element and clears the corresponding cache data.

For (; (elem = this [I])! = Null; I ++) {// prevents Memory leakage remove Element Node cache if (elem. nodeType = 1) {jQuery. cleanData (getAll (elem, false);} // remove all child nodes while (elem. firstChild) {elem. removeChild (elem. firstChild);} // IE <9, the select node needs to leave option empty if (elem. options & jQuery. nodeName (elem, "select") {elem. options. length = 0 ;}}

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.