<

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x

10 ways to reinvent the wheels on jQuery and JavaScript

Published on
9,938 Points
3,638 Views
3 Endorsements
Last Modified:
Approved
Once again, I've opened my peers' code and was terrified. So I decided to write this article. I hope that would be useful for someone. At the same time I've found an easier way to explain it to newcomers. I will just give them a link to this very article.
Of course the number of such things is very large, so the article is limited to a few of them.

1. Constants

This problem is not only javascript one but programming in general. Let's consider an example:
$elem.on('keydown', function(e) {
    if (e.keyCode == 27) {
        //...
    }
});

Open in new window

What is the magic number 27? People who often face the code immediately say - this is the key ESC. But most developers, especially beginners, do not remember the code, have to get into the search engine and waste their time.
Of course you can add a comment that hands ESC in the code, but much more effective is to introduce a constant, KEY_ESC = 27, for example.

2. Getting of the identifier

Often you need to get the ID of the element (comment, post, user, etc.) to perform some action (for example, to assess the comments with ajax). And you can often find this approach:
var id = $(this).attr('id').substring(8);

Open in new window

As in the previous example, the developer has to wonder - what is that number 8. He has to climb in the html code, etc.

There are worse examples (pattern copied from a real project):
var last_id = $('#answer_pid' + id + ' li:first div').attr('id').substr(7);

Open in new window

As the result the slightest change in layout will lead to change js code.

Sometimes we can see something like this:
<div class="comment" id="comment_123"></div>

Open in new window

var id = $(this).attr('id').substring("comment_".length);

Open in new window

Even better (or at least no hardcoded numbers), but still, this approach too strongly attached js code to html.

In my opinion much better to use data-* options, such as:
<div class="comment" data-id="123"></div>

Open in new window

then get the ID will be very simple:
var id = $(this).attr('data-id');

Open in new window

or
var id = $(this).data('id');

Open in new window

About the differences in the data and attr methods there is a set of articles.

3. Event handlers to multiple items

It is often necessary to add event handlers to page elements (such as a button "delete message"). And often you can find a similar approach:
$('.comment a.delete').click(function(){
    //
});

Open in new window

And we have a problem: how to add the same handler to the new item (such as a dynamically loaded comment). And here I saw a lot of decisions, including the redefinition of all the handlers again (often by copy-paste content of the handlers):
$('.comment a.delete').unbind('click').click(function() {
    //
});

Open in new window

Solution: jQuery 1.7 has a method on, which binds handlers, filtering elements by selector. Example:
$('body').on('click', 'a.external', function(e) {
    // Function will be called when you click on any link with a class [b]external[/b]
});

Open in new window

It is important that the handler works for dynamically created objects.
It should also be noted that this approach should be used wisely. For example the following code can result in poor performance and freezes the browser:
$('body').on('mousemove', selector, function() {
    //
});

Open in new window

4. Namespaced events

Despite the fact that the namespaced events were added in jQuery 1.2 - few people enjoy them (I think most people just do not know about them).
Consider this example:
$('a').on('click', function() {
    // handler 1
});
$('a').on('click', function() {
    // handler 2
});

Open in new window

Now, suppose that we need to remove the second handler of reference. But then the bad luck - $('a').off('click') will remove both the handlers. To the aid come namespaced events. Let's rewrite the code above:
$('a').on('click.namespace1', function() {
    // handle 1
});
$('a').on('click.namespace2', function() {
    // handle 2
});

Open in new window

Now it is possible to remove the second handler by calling $('a').off('click.namespace2');
More about namespaced events can be found here: docs.jquery.com/Namespaced_Events

5. Write as you say it out loud

Your task is to show other programmers what you do, not how you do it.
Before writing the next portion of code, think and speak out loud or to yourself that you are going to do. For example: "We need to clean the item". Many people do that not very well:
$(".info").html("")

Open in new window

because in the past they've used the convenient phrase .innerHTML = "". And above the preferred option:
$(".info").empty()

Open in new window

These programmers often clean item before placing a new information in it:
$(".info").html("").html("<b>Ok</b>")

Open in new window

But do not forget - you write on jQuery! And he cares about you, and .html() will clear itself before inserting a new element value.

And it will make it more wisely than to do it through .innerHTML. The fact that within the cleaning element can hold such items for which you have previously hung event handlers or bind data with .data(). jQuery will clean it and no memory leaks will appear.
By the way, sometimes in order to hide this information, it is better not just clean an item, but remove it at all:
$(".info").remove()

Open in new window

Furthermore...
I think when you write like so:
$("#history").css("display", "none")
$("#description").css("display", "none")
$("#category").css("display", "none")
$("#contact").css("display", "none")

Open in new window

you say "Hide history, hide the description, hide categories, hide contacts." Most likely, you said: "Hide history, description, category, and contacts." So write it:
$("#history, #description, #category, #contact").hide()

Open in new window

and do not forget that jQuery loves you, and there are methods to hide - .hide(), and to show - .show() - elements.

6. mouseenter/mouseleave vs. mouseover/mouseout

You may have noticed trouble: sometimes when you hang a pair of events on the element mouseover / mouseout to display tooltip, this tooltip is flashing. And it happens because of the fact that inside the element to which you hung handlers, there is another element. If you hover the mouse cursor over it for your browser generates an external element event mouseout, and for internal - mouseover, and then again to the outside - mouseover, which leads to distortion.

But jQuery loves us and offers us another couple of events - mouseenter / mouseleave, which solve this problem as follows. For the transmitted handlers a certain wrapper is made. At a time when the cursor moves to the inner element mouseout event is generated for the external element. Skeptical wrapper function checks the event.relatedTarget - the element which is hovered - and if it is located within the outer element, does not cause transmitted handler mouseleave. And no flicker.

