$(this) OR event.target OR var input = $(this) - javascript

jQuery is currently providing me with a fun introduction to Javascript after 12 years of surviving happily without. I'm at the stage where I'm trying to learn as much as I can about optimising the code I write and, whilst I have found plenty of good reference material, there is something quite basic which is puzzling me and I have been unable to find anything about it anywhere.
When I'm attaching something to an element how should I be referring to that element within the function. For example, when attaching a function to an element's click event :
$('#a_button',$('#a_list_of_buttons')).click(function() {
// NOW WHAT'S THE BEST WAY TO REFER TO '#a_button' ?
});
I know not to keep re-selecting it like so as the browser has to search the whole DOM again from scratch to find what it's already found once :
$('#a_button').click(function() {
// I KNOW THAT THIS IS NAUGHTY
var buttonValue = $('#a_button').val();
$('#a_button').addClass('button_has_been_clicked');
});
Currently I'm using either of the following but am not entirely sure what each is actually doing :
$('#a_button').click(function() {
// USING this
var buttonValue = $(this).val();
$(this).addClass('button_has_been_clicked');
});
But is this just re-selecting like in the first "naughty" example?
$('#a_button').click(function(event) {
// USING event.target
var buttonValue = $(event.target).val();
$(event.target).addClass('button_has_been_clicked');
});
This seems like it might be better but is it efficient to refer to 'event.target' multiple times?
$('#a_button').click(function(event) {
// USING A LOCAL VARIABLE
var thisButton = $(this);
// OR SHOULD THAT BE
var thisButton = $(event.target);
var buttonValue = thisButton.val();
thisButton.addClass('button_has_been_clicked');
});
I understand the performance efficiencies of passing things to variables but I'm unsure whether or not in these situations using $(this) or $(event.target) provides me with the same efficiencies already and so by setting a new variable I'm actually doing more work that I need to.
Thank you.

this and event.target are not always the same.
this refers to the element you assigned the listener to ( in this case the '#a_button' ). event.target however is the element that actualy triggered the event, which can be a childnode of #a_button.
So $(this) is the thing you are looking for.
See reference: http://api.jquery.com/event.target/

I may be wrong, but this and event.target are both just different references to the same element.
this and event.target are not always references to the same element. But in answer to your question, var thisButton = $(this); is definitely the winner. If you were writing C# code, you would never do the following:
this.Controls[0].Controls[0].Text = "Foo";
this.Controls[0].Controls[0].Controls.Clear();
You would do this:
var control = this.Controls[0].Controls[0];
So you probably should never re-use $(this) more than once either. Althought it's trivial to convert this from a DOM element to a jQuery object, it's still an unnecessary overhead.
However, sometimes you need to gear back from optimisation to make sure your code maintains it's readability.
Another option of course is just to change what this is. This is javascript afteral:
this = $(this); // Now `this` is your jQuery object
Disclaimer: I only just tried the above and it seemed to work. Might have some issues though.

I built a little example to demonstrate how this and e.target actually work: http://jsfiddle.net/xZAVa/

In my experience i would go with the following:
$('#a_button').click(function() {
// USING this
var buttonValue = $(this).val();
$(this).addClass('button_has_been_clicked');
});
The this in the context of your click callback method is a reference to the DOM event. Since you already have a reference to the DOM object it is trival to convert it into a jQuery object since a lookup is not required.
But on a side note, if you don't need to use jQuery in your callback, then don't. You can simply get the value of the button using standard JS this.currentTarget.value.
The other examples you mentioned require a DOM lookup, and depending on the complexity of your selector can take longer. Using a id based lookup like '#a_button' will perform better than a class based looked like .myClass.

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)...

Using jQuery, is is possible to watch a DOM element and report each time something is done to it?

