Something that bugs me, especially when I’m in the depths of a nasty-looking jQuery chain, is the fact that there’s no way to reference the last selection, that is, without explicitly assigning it to a variable.
Look at the following code:
$( '#something' ).width( this.parent().innerWidth() ); /* NOT POSSIBLE */ |
This is what I’d really love to be able to do ($('#something')
is assigned to this
in the background). But, unfortunately, there’s no way to change the value of this
(because it’s immutable); plus jQuery provides us with no way to grapple the previous selection. So, unless I want to mess around with functions as setters there’s really only two ways of doing what I want:
// #1 (BAD PRACTICE - selecting twice) $( '#something' ).width( $('#something').parent().innerWidth() ); // #2 var something = $('#something'); something.width( something.parent().innerWidth() ); |
So now we’re left with only one acceptable way of doing this; assigning $('#something')
to a variable and then referencing that variable from that point onwards.
I accept that this is the “best practice” but, to be honest, I really hate it… Just looking at it; it seems unnecessary. And I don’t want to mess around with functions as setters because that just makes the code even more bloated. Instead, I’d much rather have a property under the jQuery namespace that will always be referencing the last selection. Something like this:
$( '#something' ).width( $._this.parent().innerWidth() ); /* $('#something') === $._this */ |
And as the code progresses, $._this
is re-assigned continually; always reflecting the previous selection.
Maybe, in the future, the jQuery team will choose to implement something like this, but, for now, it’s possible using the following “hack”:
(function(_jQueryInit){ jQuery.fn.init = function(selector, context) { return (jQuery._this = new _jQueryInit(selector, context)); }; })(jQuery.fn.init); |
NOTE: This is not the same as jQuery(something).prevObject
.
I know its usefulness may not be immediately apparent to all of you; and I don’t blame you if you don’t find this technique appealing, but, before you make your decision, consider that its usage is quite specific and I think you’ll only really understand its appeal when you’ve encountered a situation that requires it.
Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!
This is cool. I just got done with a project where I had to write a couple of functions to handle making things equal height and something like this would of helped out a lot.
I am trying to understand the JavaScript code you used. I guess I don’t understand closures enough to understand why this works. Also are you extending jQuery’s init function? Can you explain how it works please?
@Ralph, the code I used is essentially doing three things:
– Get a reference to the current
jQuery.fn.init
– it’s been aliased as “_jQueryInit”.– Re-assign
jQuery.fn.init
to our custom function.– Within our function, return a new jQuery object while assigning it to
jQuery._this
.It could be re-written to the following:
I prefer to have everything contained – that’s why I used the anonymous-function to wrap around everything in my solution.
jQuery.fn.init
is the jQuery constructor function. So, if you wanted you could usenew jQuery.fn.init('div')
instead ofjQuery('div')
.The reason I overwrote the constructor (
init
) instead of the “wrapper” (jQuery
itself) is so that the prototype of jQuery would remain intact plus any direct properties, e.g.jQuery.each
.@James – I totally understand now. Thanks.
I’ve come across similar problems, but I used I different tactic:
by wrapping this with in a jQuery object.
@Tony, how does that solve the problem…? You still need to somehow get a DOM/jQuery collection to start with. Working within functions is no problem; but what if you need a reference outside of that handler?
@Tony –
$( '#something' ).width( $(this).parent().innerWidth()-100 );
this doesn’t work even though it looks like it should.That’s true, it doesn’t solve the problem if it is not inside a function, I didn’t look into it more. Now I see the wide used of having a native reference then.
Thanks for clearing that out and the script, I would use it for the project I am working on.
Although it makes me wonder if it would have any toll in performance having an extra property with a copy of the same jquery object.
I just starting learning javascript and I am always trying to leave less foot prints by using variables or functions that I don’t use (or rarely use). But that is just a personal decision for the programmer 😛
The jQuery object itself is not being copied; I’m simply assigning it to another pointer. Objects exist in memory and variables are simply pointers to those objects:
a
only exists once;b
,c
andd
are simply pointers (not copies)… all referencing the same object. In fact,a
is just a pointer too.Lol, I faced this problem yesterday and adopted the “best practice” solution you mentioned, although I didn’t liked it.
Would be great to have something like this implemented in jQuery core.
wow! that’s great! I only complain about the underscore in “_this”. it’s not really jQuery-style.. 😉
coudln’t you just do this with .each() ie:
$( ‘#something’ ).each($(this).width( $(this).parent().innerWidth() )); ??
@Valentino, you could rename it to
$.current
or anything really…@tfforums, Yes you could, but that’s long-winded and gets quite annoying after a while.
I could be wrong, but I suspect there may be issues here with animations — stop() exists for the same reason — and when $.ajax.async=true (which is the default).
In other words, will $._this always be what we think it is?
We know a variable assignment is completely safe in this respect.
@Steven, you raise a good point.. Using
$._this
in callback functions would be a bad idea, as there’s no telling what its value will be by the time the callback fires.I recommend only using it immediately after the selection; no functions involved. It’s not an all-round problem solver; just a convenience-feature.
this looks like bad design.
This is just the same as creating a global variable, and you know that there’s no such thing as good/safe global variable.
You’ll never be able to tell wether the $._this really contains that object at that given point, as I might change it anywhere on the code and it will reflect in any code using it.
Not a good practice at all.
@rafael; JavaScript is single threaded… Almost everything happens synchronously, one after the other… The value of
$._this
will always equal the last selection you made. While this, as an idea, may be considered a bad practice in other languages, its usage in JavaScript does not present any issues. Like I’ve said, the only time you would want to avoid this is when you make a selection outside of a callback function and then you wait for that callback function to fire.Plus, to be honest, your argument is flawed since any variable is subject to change (except getters) – not just
$._this
.This is a fascinating study.
I used the “Best Practices” method today for something that looks like this:
I am curious as to whether the $_.this would work in this sort of application. Is the current value of $._this assigned permanently to the onchange, or would it use the value of $._this at the time the element was changed?
what about this ?