Jquery-1.9.1 Source Analysis Series (11) Dom Operation _jquery

Source: Internet
Author: User
Tags wrapper delete cache

Dom operations include append, Prepend, before, after, ReplaceWith, Appendto, Prependto, InsertBefore, InsertAfter, ReplaceAll. Its core processing function is Dommanip.

The last five methods used in the DOM operator function are still the first five methods, source

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 browser invoke apply will take the jquery object as an array, but older versions of IE need to use. Get ()
        core_push.apply (ret, elems.get ());
      }
      Return This.pushstack (ret);
    };
  

  There are two ways in which browsers native insert nodes: AppendChild and Inserbefore,jquery Use these two methods to expand the following 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);

Look at an example of the source code (jQuery.fn.append)

  Append:function () {return
      This.dommanip (arguments, True, function (elem) {
        if (This.nodetype = 1 | | this.nod EType = = 11 | | This.nodetype = = 9) {
          this.appendchild (elem);}}
      );
    

According to the above source code. Guessing Dommanip's role is to iterate through the elements that match the current jquery object, then each element invokes the incoming callback and the node to be inserted (if it is a string that requires the creation of a document fragment node) as an argument to the incoming callback, and the incoming callback is executed.

Then analyze the Dommanip to see if the guesses are correct. Dom is the DOM element, MANIP is the acronym for manipulate, and the literal meaning of the combination is DOM manipulation.

A. dommanip:function (args, table, callback) parsing

Args DOM element or HTML code to be inserted

Whether the table needs to be modified tbody, this variable is the result of the optimization

Callback callback function, execution format is Callback.call (target element is context, document fragment to insert/single DOM element)

First look at the process, then look at the details

The first step is to initialize the variable. Where Inoclone is used later, if the current jquery object matches more than one element (n > 1), meaning that the constructed document fragment needs to be used by N, then it needs to be cloned (n-1), and the fragmented document itself is enough n times used; is the first element of the first parameter args, followed by a special treatment of value as a function;

var-I, node, hasscripts,
  scripts, doc, fragment,
  i = 0,
  l = this.length,
  set = this,
  Inoclone = l -1,
  value = args[0],
  isfunction = jquery.isfunction (value);

The second step is to deal with the special one by one call Dommanip that will match the current jquery object. There are two kinds of special cases: first, if the incoming node is a function (that is, value is a function), then each element that the current jquery object matches needs to be treated as a node in the Dommanip by the value computed by the function. The second, WebKit, we cannot clone the document fragment containing checked, the cloned document cannot be reused, then only one of each element matched by the current jquery object is invoked once Dommanip processing.

WebKit, we can't clone the text containing checked fragment
if (Isfunction | |! (L <= 1 | | typeof value!== "string" | | jQuery.support.checkClo NE | | !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 replaces the original args[0]
    if (isfunction) {
      args[0] = Value.call (this, index, table? self.html (): undefined);
    }
    Self.dommanip (args, table, callback);}

The third step is to handle the normal situation by using the incoming node to build the document fragment and insert it into the document. The document fragments built in this need to be reused and differentiated from the second process. It should be noted that if the script node needs to be executed after the load completes. Follow the source sequence to see the process

  Building Document Fragmentation

fragment = Jquery.buildfragment (args, this[0].ownerdocument, False, this);
i = Fragment.firstchild;
if (fragment.childNodes.length = = 1) {
  fragment = i
}

Isolate the script, which has a function disablescript changes the type value of the script label 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 Fragmentation 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 script, in two cases, remote use AJAX to handle, local direct execution.

if (hasscripts) {
  doc = scripts[scripts.length-1].ownerdocument;
  reenable scripts
  jquery.map (scripts, restorescript);
  Inserts the first document into the executable script for
  (i = 0; i < hasscripts; i++) {
    node = scripts[i];
    if (rscripttype.test) (Node.type | | "") &&
      !jquery._data (node, "Globaleval") && Jquery.contains (doc, node)) {
      if (NODE.SRC) { c10/>//Hope Ajax is available
        ... Jquery.ajax ({
          url:node.src,
          type: "Get",
          dataType: "Script",
          Async:false,
          Global:false,
          "throws": true
        });
      else {
        jquery.globaleval (node.text | | node.textcontent | | | node.innerhtml | | ""). Replace (Rcleanscript, ""));

B. DOM operational 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 ) );

Among them jquery.text = Sizzle.gettext;

JQuery.fn.html

function uses jquery.access to handle

  JQuery.fn.html:function (value) {return
      jquery.access (this, function (value) {...}, null, value, Arguments.length );
    }

If no parameter representation is taken

if (value = = = undefined) {return
  Elem.nodetype = = 1?
  Elem.innerHTML.replace (Rinlinejquery, ""):
  undefined;
}

Otherwise see if you can add content with innerHTML. Click on reference compatibility issues

See if we can take a shortcut, just use the innerHTML
//need to execute the code Script|style|link etc cannot be used innerHTML
//htmlserialize: Make sure that the link node can use innerHTML to properly serialize, which needs to be used in the//leadingwhitespace:ie strips of the IE browser's wrapper element
. innerHTML need to start with a blank
// It is not necessary to add an additional end tag or peripheral wrapper label to the element
if (typeof value = = = "string" &&!rnoinnerhtml.test (value) &&
  (jQuery). Support.htmlserialize | | !rnoshimcache.test (value)) &&
  (JQuery.support.leadingWhitespace | |!rleadingwhitespace.test (value)) &A mp;&
  !wrapmap[(rtagname.exec (value) | | ["", ""] ) [1].tolowercase ()]) {
  value = Value.replace (Rxhtmltag, "<$1></$2>");
  try {
    for (; i < L; i++) {
        //remove element node and cache, prevent memory leaks
        elem = this[i] | | | {};
        if (Elem.nodetype = = 1) {
          jquery.cleandata (GetAll (Elem, false));
          elem.innerhtml = value;
        }
      }
      Elem = 0;
    If you use innerHTML to throw an exception, use an alternate method
  } catch (e) {}
}

If you cannot use innerHTML or use it unsuccessfully (throw an exception), use an alternate method append

Alternate method, using Append to add node
if (elem) {
  this.empty (). Append (value)
;
JQuery.fn.wrapAll (wrapping all matching elements with a single label)
 process steps:
incoming arguments are functions that pass function results to
if (jquery.isfunction (HTML)) {return
This.each (function (i) {
jQuery (this). Wrapall (The Html.call (this, i));
});
}
Create a wrap layer
//Get package tag the elements to wrap the target around
var wrap = jQuery (HTML, this[0].ownerdocument). EQ (0). clo NE (true);
if (this[0].parentnode) {
wrap.insertbefore (this[0]);
}  

Wrap the current jquery object in a package

Wrap.map (function () {
var elem = this;
while (elem.firstchild && elem.firstChild.nodeType = = 1) {
elem = elem.firstchild;
} return
 Elem
}). Append (this);

Note: The current jquery object matching element is best only one, if there are multiple words are not recommended, this situation is carefully used, the following examples can be seen.

The simple example of the original DOM is (use this example later)

<div id= ' center ' class= ' center ' >
  <div id= ' ss ' class= ' center ' >
    <input type= ' submit ' id= ' left ' Class= "Left" >
  </div>
</div>
<div class= "Right" > I'm right</div>
$ (' #    Center '). Wrapall ("<p></p>"), Dom becomes
<p>
<div id= "center" class= "Center" >
<div id= "ss" class= "Center" >
<input type= "Submit" id= "left" class= "left" >
</div>
    </div>
</p>
<div class= "Right" > I'm right</div>.

Caution: If the current jquery matches more than one element, for example, 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'm right</div. >
</p>

See the result of it, originally #center is #ss parent node, the result becomes #ss sibling node.

JQuery.fn.wrapInner (The specified HTML structure is wrapped outside all child nodes of each matching element)

Processing steps:

The incoming argument is a function that passes the result of the function

if (jquery.isfunction (HTML)) {return
  This.each (the function (i) {
    jQuery (this). Wrapinner (Html.call (this, i)); 
   });

Iterate through the jquery object array, get the contents of each element (all child nodes) contents, and then use Warpall to wrap contents

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

  if (contents.length) {
    contents.wrapall (HTML);

  } else {
    self.append (HTML);}}
);

or using the original DOM in the example above, 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>
</p>
</div>
<div class= "right" >
<p>
I'm OK
</p>
</div>

JQuery.fn.wrap (The specified HTML structure is wrapped outside each matching element)

Use Wrapall to wrap each element of jquery separately.

Return This.each (function (i) {
  jQuery (this). Wrapall (isfunction? Html.call (This, i): HTML);
};

Row $ (' div '). Wrap (' <p></p> ') 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>
   </p>
<p>
<div class= "Right" > I'm right</div>
</p>

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

Use ReplaceWith to replace the parent node of the matching element with all child nodes that match the element's parent node. Of course, the parent node is body/html/document must be removed.

Return This.parent (). each (the function () {
  if (!jquery.nodename (this, the "body")) {
    jQuery (this). ReplaceWith (Thi s.childnodes);
  }
).  End ();
Execute $ (' div '). Wrap () result Dom becomes
<div id= "ss" class= "Center" >
<input type= "Submit" id= "left" class= " Left ' >
</div>
<div class= ' right ' > I'm right</div>.
  

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

You can also use selectors to further narrow the range of removal, removing only some elements of the current matching element that match the specified selector.

The Remove () function also removes the additional data (the data () function) and the event handler that are bound to the element Association (detach ()), as opposed to detach ().

for (; (Elem = this[i])!= null; i++) {
  if (!selector | | jquery.filter (selector, [Elem]). length > 0) {
    //Detach incoming parameter Keepdata is true, cache is not deleted C3/>if (!keepdata && Elem.nodetype = = 1) {
      //Clear Cache
      Jquery.cleandata (GetAll (elem))
    ;
    if (elem.parentnode) {
      if (keepdata && jquery.contains (elem.ownerdocument, Elem)) {
        Setglobaleval (g Etall (Elem, "script");
      }
      Elem.parentNode.removeChild (Elem);}}

You can see that there is an important function cleandata, which is used to clear the cache: iterate through each node element and handle each node element:

1. Get the cache corresponding to the current element

id = elem[Internalkey];
data = ID && cache[ID];

2. If there are bound events, traverse the Unbind 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 the cache is not removed by JQuery.event.remove, manually remove the cache. Which IE needs to do some compatibility processing, and will eventually save the history of deletion as Core_deletedids

Remove Cache
if (cache[ID) {
  delete cache[id] When jQuery.event.remove has not removed cache;
  IE does not allow deletion of expando features from the node using delete,
  //can also use RemoveAttribute functions on file nodes;
  We have to deal with all of 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 (clears all content within each matching element (all child nodes))

function will remove all child nodes of each matching element, including text nodes, annotation nodes, and all types of nodes, and the corresponding cached data will be emptied.

for (; (Elem = this[i])!= null; i++) {
  ///Prevent memory leaks from removing element node cache
  if (Elem.nodetype = = 1) {
    jquery.cleandata (GetAll (Elem, false));
  }
  Remove all child nodes while
  (elem.firstchild) {
    elem.removechild (elem.firstchild);
  }
  The Ie<9,select node needs to have option empty
  if (elem.options && jquery.nodename (elem, "select")) {
    elem.options . length = 0;
  }
}

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.