I'm working on a site containing a confusing chain of asynchronous effects. Often, effects do/undo each other several times before stopping. I am having a very hard time following the spaghetti code.
Is it possible to set a callback to run any time a DOM element is manipulated so I can follow the chain of effects the code is applying to it?
Edit: I am currently adding dozens of console.logs everywhere and wanted a cleaner approach.
One approach is hooking into the core jQuery functions and log the information you want. Here's some very rough code on a few functions that i've hijacked i.e hide and animate.
var log_for_functions = 'hide animate'.split(' ');
$.each( log_for_functions , function(){
var function_name = this;
var original_function = $.fn[ original_function_name ]; // get a reference to old function name
$.fn[ function_name ] = function () {
var r = original_function.apply(this, arguments);
console.log( function_name + ' called on element ', this );
return r;
}
});
Now if you hide an element using jquery $('#test').hide() then you will get the following output.
hide called on element [context: #test, selector: "#test"]
You can change this to fit your needs and even hook into other functions.
Not without setInterval.
You could just have the code that applies an effect also note what changed.
You could do this with data attributes.
<div data-state="some state" ...
console.log(targetElement.getAttribute("data-state");
targetElement.setAttribute("data-state","changes");
MakeChanges(targetElement);
The following article introduces two techniques that might work in your case, Mutation Observers and Keyframes:
http://www.backalleycoder.com/2012/08/06/css-selector-listeners/
For each technique the article points to the related library.

Is calling $find a relatively expensive operation?

I am just wondering what the pros/cons would be between these two styles of coding:
var foo;
$(document).ready( function() {
foo = $find(fooID);
});
function OnBarClicked()
{
foo.doStuff();
foo.doMoreStuff();
}
compared to
function OnBarClicked()
{
$find(fooID).doStuff();
$find(fooID).doMoreStuff();
}
I feel like there's probably a gotcha in the former, but I'm not aware of why there would be a gotcha. In addition, if there's any seek time for $find().. is it more efficient to find all variables guaranteed to be 'found' 2+ times at the start, and then use the one instance?
EDIT: $find description and usage
var radListBox1ID = "<%= RadListBox1.ClientID %>";
var radListBox = $find(radListBox1ID);
alert(radListBox.get_id()); //Alerts RadListBox1.ClientID
Assuming you're asking if querying the DOM once vs multiple (one per statement) with $()...
Yes, it is more efficient to use a single $() and store the result in a variable, rather than invoke jQuery at every statement if you are using the element multiple times within the same scope...
This is particularly useful in event handlers where you are referencing this as a jQuery object multiple times.
$('#element').click(function(e) {
var $clicked = $(this);
$clicked.doStuff();
$clicked.doMoreStuff();
});
First of all, you felt that there was a gotcha in the first example, and indeed, there is: You declare the foo variable in a local context that your onBarClicked function doesn't have access to. You'll get an undefined error if you try to use it. To fix that problem, declare foo in the global scope:
var foo;
$(document).ready(function () {
foo = $(fooID);
});
When it comes to your actual question, the short answer is: Yes. Running the search once and storing the result will be faster.
But if you're only searching for an element by its ID, then in all honesty it probably won't make much of a difference. The times when it really matters are when you have a complicated selector that involves attribute selectors or pseudo-elements or descendant combinators or stuff. You'd definitely want to save that result instead of running it again and again and again.
The first one doesn't work as your would expect since foo won't be defined in OnBarClicked. foo gets set in the callback function once the document is ready.
I recommend this aproach:
$( function () {
// get all the references (to the DOM elements) ASAP
var header = $( '#header' )[0],
nav = $( '#navigation' )[0],
toolbar = $( '#toolbar' )[0];
// etc.
// now whenever you need to work with a referenced DOM element
// just wrap it inside a jQuery object
$( nav ).click( function () { ... });
});

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);
});

JavaScript mechanism for holding onto a value from a user action

I've created a JavaScript object to hold onto a value set by a user checking a checbox in a ColorBox.
I am relatively new to jQuery and programming JavaScript "the right way" and wanted to be sure that the below mechanism for capturing the users check action was a best practice for JavaScript in general. Further, since I am employing jQuery is there a simpler method to hold onto their action that I should be utilizing?
function Check() {
this.Checked = false;
}
obj = new Check;
$(document).ready(function() {
$('.cboxelement').colorbox({ html: '<input id="inactivate" type="checkbox" name="inactivatemachine"> <label for="inactivate">Inactivate Machine</label>' });
$(document).bind('cbox_cleanup', function() {
obj.Checked = $.fn.colorbox.getContent().children('#inactivate').is(':checked');
});
$(document).bind('cbox_closed', function() {
if ($($.fn.colorbox.element()).attr('id').match('Remove') && obj.Checked) {
var row = $($.fn.colorbox.element()).parents('tr');
row.fadeOut(1000, function() {
row.remove();
});
}
});
});
Personally, I would attach the value(s) to an object directly using jQuery's built-in data() method. I'm not really entirely sure what you are trying to do but, you can, for instance, attach values to a "namespace" in the DOM for use later one.
$('body').data('colorbox.checked',true);
Then you would retrieve the value later by:
var isChecked = $('body').data('colorbox.checked');
You run the data() method on any jquery object. I would say this is best-practice as far as jQuery goes.
You could capture the reference in a closure, which avoids global data and makes it easier to have multiple Checks. However, in this case it appears to be binding to the single colorbox, so I don't know that you could usefully have multiple instances.
function Check() {
this.Checked = false;
var obj = this; // 'this' doesn't get preserved in closures
$(document).ready(function() {
... as before
)};
}
var check = new Check; // Still need to store a reference somewhere.
$($.fn.colorbox.element()) is redundant. $.fn.colorbox.element() is already a jquery element.
It's common use (in the examples i watched, at least) to prepend a $ to variables referencing jquery elements.
So, var $rows = $.fn.colorbox.element().parents('tr'); gives instantly the idea that it is referencing jquery element(s).
I am afraid fadeOut won't work on rows in IE6 (if i recall correctly). You should be able to hide all the content inside the <tr> before removing it.
Can't help on the "simplify" thing because i don't know the colorbox's best uses.

Categories