How does the jQuery stack (stack) work?

Asked

Viewed 326 times

7

When I learned to use the method .end jQuery, I realized that it was a powerful tool that ensures a lot of expressiveness to the code (and I’ve already seen how to integrate it to my plugins). Although I have a basic knowledge of how it works:

$(meuSeletor)       // Seleciona um conjunto de elemtentos   [meuSeletor]
    .fazAlgo()      // Faz algo com ele, mantém o conjunto   [meuSeletor]
    .find(sub)      // Acha um subconjunto, mas empilha o anterior [sub, meuSeletor]
        .fazAlgo()  // Faz algo com esse subconjunto               [sub, meuSeletor]
    .end()          // "Desempilha": volta o que tinha antes [meuSeletor]
    .fazAlgo();     // Faz algo com o conjunto original      [meuSeletor]

Some functions like the andSelf and the addBack still intrigue me (I remember long ago I tried to use the andSelf in practice, but did not have the expected result). I would like someone to explain, simply and succinctly, how this jQuery stacking system works, and how this can benefit us (in the authorship of plugins, for example).

  • The question is about the stacking system itself, or about addBack?

  • @bfavaretto It is about the stacking system itself (the addBack, I just saw here that it is similar to the addSelf - now obsolete). It’s easy to see in the API what each individual method does, but I lack the overview of the whole. I thought it would be interesting to ask here, instead of testing case by case everything that comes into my head (e.g., if I do it myself addBack afterward end, what will happen? if I do pushStack and in the original object I make end, what will happen? etc).

2 answers

3


I looked at the source code is simpler than I thought. The stack has been implemented as a string of objects. Each jQuery object can have a property prevObject, pointing to the previous object. Therefore:

var o = $(meuSeletor);
var sub = o.find(sub);
sub.prevObject === o; // true

The end returns the prevObject, or an empty jQuery object (think):

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

The pushStack creates a new object with the past elements and the object itself that invoked it, and defines the prevObject like the latter:

pushStack: function( elems ) {

    // Build a new jQuery matched element set
    var ret = jQuery.merge( this.constructor(), elems );

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

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

The addBack creates a new object by adding the prevObject to the current one (thanks to Gabriel Santos for locating the code):

addBack: function(selector) { 
    return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector)); 
}
  • You’re right, it looks very simple. I did a single test with the addBack, and it seems that it is a "destructive operation" like any other (i.e. $('#meuDiv').siblings().addBack().end() back to the brothers; to return to the original element, would need one more .end()). Tomorrow I’ll come back to that question.

  • 2

    addBack: function(selector) {
return this.add(selector == null ? this.prevObject : this.prevObject.filter(selector)); }

0

It’s quite simple actually, the selection functions, return the element itself, so you can use the so-called chaining to give a natural continuity in the script.

It’s like doing something like this:

$.fn.test = function() {
    return this.each(function(){/* faz algo com cada elemento*/});
};

The use would look like this: $('div').test();

And you could keep picking up the children paragraphs for example:

$('div').test().find('p');

So when you use a function that returns a node, it executes its scope and returns the same node so that it continues to be using... It’s like you walk through the DOM.

Here you come to the element li and adds a class:

$('li').addClass('test');

Here from the same point you stopped, you can go up to the parent element list of the current list item and perform another action, like hide for example:

$('li').addClass('test').closest('ul').hide();

This decreases writing and greatly improves performance in certain scripts.

If you want to see how a plugin does this inside you can see the standards developed by Addy Osmani from Google, I even contribute in this here: Best Options

  • Thanks for the answer, but I think you misunderstood the question: I’m not referring to simple chaining, but when jQuery "remembers" the last selection and allows you to come back to it later. In your last example, you could do ...hide().end().addClass('foo'); and the lis - not the ul - would take the class foo [besides test].

  • @mgibsonbr I understood, then, in fact jQuery when it returns to you a set of nodes, it does not return exactly nodes, it returns an object that itself generates, and in this object it stores beyond the true nodes in an index, that "stacking" which is passed from one function to another, so when the chaining function picks up the object returned by the previous function it knows which selectors it passed through, and can do the reverse path for example. I will not remember which index he keeps since it is of little use, but if I recall comment here.

  • I hope I’ve understood now rsrs ^^

  • Yeah, that’s kind of what you said. It has nuances, of course, hence the reason for the question (i.e. so that someone with more experience can explain how it works).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.