Caching jQuery variables - javascript

I just read that if you are referring to the same DOM element over and over in a function it is better to cache them in a local variable like :
var btn = $('#clearBtn');
I have followed this where necessary but when accessing back this object I have always used $(btn).somemethod(); although we can access this directly like btn.somemethod();
I just need to know whether this will have a negative impact ?

There's no point in passing the jQuery object through the jQuery constructor. It's just wasteful. If the dollar sign looks nice to you, just prepend it to the variable name and use $btn in place of $(btn):
var $btn = $('#clearBtn');

btn.somemethod(); will work, and is the correct way, since btn is already a jQuery object.
Besides, unless you call $('#clearBtn'); a lot of times in a short period of time, I wouldn't bother caching it.

Related

Is it okay to store the result of a JQuery selector in a variable?

Developers I know tend to call the same JQuery selectors over and over instead of storing the result in a variable. They are consistent with this approach.
For example, they do this:
var propName = $(this).attr('data-inv-name');
var propValue = $(this).attr('data-inv-value');
Instead of this:
var current = $(this);
var propName = current.attr('data-inv-name');
var propValue = current.attr('data-inv-value');
The latter approach feels correct to me but maybe I'm missing something. This is a simple example, but I've seen $(this) repeated dozens of times in the same function.
What is the best practice for development with JQuery? Call selectors repeatedly or store in a variable?
The shown analysis is a micro optimization. Using $(this) repeatedly versus storing $(this) in a variable and reusing it will not cause a significant hit to performance.
The times you really want to store the result is when there is an actual selector in there. The only hit you are taking by repeatedly calling $(this) is calling the jQuery constructor function which is very lightweight.
So in this instance go with what reads better. If there really is a dozen occurrences of $(this) in a row, then there should have either been some storing of the variable as indicated, or more likely there was an opportunity to take advantage of chaining which was missed.
If I'm going to use the same selector more than twice I always create a variable. The one change I would recommend is using $ before your variable name to signify that it is a jQuery object
var $current = $(this);
var propName = $current.attr('data-inv-name');
var propValue = $current.attr('data-inv-value');
In theory selecting the component many times demands more process then using one you already have...
If you don't have too many selectors in your page the diference will be almost null (I guess this is the more commom case)... Then you can think about what makes it more readable or easy to modify...
Sometimes you use the same element in a dozen of lines, in this case I prefer to assign this to a variable because when the element change I will need to change just one line (the line I assigned the variable)...

javascript / jquery - get and hold element's initial html content

