DOMLint

I have seen JSLint , JavaScript Lint , JSON Lint and jQuery Lint .

Today I came across DOM Lint in this jQuery thread .

I never really thought it before but what if the name of a form is getElementById.

<form name="getElementById">
    ...
</form>

How about addEventListener as the id of a div.

 <div id="addEventListener">
    ...
  </div>

I am quoting Dave here. He explained the problem very well.

I strongly recommend against using using name/id attributes that are identical to the elements or properties. Specifically, don’t name an input element “select”, “id”, “name”, “form”, “action”, “method”, “value”, or any other name that is documented to be used by form or input elements. Here is why. The form element allows you to access its inputs by saying formname.inputname. So if you have an input named “myinput” you can use form.myinput to access it. But what if you name an input “id”? Now when you say form.id do you mean the “id” property of the form, or do you mean the “id” input inside the form?

If you run into those cases then you can use DOMLint . Source code is on github and here is the initial announcement .

jQuery edge: delegate method has arrived

One of the issues with live method is that live method first searches through all the elements then throws away the result.

$('p').live('click', function({})

In the above case jQuery does nothing with the selected p elements. Since the result does not really matter, it is a good idea to remove such codes from the document ready callbacks list.

So instead of doing this

$(function(){
$('p').live('click', function({})
})

just do this

$('p').live('click', function({})

Going a step further

John just landed this commit which adds delegate method .

Html markup

<div id='lab'>
  <p>p inside lab</p>
</div>
<p>p outside lab</p>

If you want to track all the clicks on p then you could write like this.

$(document).delegate('p','click', function(){
  log('p was clicked');
});

However if you only want to track clicks on ‘p’ which are inside the id lab then you can write like this.

$('#lab').delegate('p','click', function(){
  log('p was clicked');
});

Note this functionality is in jQuery edge and is not available in jQuery 1.4.1. So you will have to get jQuery code from github to play with it.

If you are interested in the jQuery.live vs jQuery.fn.live discussion then follow this thread.

Understanding jQuery effects queue

Recently I tried following code in jQuery and it did not work.

 $('#lab')
    .animate({height: '200px'})
    .hide();

If I pass a parameter to hide then it would start working.

 $('#lab')
    .animate({height: '200px'})
    .hide(1);

As it turns out I did not have proper understanding of how effects work in jQuery.

animate method uses a queue inside. This is the queue to which all the pending activities are added.

$('#lab').animate({height: '200px'}).animate({width: '200px'});

In the above code element is being animated twice. However the second animation will not start until the first animation is done. While the first animation is happening the second animation is added to a queue. Name of this default queue is fx. This is the queue to which jQuery adds all the pending activities while one activity is in progress. You can inquire an element about how many pending activities are there in the queue.

    $('#lab')
      .animate({height: '200px'})
      .animate({width: '200px'})
      .animate({width: '800px'})
      .queue(function(){ console.log( $(this).queue('fx').length); $(this).dequeue(); })
      .animate({width: '800px'})
      .queue(function(){ console.log( $(this).queue('fx').length);$(this).dequeue(); }) ;

In the above code, twice the current queue is being asked to list number of pending activities. First time the number of pending activities is 3 and the second time it is 1.

Method show and hide also accepts duration. If a duration is passed then that operation is added to the queue. If duration is not passed or if the duration is zero then that operation is not added to queue.

 $('#lab').hide(); // this action is not added to fx queue

 $('#lab').hide(0); // this action is not added to fx queue

 $('#lab').hide(1); // this action is added to fx queue 

Coming back to the original question

When show or hide method is invoked without any duration then those actions are not added to queue.

 $('#lab')
    .animate({height: '200px'})
    .hide();

In the above code since hide method is not added to queue, both the animate and the hide method are executed simultaneously. Hence the end result is that element is not hidden.

It could be fixed in a number of ways. One way would be to pass a duration to hide method.

 $('#lab')
    .animate({height: '200px'})
    .hide(1);

Another way to fix it would be to pass hiding action as a callback function to animate method.

$('#lab')
   .animate({height: '200px'}, function(){
     $(this).hide();
   });

Another way would be to explicitly put hide method in a queue.

     $('#lab')
        .animate({height: '200px'})
        .queue(function(){
          $(this).hide();
        })

Since hide method is not added to queue by default, in this case I have explicitly put the hide method to the queue.

Note that inside a queue method you must explicitly call dequeue for the next activity from the queue to be picked up.

   $('#lab')
        .animate({height: '200px'})
        .queue(function(){
          $(this).hide().dequeue();
        })
        .animate({width: '200px'})

In the above code if dequeue is not called then second animation will never take place.

Also note that methods like fadeTo, fadeIn, fadeOut, slideDown, slideUp and animate are ,by default, added to default queue.

Turning off all animations

If for some reason you don’t want animation then just set $.fx.off = true.

$.fx.off = true;  
$('#lab')
   .animate({height: '200px'}, function(){
     $(this).hide();
   });

Above code is telling jQuery to turn off all animations and that would result in the element hiding in an instant.

jQuery show method edge case

Here is a simple case of invoking show method on a hidden element.

<style>
  p { display: inline; }
  #hello p { display: none;}
</style>

<div id='container'>
  <div  id='hello'>
    Hello World
    <p>this is p inside hello</p>
  </div>
</div>

jQuery code.

$('p').show();

You can see the result here . Notice that when p is shown then display property of p is inline which is what it should be. All is well.

Now I’ll change the css a little bit and will try the same code again. New css is .

<style>
  #container p { display: inline; }
  #hello p { display: none;}
</style>

See the result here . Notice that display property of p is block instead of inline .

Where did jQuery go wrong?

jQuery did not do anything wrong. It is just being a bit lazy. I’ll explain.

Since the element was hidden when jQuery was asked to display it, jQuery had no idea where the element should have display property inline or block. So jQuery attempts to find out the display property of the element by asking browser what the display property should be.

jQuery first finds out the nodeName of the element. In this case value would be P. Then jQuery adds a P to body and then asks browser what is the display property of this newly added element. Whatever is the return value jQuery applies that value to the element that was asked to be shown.

In the first experiment, css style p { display: inline; }said that all p elements are inline. So when jQuery added a new p element to body and asked browser for the display property, browser replied ‘inline’ and ‘inline’ was applied to the element. All was good.

In the second case, I changed the stylesheet #container p { display: inline; } to have only p elements under id hello to have inline property. So when jQuery added a p element to body and asked for display type, browser correctly replied as ‘block’.

So what’s the fix.

Find the parent element (#hello) of the element in question ( p in this case) . jQuery should add a new p element to the #hello and then jQuery would get the right display property.

jQuery fadeTo method fades even the hidden elements

Following code has been tested with jQuery 1.4.1 . Code demo links are the end of the blog .

fadeTo method of jQuery ,strangely, fades event the hidden elements.

Here is html markup.

<style> #hello p { display: none;}</style>
<div id='hello'>
  <p>this is p inside hello</p>
</div>
<p>This is p outside hello</p>

Since the first p is hidden, you will see only one p element in the browser. Now execute following jQuery code.

$('p').fadeTo('slow', 0.5');

You will see both the p elements.

jQuery goes out of its way to make sure that hidden elements are visible. Here is fadeTo method.

fadeTo: function( speed, to, callback ) {
    return this.filter(":hidden").css("opacity", 0).show().end()
                .animate({opacity: to}, speed, callback);
}

Also notice that for a hidden element fadeTo operation starts with opacity of zero, while other elements will go down towards zero.

Checkout the same demo in slow motion and notice that while the first p element emerges out of hiding, the other p element is slowing fading. This might cause unwanted effect . So watch out for this one.

First Demo

Second Demo

Third Demo

My name is Neeraj. You can contact me at neerajdotname@gmail.com . I am also on twitter , facebook and Linkedin .

admin_data , active_record_no_table , session_expiration_management , always_at_bottom and javascript_lab are some of my projects on github .