Ccna final exam - java, php, javascript, ios, cshap all in one. This is a collaboratively edited question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.
Thursday, May 31, 2012
jQuery pitfalls to avoid
I am starting a project with jQuery.
What pitfalls/errors/misconceptions/abuses/misuses did you have in your jQuery project?
$('.button').click(function() { /* do something */ });
This will end up looking at every single element to see if it has a class of "button".
Instead, you can help it out, like:
$('span.button').click(function() { /* do something */ }); $('#userform .button').click(function() { /* do something */ });
I learned this last year from Rebecca Murphy's blog
Update - This answer was given over 2 years ago and is not correct for the current version of jQuery. One of the comments includes a test to prove this. There is also an updated version of the test that includes the version of jQuery at the time of this answer.
Avoid abusing document ready. Keep the document ready for initialize code only. Always extract functions outside of the doc ready so they can be reused.
I have seen hundreds of lines of code inside the doc ready statement. Ugly, unreadable and impossible to maintain.
While using $.ajax function for Ajax requests to server, you should avoid using the complete event to process response data. It will fire whether the request was successful or not.
Suppose you wanted to animate a paragraph vanishing upon clicking it. You also wanted to remove the element from the DOM afterwards. You may think you can simply chain the methods:
In this example, .remove() will be called before .fadeOut() has completed, destroying your gradual-fading effect, and simply making the element vanish instantly. Instead, when you want to fire a command only upon finishing the previous, use the callback's:
The second parameter of .fadeOut() is an anonymous function that will run once the .fadeOut() animation has completed. This makes for a gradual fading, and a subsequent removal of the element.
If you find yourself reaching for the jQuery '.each' method to iterate over DOM elements, ask yourself if can use a selector to get the elements instead.
More information on jQuery selectors: http://docs.jquery.com/Selectors
Pitfall: NOT using a tool like Firebug
Firebug was practically made for this kind of debugging. If you're going to be mucking about in the DOM with Javascript, you need a good tool like Firebug to give you visibility.
More information on Firebug: http://getfirebug.com/
Other great ideas are in this episode of the Polymorphic Podcast: (jQuery Secrets with Dave Ward) http://polymorphicpodcast.com/shows/jquery/
Most of the times you'll only need the library and maybe the user interface. If you keep it simple your code will be maintainable in the long run. Not all plug-ins are supported and maintained, actually most are not. If you can mimic the functionality using core elements I strongly recommend it.
Plug-ins are easy to insert in your code, save you some time, but when you'll need an extra something, it is a bad idea to modify them, as you lose the possible updates. The time you save at the start you'll loose later on changing deprecated plug-ins.
Choose the plug-ins you use wisely. Apart from library and user interface, I constantly use $.cookie , $.form, $.validate and thickbox. For the rest I mostly develop my own plug-ins.
Misunderstanding of using this identifier in the right context. For instance:
$( "#first_element").click( function( event) { $(this).method( ); //referring to first_element $(".listOfElements").each( function() { $(this).someMethod( ); // here this is not referring first_element anymore. }) });
And here one of the samples how you can solve it:
$( "#first_element").click( function( event) { $(this).method( ); //referring to first_element var $that = this; $(".listOfElements").each( function() { $that.someMethod( ); // here this is not referring first_element anymore. }) });
I say this for JavaScript as well, but jQuery, JavaScript should NEVER replace CSS.
Also, make sure the site is usable for someone with JavaScript turned off (not as relevant today as back in the day, but always nice to have a fully usable site).
Usually, working with jquery means you don't have to worry about DOM elements actual all the time. You can write something like this - $('div.mine').addClass('someClass').bind('click', function(){alert('lalala')}) - and this code will execute without throwing any errors.
In some cases this is useful, in some cases - not at all, but it is a fact that jquery tends to be, well, empty-matches-friendly. Yet, replaceWith will throw an error if one tries to use it with an element which doesn't belong to the document. I find it rather counter-intuitive.
Another pitfall is, in my opinion, the order of nodes returned by prevAll() method - $('<div><span class="A"/><span class="B"/><span class="C"/><span class="D"/></div>').find('span:last-child').prevAll(). Not a big deal, actually, but we should keep in mind this fact.
Making too many DOM manipulations. While the .html(), .append(), .prepend(), etc. methods are great, due to the way browsers render and re-render pages, using them too often will cause slowdowns. It's often better to create the html as a string, and to include it into the DOM once, rather than changing the DOM multiple times.
Instead of:
var $parent = $('#parent'); var iterations = 10;
for (var i = 0; i < iterations; i++){ var $div = $('<div class="foo-' + i + '" />'); $parent.append($div); }
Try this:
var $parent = $('#parent'); var iterations = 10; var html = '';
for (var i = 0; i < iterations; i++){ html += '<div class="foo-' + i + '"></div>'; }
$parent.append(html);
Or even this ($wrapper is a newly created element that hasn't been injected to the DOM yet. Appending nodes to this wrapper div does not cause slowdowns, and at the end we append $wrapper to $parent, using only one DOM manipulation):
var $parent = $('#parent'); var $wrapper = $('<div class="wrapper" />'); var iterations = 10;
for (var i = 0; i < iterations; i++){ var $div = $('<div class="foo-' + i + '" />'); $wrapper.append($div); }
Using + operator a new string is created in memory and the concatenated value is assigned to it. Only after this the result is assigned to a variable. To avoid the intermediate variable for concatenation result, you can directly assign the result using += operator. Slow:
a += 'x' + 'y';
Faster:
a += 'x'; a += 'y';
Primitive operations can be faster than function calls
Consider using alternative primitive operation over function calls in performance critical loops and functions. Slow:
var min = Math.min(a, b); arr.push(val);
Faster:
var min = a < b ? a : b; arr[arr.length] = val;
Read More at JavaScript Performance Best Practices
If you plan to Ajax in lots of data, like say, 1500 rows of a table with 20 columns, then don't even think of using jQuery to insert that data into your HTML. Use plain JavaScript. jQuery will be too slow on slower machines.
Also, half the time jQuery will do things that will cause it to be slower, like trying to parse script tags in the incoming HTML, and deal with browser quirks. If you want fast insertion speed, stick with plain JavaScript.
Basically the hooks required for JavaScript are no longer necessary. I.e. use inline markup (onClick, etc) because you can simply use the ID's and classes that a developer would normally leverage for CSS purposes.
Being unaware of the performance hit and overusing selectors instead of assigning them to local variables. For example:-
ReplyDelete$('#button').click(function() {
$('#label').method();
$('#label').method2();
$('#label').css('background-color', 'red');
});
Rather than:-
$('#button').click(function() {
var $label = $('#label');
$label.method();
$label.method2();
$label.css('background-color', 'red');
});
Or even better with chaining:-
$('#button').click(function() {
$("#label").method().method2().css("background-color", "red");
});
I found this the enlightening moment when I realized how the call stacks work.
Edit: incorporated suggestions in comments.
Don't use bare class selectors, like this:
ReplyDelete$('.button').click(function() { /* do something */ });
This will end up looking at every single element to see if it has a class of "button".
Instead, you can help it out, like:
$('span.button').click(function() { /* do something */ });
$('#userform .button').click(function() { /* do something */ });
I learned this last year from Rebecca Murphy's blog
Update - This answer was given over 2 years ago and is not correct for the current version of jQuery.
One of the comments includes a test to prove this.
There is also an updated version of the test that includes the version of jQuery at the time of this answer.
Understand how to use context. Normally, a jQuery selector will search the whole doc:
ReplyDelete// This will search whole doc for elements with class myClass
$('.myClass'); //
But you can speed things up by searching within a context:
var ct = $('#myContainer');
// This will search for elements with class myClass within the myContainer child elements
$('.myClass', ct);
Avoid abusing document ready.
ReplyDeleteKeep the document ready for initialize code only.
Always extract functions outside of the doc ready so they can be reused.
I have seen hundreds of lines of code inside the doc ready statement. Ugly, unreadable and impossible to maintain.
While using $.ajax function for Ajax requests to server, you should avoid using the complete event to process response data. It will fire whether the request was successful or not.
ReplyDeleteRather than complete, use success.
See Ajax Events in the docs.
Try to split out anonymous functions so you can reuse them.
ReplyDelete//Avoid
$('#div').click( function(){
//do something
});
//Do do
function divClickFn (){
//do something
}
$('#div').click( divClickFn );
"Chaining" Animation-events with Callbacks.
ReplyDeleteSuppose you wanted to animate a paragraph vanishing upon clicking it. You also wanted to remove the element from the DOM afterwards. You may think you can simply chain the methods:
$("p").click(function(e) {
$(this).fadeOut("slow").remove();
});
In this example, .remove() will be called before .fadeOut() has completed, destroying your gradual-fading effect, and simply making the element vanish instantly. Instead, when you want to fire a command only upon finishing the previous, use the callback's:
$("p").click(function(e){
$(this).fadeOut("slow", function(){
$(this).remove();
});
});
The second parameter of .fadeOut() is an anonymous function that will run once the .fadeOut() animation has completed. This makes for a gradual fading, and a subsequent removal of the element.
Pitfall: Using loops instead of selectors.
ReplyDeleteIf you find yourself reaching for the jQuery '.each' method to iterate over DOM elements, ask yourself if can use a selector to get the elements instead.
More information on jQuery selectors:
http://docs.jquery.com/Selectors
Pitfall: NOT using a tool like Firebug
Firebug was practically made for this kind of debugging. If you're going to be mucking about in the DOM with Javascript, you need a good tool like Firebug to give you visibility.
More information on Firebug:
http://getfirebug.com/
Other great ideas are in this episode of the Polymorphic Podcast:
(jQuery Secrets with Dave Ward)
http://polymorphicpodcast.com/shows/jquery/
Don't abuse plug-ins.
ReplyDeleteMost of the times you'll only need the library and maybe the user interface. If you keep it simple your code will be maintainable in the long run. Not all plug-ins are supported and maintained, actually most are not. If you can mimic the functionality using core elements I strongly recommend it.
Plug-ins are easy to insert in your code, save you some time, but when you'll need an extra something, it is a bad idea to modify them, as you lose the possible updates. The time you save at the start you'll loose later on changing deprecated plug-ins.
Choose the plug-ins you use wisely.
Apart from library and user interface, I constantly use $.cookie , $.form, $.validate and thickbox. For the rest I mostly develop my own plug-ins.
10 Ways to Instantly Increase Your jQuery Performance
ReplyDeleteCheck this article.
If you bind() the same event multiple times it will fire multiple times . I usually always go unbind('click').bind('click') just to be safe
ReplyDeleteMisunderstanding of using this identifier in the right context. For instance:
ReplyDelete$( "#first_element").click( function( event)
{
$(this).method( ); //referring to first_element
$(".listOfElements").each( function()
{
$(this).someMethod( ); // here this is not referring first_element anymore.
})
});
And here one of the samples how you can solve it:
$( "#first_element").click( function( event)
{
$(this).method( ); //referring to first_element
var $that = this;
$(".listOfElements").each( function()
{
$that.someMethod( ); // here this is not referring first_element anymore.
})
});
Avoid multiple creation of the same jQuery objects
ReplyDelete//Avoid
function someFunc(){
$(this).fadeIn();
$(this).fadeIn();
}
//Cache the obj
function someFunc(){
var $this = $(this).fadeIn();
$this.fadeIn();
}
Similar to what Repo Man said, but not quite.
ReplyDeleteWhen developing ASP.NET winforms, I often do
$('<%= Label1.ClientID %>');
forgetting the # sign. The correct form is
$('#<%= Label1.ClientID %>');
Events
ReplyDelete$("selector").html($("another-selector").html());
doesn't clone any of the events - you have to rebind them all.
As per JP's comment - clone() does rebind the events if you pass true.
Always cache $(this) to a meaningful variable
ReplyDeleteespecially in a .each()
Like this
$(selector).each(function () {
var eachOf_X_loop = $(this);
})
Avoid searching through the entire DOM several times. This is something that really can delay your script.
ReplyDeleteBad:
$(".aclass").this();
$(".aclass").that();
...
Good:
$(".aclass").this().that();
Bad:
$("#form .text").this();
$("#form .int").that();
$("#form .choice").method();
Good:
$("#form")
.find(".text").this().end()
.find(".int").that().end()
.find(".choice").method();
I say this for JavaScript as well, but jQuery, JavaScript should NEVER replace CSS.
ReplyDeleteAlso, make sure the site is usable for someone with JavaScript turned off (not as relevant today as back in the day, but always nice to have a fully usable site).
Using ClientID to get the "real" id of the control in ASP.NET projects.
ReplyDeletejQuery('#<%=myLabel.ClientID%>');
Also, if you are using jQuery inside SharePoint you must call jQuery.noConflict().
Passing IDs instead of jQuery objects to functions:
ReplyDeletemyFunc = function(id) { // wrong!
var selector = $("#" + id);
selector.doStuff();
}
myFunc("someId");
Passing a wrapped set is far more flexible:
myFunc = function(elements) {
elements.doStuff();
}
myFunc($("#someId")); // or myFunc($(".someClass")); etc.
Excessive use of chaining.
ReplyDeleteSee this:
this.buttonNext[n ? 'bind' : 'unbind'](this.options.buttonNextEvent, this.funcNext)[n ? 'removeClass' : 'addClass'](this.className('jcarousel-next-disabled')).attr('disabled', n ? false : true);
Explanation
my two cents)
ReplyDeleteUsually, working with jquery means you don't have to worry about DOM elements actual all the time. You can write something like this - $('div.mine').addClass('someClass').bind('click', function(){alert('lalala')}) - and this code will execute without throwing any errors.
In some cases this is useful, in some cases - not at all, but it is a fact that jquery tends to be, well, empty-matches-friendly. Yet, replaceWith will throw an error if one tries to use it with an element which doesn't belong to the document. I find it rather counter-intuitive.
Another pitfall is, in my opinion, the order of nodes returned by prevAll() method - $('<div><span class="A"/><span class="B"/><span class="C"/><span class="D"/></div>').find('span:last-child').prevAll(). Not a big deal, actually, but we should keep in mind this fact.
Making too many DOM manipulations. While the .html(), .append(), .prepend(), etc. methods are great, due to the way browsers render and re-render pages, using them too often will cause slowdowns. It's often better to create the html as a string, and to include it into the DOM once, rather than changing the DOM multiple times.
ReplyDeleteInstead of:
var $parent = $('#parent');
var iterations = 10;
for (var i = 0; i < iterations; i++){
var $div = $('<div class="foo-' + i + '" />');
$parent.append($div);
}
Try this:
var $parent = $('#parent');
var iterations = 10;
var html = '';
for (var i = 0; i < iterations; i++){
html += '<div class="foo-' + i + '"></div>';
}
$parent.append(html);
Or even this ($wrapper is a newly created element that hasn't been injected to the DOM yet. Appending nodes to this wrapper div does not cause slowdowns, and at the end we append $wrapper to $parent, using only one DOM manipulation):
var $parent = $('#parent');
var $wrapper = $('<div class="wrapper" />');
var iterations = 10;
for (var i = 0; i < iterations; i++){
var $div = $('<div class="foo-' + i + '" />');
$wrapper.append($div);
}
$parent.append($wrapper);
If you want users to see html entities in their browser, use 'html' instead of 'text' to inject a Unicode string, like:
ReplyDelete$('p').html("Your Unicode string")
Use strings accumulator-style
ReplyDeleteUsing + operator a new string is created in memory and the concatenated value is assigned to it. Only after this the result is assigned to a variable.
To avoid the intermediate variable for concatenation result, you can directly assign the result using += operator.
Slow:
a += 'x' + 'y';
Faster:
a += 'x';
a += 'y';
Primitive operations can be faster than function calls
Consider using alternative primitive operation over function calls in performance critical loops and functions.
Slow:
var min = Math.min(a, b);
arr.push(val);
Faster:
var min = a < b ? a : b;
arr[arr.length] = val;
Read More at JavaScript Performance Best Practices
If you plan to Ajax in lots of data, like say, 1500 rows of a table with 20 columns, then don't even think of using jQuery to insert that data into your HTML. Use plain JavaScript. jQuery will be too slow on slower machines.
ReplyDeleteAlso, half the time jQuery will do things that will cause it to be slower, like trying to parse script tags in the incoming HTML, and deal with browser quirks. If you want fast insertion speed, stick with plain JavaScript.
Not understanding event binding. JavaScript and jQuery work differently.
ReplyDeleteBy popular demand, an example:
In jQuery:
$("#someLink").click(function(){//do something});
Without jQuery:
<a id="someLink" href="page.html" onClick="SomeClickFunction(this)">Link</a>
<script type="text/javascript">
SomeClickFunction(item){
//do something
}
</script>
Basically the hooks required for JavaScript are no longer necessary. I.e. use inline markup (onClick, etc) because you can simply use the ID's and classes that a developer would normally leverage for CSS purposes.
Using jQuery in a small project that can be completed with just a couple of lines of ordinary JavaScript.
ReplyDelete