This is probably very basic but I'm stalling ...
On page load, I need to save the html content of my element into a variable. I have other code in the page that will change the html content of the element. So I need to be able to revert the value back to it's default (what it was on page load). The issue is that my variable's value is being changed to most recent value.
How can I make the initial value I assign to the variable "stick"?
currentElementsHTML = $("#myDOMElement"),
currentElementsHTMLDefaultValue = currentElementsHTML.html()
... do stuff that changes currentElementsHTML
... revert to currentElementsHTMLDefaultValue whenever i need to
There are many ways you can store some data and make it available later, some of these require a knowledge of the way JavaScript's scope works - others just rely on jQuery methods.
the first things that come to mind
global variable
The bad way to do this would be to store the value as a global var:
function at_the_start(){
/// notice there is no var keyword, this means the variable will be global
global_html = $('element').html();
}
function later_on(){
$('element').html( global_html );
}
You shouldn't do this because your data will "pollute the global namespace" - which basically means that other code will easily be able to access your variable (and mess around with it) and that you could inadvertantly overwrite some other code's global data - especially if you use a rather general variable name.
local variable kept in scope
A better way to do this would be to use the power of JavaScript for your own ends, namely its scope abilities, there are some good points to read here -- What is the scope of variables in JavaScript?:
function my_code(){
var html = $('element').html();
/* Do stuff here */
$('element').html( html );
}
The above relies on a local variable and the fact that you must keep everything in the same function call. As it is most likely you will be relying on a mixture of user triggered events, you can't really use the above. This is because you will have many functions used in different locations and they can't all share the same local variable. Or can they?
The following is what I call a "global local" variable - completely most likely not its real name, but it describes things as I see them:
function my_code(){
/// this variable is local, due to the var keyword
/// but it will be accessible in both the functions below
var html_local = '';
var my_function_to_start = function(){
html_local = $('element').html();
}
var after_other_things_have_happened = function(){
$('element').html( html_local );
}
/// you can even apply these functions to say an event handler
/// and the variable will be remembered because it exists within
/// the "after_other_things_have_happened" function's scope.
$('another.element').click(after_other_things_have_happened);
}
The above works because JavaScript functions can always access variables defined in previous parent blocks / parent scopes or parent functions.
jQuery data
Considering you are using jQuery, jQuery offers a very simple method for storing arbitrary data and you don't need to know anything about scope or local and global vars. It's taken me a while to write this and so obviously by this time other posters have correctly stated that the following is a good idea - jQuery Data:
$('element').data( 'old_html', $('element').html() );
This can then be accessed any time after by using:
$('element').data( 'old_html' );
So...
$('element').html( $('element').data( 'old_html' ) );
Will put the value back - this is stored along with the element so whereever you can access $('element') you'll be able to get at the data assigned to it.
Some other less relevant ways (but still methods of data storage)
storing as a property of an object
Another useful ability sometimes, is that JavaScript treats nearly every datatype as an object. This means you can add properties to nearly anything. The following is actually quite possible if a little odd.
var a = new String('This is a string');
a.withAProperty = 'another string';
alert(a);
alert(a.withAProperty);
I occasionally use this to create pseudo static properties on functions, like so:
var my_function = function(){
if ( ! my_function.staticProp ) {
my_function.staticProp = 'abc';
}
/* use my_function.staticProp for something here */
}
var another_function(){
/* you can also access my_function.staticProp here
but only after my_function has been called once */
}
/* and my_function.staticProp here, but only
after my_function has been called once */
This almost has the same affect of using a global var (especially if you apply it to global functions) but means your value is stored on top of your functions namespace, cutting down the possibility of collisions with other code quite drastically. It does still mean outside code can influence the content of your var -- which can actually be a benefit depending on what you want to do.
storing content in the dom
Depending on what you wish to store, it can sometimes be of benefit to record that data in the DOM. The most obvious of these would be to write the data into a hidden input or hidden element. The benefit of the latter is that you can still navigate this data (using the likes of jQuery or document.getElementById) if it happens to take the form of markup information (as yours does). This can also be beneficial way of avoiding memory leaks caused by circular references - if you are dealing with large amounts of data - as long as you make sure to empty your variables involved in the transporting of the data.
$.ajax('request_html.php').done(function(data){
$('<div id="hidden_html" />').hide().html(data).appendTo('body');
data = null;
/// you only need mullify data if you were to have other
/// sub/child functions within this callback, mainly being wary
/// of closures - which are functions that are defined in a certain
/// scope chain, but are then returned or put to use outside of
/// that chain - i.e. like event listeners.
/// nullify vars and removing large properties is still good practice though.
});
Then when you want to retrieve:
$('#hidden_html').html();
And in the meantime between those two points you can obviously still traverse the data:
$('#hidden_html h1 > a[name=first]');
You associate the original HTML with the same DOM element, that way it won't disappear:
$("#myDOMElement").data("initial-html", $("#myDomElement").html());
something like that, but not tested yet:
$(function() {
$('#id').data('store', $('#id').html());
});
...
$('#id').html(data('store'));
Set it and forget it.
If you push the contents of .html() into a variable, it will stay there unless you do something with that variable to remove it:
var original = $("#foo").html(); // original HTML is now in 'origina'
This won't change unless you change it.
Storing data on the element with $.data()
It might be more advantageous for you to store it as data (using jQuery's .data method) on the element itself though:
var element = $("#foo");
element.data( "original", element.html() );
This way you can always access it at a later time:
console.log( element.data( "original" ) );
Record, Reset, and Restore Demo: http://jsfiddle.net/ft8M9/
Works on many items too
// Access all elements to restore
var restore = $(".restore");
// Save original HTML, and set new HTML
restore.each(function(i,o){
var that = $(this);
that.data("original", that.html())
.html("Changed " + i);
});
// After 2 seconds, restore original HTML, remove stored data
setTimeout(function(){
restore.each(function(i,o){
var that = $(this);
that.html( that.data("original") )
.removeData( "original" );
});
}, 2000);
Demo: http://jsfiddle.net/ft8M9/1/

Replacing $ for jQuery - .addClass method

I'm having an issue trying to fix a conflict between both libraries and have used the noConflict in some sections, however I keep getting the $active is null error
I'm specialized in server-side scripting and not in Javascript (nor have time to read some documentation) so would appreciate any help.
Here is my code:
First (I'm just guessing here) jQuery adds a class which will be used later
jQuery(".paging a:first").addClass("active");
Now, it's time to use that class
var triggerID = $active.attr("rel") - 1; //Get number of times to slide
Since the code is for a slider the error appears every time this line is reached so my question is... Is there any way I can remove the $ and still use jQuery in that line?
P.S: I'm using jQuery 1.6.1 min and MooTools 1.2 although I doubt this is relevant I know they have conflicts so probably the source of this error.
To select an element by it's class in jQuery you need to use CSS style notation in a jQuery object, which in your example would be jQuery(".active"). Try this:
var triggerID = jQuery(".active").attr("rel") - 1; //Get number of times to slide
To use your original code, you'd need to assign the $active variable to be the jQuery object containing the element, like this:
var $active = jQuery(".paging a:first").addClass("active");
var triggerID = $active.attr("rel") - 1; // This will now work, assuming it's in the same scope as the above.
$active has nothing to do with jQuery in this case (though it might be a jQuery variable). In javascript you can name variables "$something" without a problem, and it doesn't collide with something named simply "$", which is another variable-name altogether. It's like saying you have removed all references to the variable "car", but don't understand why you get an error complaining about the lack of a "carpet". They're totally independent, thus the code you have posted is insufficient to see what is wrong.
[Edit]
If this sounded harsh in some way it's just me having a problem formulating myself in an understanding way on this matter, as I was simply trying to educate, not implying that this is something that you should know. It's an error a lot of peoples do in javascript (at least several I know of).
It should just be a matter of replacing $ with jQuery wherever you would normally use $.
So, instead of the typical:
$('.active').attr('rel')
use:
jQuery('.active').attr('rel')
$active is a javascript variable (they chose to put the $ in front of it to help them remember it stores a jQuery object, but the behavior is the same).
So, replacing every instance of $active by active should still work. I don't know ifit will solve your problem though.
One other thing you should try is putting your code in a closure:
(function($){
// your code
})(jQuery);
That way, inside of the closure, $ will only reference jQuery

Most efficient way to re-use jQuery-selected elements

I can imagine the correct answer to this based on theory, but I'm just looking for some confirmation. I'm wondering what the most efficient way to re-use a jQuery-selected element is. For example:
$('#my_div').css('background','red');
//some other code
$('#my_div').attr('name','Red Div');
vs.
myDiv = $('#my_div');
myDiv.css('background','red');
//some other code
myDiv.attr('name','Red Div');
I assume the second example is more efficient because the element #my_div doesn't have to get found more than once. Is that correct?
Similarly, is it more efficient to first save $(this) in a varaible, such as 'obj', and then reuse 'obj' rather than using $(this) over and over? In this case, jQuery isn't being forced to find an element over and over again, but it IS being forced to convert this to a jQuery object [$(this)]. So as a general rule of thumb, should a jQuery object ALWAYS be stored in a variable if it will be used more than once?
You should write your code such that you limit the number of DOM traversals.
When you write something like this:
$('#my_div').css('background','red');
//some other code
$('#my_div').attr('name','Red Div');
You are finding #my_div twice, which is inefficient.
You can improve this either by assigning the result of a selector (i.e. var x = $('.something')) and manipulate the variable x, or you can chain your method calls like this:
$('#my_div').css('background','red').attr('name','Red Div');
You'll see the above code used a lot, because you're finding the element once. The css() method will apply a CSS style and return the actual result of $('#my_div'), so you can invoke another method, in this case attr().
My preferred way of handling the re-use of selectors is to store them as a variable, and wrap my stuff in a closure.
if you're using jQuery selector (like $('#element')), then yes, you should always store your results.
if you're using object and wrapping it in jQuery (like $(this)), it's not necessary, because jQuery doesn't need to search for that element again.
One thing that I find is generally overlooked is just how powerful jQuery chains are. It may not be so noticeable, but since jQuery caches your wrapped elements within a chain, you can modify elements, go into a more specific subset, modify, then go back up into a a general superset without much overhead.
I expect something like (pardon the example)
$('#myDiv')
.addClass('processing')
.find('#myInput')
.hide('slow')
.end()
.removeClass('processing')
;
to be better performance-wise than even
var $myDiv = $('#myDiv').addClass('processing');
var $myInput = $('#myDiv #myInput').hide('slow');
$myDiv.removeClass('processing');
This also holds for applying the jQuery function to elements returned in an event handler. Try to avoid applying $(...) too many times, because this is slow. Instead create a variable that contains the result of $(...). Good practice is to start the variable with a $, which gives a hint about the jQuery object inside the variable.
$('a').click(function(){
var $this = $(this);
$this.addClass("clicked");
$this.attr("clicked", true);
});

What is a reverse reference to the DOM object?

In this link: http://css-tricks.com/snippets/jquery/jquery-plugin-template/ it has a line of code that says
// Add a reverse reference to the DOM object
base.$el.data("yourPluginName", base);
what does the "reverse reference to the DOM object" mean?
Assuming that you know the jQuery data function:
It's storing a reference to the instance of the class in the data cache of jQuery, meaning that the stored instance can be used to access the initial base object if it in the current context is not available.
This way, the class instance can be used later. However, the use of the prototype keyword upon the initial class that the instance were created from will modify the instance.
EDIT:
Ooops, it seems that Anurag is right, and I was giving wrong information.
Sorry, the information I gave in initial answer was not completely correct. I've updated the answer, so it now tells the truth.
In the comments you're asking:
so you mean its storing the current state of "base" in the data cache but if we make changes to "base" later on then the one in the data wont be affected? so if for some reason we needed to get the original one again we can do data('yourPluginName') to retrieve it? can you give me an example of when this would be helpful?
It seems that none of the statements are correct.
As I did obviously not remember adequately, the thing stored in data is only a reference to the object:
var obj = {};
obj.hello = "Hello";
$("#someElement").data("object", obj);
obj.world = " world.";
alert(
obj.hello +
$("#someElement").data("object").world
); // alerts "Hello world."
BTW, JavaScript variables with names like this base-thing (but, more often seen as that or similar) are typically used to represent the current context, accessed through the this keyword, which on many occasions is more easy to store in another variable due to scoping/context changes, that will make the current context and therefore this, change.
Also due to issues with context, the stored value in data could be used to access the specific object instance from another context (that is, when this represents something else), instead of the version of the base object that was continually used after a copy of it was stored.
I hope this answered you questions :D
The technique and the problem it solves is general and not specific to jQuery plugins. There may be cases where a Javascript object corresponds to a DOM element, and wraps logic specific to that DOM element. This object might be interested in listening to events such as clicks that happen within that DOM element. The information we get in those callbacks is the element that triggered it, and not the associated object. You could use jQuery's data API or any type of map in general to retrieve the corresponding object, and do something with it.

Categories