📜 ⬆️ ⬇️

jQuery from the inside - DOM manipulations

Vacations are continuing and we will use this to obtain new knowledge, strengthen and expand the old ones.

I thought for a long time what to look at further - attributes, properties and data or manipulation of the DOM, even started writing both articles. And it seems that at first it would be good to give the first topic, but in the comments to the previous topic, we already paid attention to one feature of working with scripts, which is just related to the second topic, so let's not pull and start with it. At the same time I apologize to those who saw the beginning of the article, which I mistakenly published in the process of writing.

So, today we will continue the series of digging in the jQuery source code numbered 1.8.3 (stable version at the time of this writing). We already got a general idea of ​​jQuery, parsing html . It’s time that we put in somewhere.

Virtually any work with the DOM somehow go to jQuery through the domManip function. Consider all the functions with which you can change the DOM in the library, we will not. Consider only the base.
')

domManip


The purpose of this service function is to perform a specific callback to each element, but at the same time to perform another bunch of additional work.

In order not to talk about domManip connection, here’s all the code for the append function:
 append: function() { return this.domManip(arguments, true, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 ) { this.appendChild( elem ); } } ); }, 

To be brief, then each element (if it is a regular ELEMENT_NODE or DOCUMENT_FRAGMENT_NODE ) of our set in the current jQuery object will be added through an ordinary appendChild element from the parameters to the function.

Inside the function, a fragment is built (using the buildFragment and clean functions familiar to us in the previous article), while inside clean all scripts are additionally collected that will not fall into the resulting fragment, but return to a separate array.

Further, for each element in our jQuery-object in its context, the same callback will be executed , to the input of which the cloned fragment will be submitted, which we received above.

Function as a parameter

domManip (respectively, and the functions that use it) are able to receive a function at its input, in which case it will be called for each element in a jQuery object in its context to get what we want to pass to it.

Useless (but visual, I hope) example in the studio:
 <span class="user" data-id="15"></span> <span class="user" data-id="10"></span> <script src="http://code.jquery.com/jquery-1.8.3.js"></script> <script> //     -   ,    $('span.user').prepend( function(idx, html) { //         , //     console.log(idx, html); //  $.data      return $(this).data('id') + ': '; } ); </script> 

In this case, a text node will be added to the beginning of each span with the “user” class, the contents of which will contain the user ID:
 jQuery.fn.prepend = function() { //            span'  jQuery- return this.domManip(arguments, true, function( elem ) { //      //   span.user,    (this) // 1 -> ELEMENT_NODE // 11 -> DOCUMENT_FRAGMENT_NODE //     span'  if ( this.nodeType === 1 || this.nodeType === 11 ) { //  elem (   -    "xx: ") //     span (  ,   ) this.insertBefore( elem, this.firstChild ); } } ); }; 

Scripts

What about the scripts, for which we collected them separately? Each script will be executed by the usual eval . If this is a script with the src attribute, then it will be pre- synchronous (to follow the order of execution of scripts) loaded. These scripts won't get into the DOM.

 $('<div>').append('<script>alert(1);</script>') 

This is an example from the comments on the previous topic (thanks to alisey ). Note that the created div hangs in the air, it is not in the document, but the script will still be executed in this case.

Immediately lies the catch with the insertion of scripts with the attribute src . The same with jQuery.getScript and jQuery.ajax with the type of script (all this is analog). By default, jQuery thinks that this does not need to be cached and adds a parameter with the current unix-timestamp to the URL when loading. In this case, if the browser wants to cache the response from the server (it all depends on the headers), then it will cache it according to the url, where there will be a timestamp, which in some (most likely even in most) cases is not acceptable, because the next similar request will again go past the cache and will again be cached with a timestamp in the url.

In this case, this code is bad, do not do this :
 $('<script>', { 'src': 'http://code.jquery.com/jquery-1.8.3.min.js' } ).appendTo(document.body); 

And this is how it is possible, long live the native Javascript:
 var scriptElement = document.createElement('script'); scriptElement.setAttribute('src', 'http://code.jquery.com/jquery-1.8.3.min.js'); document.body.appendChild(scriptElement); 

jQuery.empty and jQuery.remove


These functions work past the domManip and deal with removing elements from the tree through the usual removeChild . jQuery.empty removes all the nested elements of the victim, and jQuery.remove removes only those nodes that match the selector specified in the parameter to the call of this function. Both methods call the unknown function cleanData , which we will touch on in the next part.

jQuery.html


This function, if no parameters are specified during the call, returns the contents of our tag to us, the value of its innerHTML property, from which the jQuery service attributes will be previously removed.

If the parameter is specified, the library in most cases of our jQuery object in most cases (again, if the code does not include script , style , link tags and the first tag is not found in wrapMap we are familiar with in the previous article) will try to set the innerHTML property directly , otherwise, first clear the contents of our tag with empty , and then add code to it with append .

Few aliases for last


appendTo , prependTo , insertBefore , insertAfter , replaceAll . What is common between them? They, in fact, are aliases to other functions, they simply swap what the function is applied to, with its parameter. Simply put, something like this happens:

 $('<span>').appendTo(document.body) => $(document.body).append('<span>') $('span.user').insertAfter('span:first') => $('span:first').after('span.user') 

Conclusion


Messy happened this time somehow, right? I could miss something. In any case, write if you have any questions, if I missed something or even lied.

Write the code and enjoy it!

Content of a series of articles


  1. Introduction
  2. DOM manipulations

Source: https://habr.com/ru/post/164677/


All Articles