Showing posts with label events. Show all posts

Bind jQuery events to tab or window close

This post shows you how to bind JavaScript functions that will fire when user navigates away from your page, closes browser or a tab. Browsers have native JavaScript events on window object: unload and beforeunload. beforeunload is a proprietary event introduced by Microsoft and supported by other browsers, but not all.

Please note, these events will be fired when user navigates away from the current page. This includes when user:

  • closes the browser or a tab;
  • clicks on any link (does not matter if it's to other domain or the current domain);
  • types any URL to the browser bar and leaves the page;
  • clicks browser's page reload, back, forward buttons.

NOTE
You must test your code, because the behavior of how browsers handle unload event has changed even in different versions of the browsers.

Catching browser/tab close event with JavaScript

Since unload is a native JavaScript event, we can get away without using jQuery at all.

window.onunload = function(){
    // do some clean up
    clearLocalStorage();
};

If you are using alert(), confirm() or making AJAX requests here, please see what browsers block them belove.

Subscribing to browser/tab close using jQuery

jQuery introduced $.unload() event shorthand in v1.0 and deprecated it in v1.8. $.unload() is a shorthand for .bind('unload', handler).

$(window).unload(function() {
    // Do some code clean up
});

How browsers handle alert(), confirm() and AJAX requests on unload event?

The document unload event was originally designed to let JavaScript to do some clean up. For example, clear or set cookies, but most of the time it is used to fire alert/confirm box or make an AJAX request to the server. This led to bad user experience and browsers started to block these methods in unload event.

Here is what each browser does:

  • Chromer/Safari (WebKit)
    • alert(), confirm() - blocked (confirm() returns false), AJAX request - not sent.
  • Firefox
    • alert(), confirm() - blocked (throws an internal NS_ERROR_NOT_AVAILABLE exception), AJAX requests - sent on page reload, but not on tab close.
  • IE9
    • alert(), confirm() - not blocked, AJAX requests - not sent.
  • Opera
    • alert(), confirm() - blocked, AJAX requests - not sent.

Browsers that have pop-up window blockers will block all window.open() function calls in unload event handler.

jQuery.live() – event binding to AJAX loaded elements

jQuery.live() function was introduced in jQuery version 1.3. It makes it easy to dynamically bind events to DOM elements that have not yet been created. In other words, it helps you to easily attach events to AJAX loaded elements that match your criteria.

NOTE:
If for some reason you are using old versions of jQuery, prior to 1.3, you will need to use event delegation or another method as they are described in my previous post named “How to bind events to AJAX loaded elements in jQuery 1.2.x”.

So, how does .live() function works?

It uses event delegation technique to bind to the events that fire on your page. In other words, it binds an event to the DOM tree’s root and listens to all events. When an event is fired it checks it’s originator and checks if we have bound any events to that particular DOM element.

// Example usage of jQuery().live() function
$('.mySelector').live('click', function(event){
    // my click event handler
});

// As of jQuery 1.4.1 .live() can accept
// multiple events, just like .bind() does
$('input').live('focus blur', function(event){
    // fires on focus and blur
});

You can also pass in additional data to your events to overcome some issues caused by closure. This was introduced in jQuery 1.4. Also, it worth mentioning that data is passed when the binding is made. Keep this in mind when you pass in dynamic data.

$('.mySelector').live('click', {myVar: 'myVal'}, function(event){
    // my click event handler
});

NOTE:
Some events were not supported cross browser in jQuery 1.3. Events like submit were supported in Firefox only. This is resolved in jQuery 1.4. Other methods that were not supported cross browser in jQuery 1.3 include: focusin, focusout, mouseenter, mouseleave, etc.

NOTE:
.live() function also works with custom events.

jQuery.live() function performance

Because .live() uses event delegation and performs additional checks, it will always be slower then events attached to the DOM elements using .bind() function (this includes shorthands like: .click(), .submit(), etc.). However, you can improve your .live() function performance providing context (as of ver. 1.4).

// Using context for better performance
// Note that context is a DOM element, not jQuery
$('.mySelector', $('.myParent')[0]).live('click', function(event){
    // my faster click event
});

Unbinding events attached using .live() function

.unbind() function equivalent of .live() function is .die(). You can unbind ALL events attached using .live() function from an element by simply calling .die(). If you want to remove a specific event or a specific handler function of a specific event type, you can call .die('event', functionHandler).

// Remove ALL events attached using .live()
$('.mySelector').die();

// Remove myFunk() from click event
$('.mySelector').die('click', myFunk);

function myFunk(){
   alert("Clicked!");
}

If you have any questions or need any help, you can ask me on Facebook.

Create callback functions for your jQuery plugins & extensions

Most of the time custom jQuery plugins and extensions that we create do not use a callback functions. They usually simply work on DOM elements or do some calculations. But there are cases when we need to define our own custom callback functions for our plugins. And this is especially true when our plugins utilize AJAX querying.

Let’s say our custom jQuery extension gets data by making some AJAX request.

$.extend({
  myFunc : function(someArg){
    var url = "http://site.com?q=" + someArg;
    $.getJSON(url, function(data){

        // our function definition hardcoded

    });
  }
});

What is bad in this jQuery code is that the callback function is defined and hardcoded right in the plugin itself. The plugin is not really flexible and its user will have to change the plugin code, which is bad!

So the solution is to define your own custom callback function argument. Here is how it is done:

$.extend({
  myFunc : function(someArg, callbackFnk){
    var url = "http://site.com?q=" + someArg;
    $.getJSON(url, function(data){

      // now we are calling our own callback function
      if(typeof callbackFnk == 'function'){
        callbackFnk.call(this, data);
      }

    });
  }
});

$.myFunc(args, function(){
  // now my function is not hardcoded
  // in the plugin itself
});

The above code shows you how to create a callback function for AJAX load, but if you want to create a simple callback function for your jQuery plugin, for example the one that executes on your own custom events. To achive this you can do the following:

$.extend({
  myFunc : function(someArg, callbackFnk){
    // do something here
    var data = 'test';
 
    // now call a callback function
    if(typeof callbackFnk == 'function'){
      callbackFnk.call(this, data);
    }
  }
});

$.myFunc(someArg, function(arg){
  // now my function is not hardcoded
  // in the plugin itself
  // and arg = 'test'
});

Identifying & locating mouse position in jQuery

While writing the next jQuery tutorial I needed to identify and locate where the mouse was on the page. Tracking mouse position on the page with jQuery is easy. You don’t need to check what browser the script is running like it is used to be with plain JavaScript. To identify where the mouse is in jQuery all you have to do is to read event object’s .pageX and .pageY properties.

Example:

$().mousemove(function(e){
   // e.pageX - gives you X position
   // e.pageY - gives you Y position
});

The above jQuery code is binding a new ‘on mouse move’ event to the current document and triggered every time mouse moves. The coordinates are calculated in pixels from top left corner of the document. See the above code in action.

You may also want to know the coordinates of the mouse relative to some <div> or an element. Since jQuery returns mouse position relative to the document root, by subtracting element’s position from document root you get mouse positions relative to that element. Long story short here is the code to do just that:

$("#someDiv").click(function(e){
    var relativeX = e.pageX - this.offsetLeft;
    var relativeY = e.pageY - this.offsetTop;
});

Don’t forget that you can bind any mouse event to any element and then get mouse positions. You can easily create a draggable object with click and mousemove events by simply setting the CSS top and left values to .pageX and .pageY.

Anyway, that’s how you locate and handle mouse positions in jQuery. As always, you don’t need to worry about cross browser compatibility issues while using jQuery. To learn more see more examples here.

Disable submit button on form submit

Form submission is one of the most used actions and double form submission is one of most occurred problems in web development. Rather than dealing with this problem server side, eating up your CPU process time, it is much easier and better to deal with this problem client side using JavaScript. When we talk javascript, what it a better way to write it other than using jQuery?!

So this Friday’s quick tip is disabling form submit button on its click or preventing double form submission by disabling submit button.

Consider we have this HTML form in our code:

<form id="myform" action="someUrl.php" method="get">
    <input name="username" />
    <!-- some more form fields -->
    <input id="submit" type="submit" />
</form>

Here is a jQuery code that disables submit button on form submission:

$('#myform').submit(function(){
    $('input[type=submit]', this).attr('disabled', 'disabled');
});

The above jQuery code binds a submit event to our #myform form, then finds its submit button and disables it.

Bonus: disable submit button on form submission for all forms on your page.

// Find ALL <form> tags on your page
$('form').submit(function(){
    // On submit disable its submit button
    $('input[type=submit]', this).attr('disabled', 'disabled');
});
Want to disable any other elements using jQuery? Read my previous “Disabling and enabling elements in jQuery” (very short) post.

Problems with jQuery mouseover / mouseout events

Today I have a quick note for you that will probably save you time someday. Basically it’s a workaround for a bug when you have parent element with children elements and parent element has mouseover or mouseout event. Moving your mouse over children elements may fire mouseout event of their parent. This is caused by event bubbling / propagation and if you would like to have a quick solution just read the solution at the bottom of this post. If you would like to understand it in more details please search Google for event propagation.

The problem:

When you have mouseover and mouseout events bound to some element on you page with children elements. Hovering over children element fires parent’s mouseover and/or mouseout event.

The solution:

The solution to this error is to use mouseenter and mouseleave events instead of mouseover and mouseout.

The reason:

This solution works because mouseover and mouseout events do not bubble from child to parent element.

Only the last element is bound/inserted/etc. in your javascript code’s “for” loop

There is a common problem when you use javascript for loop to bind an event function or add a class that comes from looping selection’s attributes.

To make clear what I mean consider this example:

var lis = $('ul li');

for (var i = 0; i<lis.length; i++) {
    var id = lis[i].id;
    lis[i].onclick = function () {
        alert(id);
    };
} // All li's get and alert the last li's id

There is no obvious code syntax nor logical problem in this code. But you still will get last li’s id in alert window whichever li you click.

The solution to this problem is to rewrite your code similar to this one:

var lis = $('ul li');

for (var i = 0; i<lis.length; i++) {
    var id = lis[i].id;
    lis[i].onclick = function (the_id) {
        return function () {
            alert(the_id);
        };
    }(id);
}

Here, we are introducing another anonymous function which will be called immediately after it has been declared, because of the trailing () (in our case (id)) with the current id.

This solves the problem of all items in the loop getting the last arrays/elements/etc. attribute value (id/class/etc.).

Preload images with jQuery

Web2.0 came with AJAX and AJAX came with its own requirements and standards for web application developers. Now web applications are more like desktop applications with a lot of options, dialogs and more. If you have developed AJAX application with different user controls you surely loaded resources such images, other javascript files on demand. This helps you keep your application lightweight and makes its load time faster.

jQuery makes creation and loading DOM elements (in our case images) very easy. If you need to preload an image you can use this jQuery script here:

// Create an image element
var image1 = $('<img />').attr('src', 'imageURL.jpg');

First jQuery creates a image DOM element and setting the src attribute of the image element would tell the user browser to load that image. Thus preloading the image.

Next you should insert your DOM element into the DOM tree using one of the many jQuery DOM manipulation methods.

Here are some examples of how you could insert preloaded image into your website:

var image1 = $('<img />').attr('src', 'imageURL.jpg');

// Insert preloaded image into the DOM tree
$('.profile').append(image1);
// OR
image1.appendTo('.profile');

But the best way is to use a callback function, so it inserts the preloaded image into the application when it has completed loading. To achieve this simply use .load() jQuery event.

// Insert preloaded image after it finishes loading
$('<img />')
    .attr('src', 'imageURL.jpg')
    .load(function(){
        $('.profile').append( $(this) );
        // Your other custom code
    });

Similar how to’s:

How to disable all jQuery animations at once

Yesterday I came across jQuery.fx.off setting in jQuery documentation. It disables all jQuery animations effective immediately when you set it's value to true.

Consider this code:

jQuery.fx.off = true;

$("input").click(function(){
  $("div").toggle("slow");
});

Your div will be showed/hidden immediately without animation. One of the reasons (as documentation mentions) to disable animations would be "slow" environments.

Working with jQuery 1.3's new Event object (jQuery.Event)

To start, in jQuery 1.3 event object has been normalized and wrapped into jQuery.Event object. As it says in the documentation: "The event object is guaranteed to be passed to the event handler (no checks for window.event required)."

Here is an jQuery.Event object overview:

  • Attributes
    1. event.type
    2. event.target
    3. event.relatedTarget
    4. event.currentTarget
    5. event.pageX/Y
    6. event.result
    7. event.timeStamp
  • Methods
    1. event.preventDefault()
    2. event.isDefaultPrevented()
    3. event.stopPropagation()
    4. event.isPropagationStopped()
    5. event.stopImmediatePropagation()
    6. event.isImmediatePropagationStopped()
Now, how to work with jQuery.Event object?

Anonymous functions that were bind to your elements will receive this new (jQuery.Event) event object and can utilize it's new attributes and methods. So your previous code will work fine (most of the time :) ).

$("a").click(function(event) { 
    alert(event.type); 
});

The fun part starts when you trigger events with jQuery.Event. You can create new jQuery.Event object and give it to the trigger()'er to trigger that event.

Example:

// Create new event object 
// the "new" is optional 
var e = jQuery.Event("click"); 

// Add additional data to pass 
e.user = "foo"; 
e.pass = "bar"; 

// Call your event 
$("a").trigger(e);

NOTE:
You don't have to use new to create a new jQuery.Event object. It is optional.

Alternative way to pass data through event object:

$("a").trigger({ 
    type:"click", 
    user:"username", 
    pass:"password" 
});

Try to play with the new event attributes and methods. You can do all kinds of fun things with them. Example: event.result, event.relatedTarget.

jQuery: Bind events to AJAX loaded / dynamically created elements

One of the most used features of jQuery is DOM manipulation, but right after that are probably event binding and AJAX content loading. When we bind events to AJAX loaded or dynamically added elements our app might not behave as we would have expected. This is one of the oldest dilemmas of front-end development.

jQuery has addressed this problem back in version 1.3 by introducing .live() method. Since then, the jQuery team has improved upon it and introduced .delegate() and .on() event binding methods. Depending on the jQuery version your project is using, you will need to use one of these methods.

In this post, I will try to help you identify the recommended way of binding events to AJAX loaded (or dynamically created) elements depending on jQuery version you are using. Also, cover some of the performance considerations you should be aware of.