Do not forget about the function .hover(handlerIn, handlerOut), which takes two handlers and handles them as mouseenter and mouseleave:
$(selector).hover(function() {
    tooltip.show()
}, function() {
    tolltip.hide()
})

Open in new window

Note, that starting with version 1.8 .hover() function is deprecated.

7. $.parent() vs. $.closest()

Often I see something like this:
var person = $(".name").parent().parent().parent()

Open in new window

It is clear that here is an attempt to get to the needed parent, which has important information, or has another item you want. And what if while you were on vacation, your $ (". Name") nestles in a different place, but under all the same person? Many craftsmen cyclically cause .parent() to the desired item. More experienced know that there is .parentsUntil(selector), which will return all parents to a specified (excluding itself). But still cumbersome decision:
var person = $(".name").parentsUntil(".person").last().parent()

Open in new window

But there's an obvious way:
var person = $(".name").closest(".person")

Open in new window

If we remember that we write code the way we put it into words, then this option is more suitable because of its transparency and brevity.

8. $.ajax() - less is better!

As you know - there is a method in jquery to work with ajax - $.ajax. There are some shorthand functions of this method such as $.get, $.load, $.post, etc. These features have been added specifically to alleviate some of your actions (upload script, json, perform post request), but in the implementation of all of these methods refer to the $.ajax.
Personally, I never use a shorthand function, and here's why.

In the code of beginners or inexperienced developers we can see several different stages.

1. Initial:
$.post(url, data, function(data) {
    data = $.parseJSON(data);
    // ...
});

Open in new window

2. Added try-catch block:
$.post(url, data, function(data) {
    try {
        data = $.parseJSON(data);
    } catch (e) {
        return;
    }
    // ...
});

Open in new window

3. Learn from the documentation that to the $.post() method could be passed dataType as last parameter (which lost in the abyss of the code, if success function does not fit the screen):
$.post(url, data, function(data) {
    // ...
}, 'json');

Open in new window

Very few web developers add handlers for the error situations. This is mainly due to laziness or unwillingness to spend the extra 5 minutes of time, or the developers just believe that mistakes will never happen. If the developer decided to add an error handler, you get something like:
$.post(url, data, function(data) {
    // ...
}, 'json').error(function() {
   // ...
});

Open in new window

In my opinion - it's awful unreadable. Also writing each time the error handler - it tiresome. Therefore, you can set the default error handler for all ajax requests, such as:
$.ajaxSetup({
    error: function() {
        // ...
    }
});

Open in new window

You can define any other repeated ajax parameters:
$.ajaxSetup({
    dataType: "json",
    data: {
        user_id: userId
    },
    error: function() {
        // ...
    }
});

Open in new window

Now we do not have to specify every time these parameters in queries:
$.ajax({
    data: {
        product_id: productId
    },
    success: function(json) {
        console.log(json)
    },
    alert: "Loading..." // For what? See below...
})

Open in new window

9. $.ajax() – notify users

And now we can give the user a message when AJAX-requests? I'm sure you thought about it, but avoided due to the fact that we must often write the same thing.

For a simple example will show messages like this:
<div id="popup" class="centered-bold-red"></div>

Open in new window

And link it to our requests:
$("#popup").ajaxSend(function(event, xhr, options) {
    $(this).text(options.alert || "Please wait...").show()
}).ajaxStop(function() {
    $(this).fadeOut("fast")
})

Open in new window

.ajaxSend() calls the handler every time there is an AJAX-request. Here we derive the transmitted (see above) a message or default one.

.ajaxStop () is called at the end, after all the AJAX-requests worked, i.e. if you have a parallel processed several AJAX-requests, the handler is invoked only once - after the last operation.

10. $.ajax() – Alert the code

Now let's look at our example above from the other side. Suppose one person is the developer of a composite widget, which displays information about the current events on the page. And the other person develops very business logic and implements AJAX-requests. Obviously, the need in the widget could arise  after it had been written a lot of AJAX-requests. If we love our code and our future colleagues as well as jQuery do, then we would have to predict the need of other code in notification of the completion of any of our AJAX-actions. And would do so:
$.ajax({
    data: {
        action: "search-name",
        name: $("#name").val()
    },
    beforeSend: function() {
        $(searchForm).trigger("start.search")
    },
    complete: function() {
        $(searchForm).trigger("finish.search")
    }
})

Open in new window

Now we can just tell (e.g. in the comments) that anyone can subscribe to the events start.search and finish.search:
$(searchForm)
    .on("start.search", function() {
        // Show preloader
    })
    .on("finish.search", function() {
        // Hide preloader
    })

Open in new window


Instead of a conclusion

This is only a small part of the problems that I encounter on a regular basis in a foreign code. I hope that this post will help to improve the quality of the code.
3
Author:uaoleg
5 Comments
LVL 75

Expert Comment

by:Michel Plungjan
Hi

Can you re-read your first paragraph or get a native English speaker to look at it? It is very hard to read.

Otherwise a good idea for an article
0
LVL 2

Author Comment

by:uaoleg
ok, I'll do this.
0
LVL 12

Expert Comment

by:jazzIIIlove
good insights!
0
LVL 58

Expert Comment

by:tigermatt
Nice set of tips. THank you for the submission. I voted for this by clicking "Yes" at the top.

-Matt
0
LVL 2

Author Comment

by:uaoleg
Thank for your response!
0

Featured Post

Determine the Perfect Price for Your IT Services

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden with our free interactive tool and use it to determine the right price for your IT services. Download your free eBook now!

The viewer will learn the basics of jQuery, including how to invoke it on a web page. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery.: (CODE)
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month