Thursday, May 20, 2010

The Mysterious JavaScript 'This' Keyword.

The this keyword

One of the most powerful JavaScript keywords is this. Unfortunately it is hard to use if you don't exactly know how it works.

Below I explain how to use it in event handling.
Later on I'll add some information about other uses of this.

Owner

The question that we'll discuss for the remainder of the page
is: What does this refer to in the function doSomething()?

function doSomething() {
this.style.color = '#cc0000';
}

In JavaScript this always refers to the “owner” of the function
we're executing, or rather, to the object that a function is a method of.
When we define our faithful function doSomething() in a page, its owner
is the page, or rather, the window object (or global object) of JavaScript. An onclick
property, though, is owned by the HTML element it belongs to.

This "ownership" is the result of JavaScript's object oriented approach.
See the Objects as associative arrays page for some more information.


------------ window --------------------------------------
|                                          / \           |
|                                           |            |
|                                          this          |
|   ----------------                        |            |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          --------------------                          |
|          | onclick property |                          |
|          --------------------                          |
|                                                        |
----------------------------------------------------------

If we execute doSomething() without any more preparation the this
keyword refers to the window and the function tries to change the style.color of the window. Since the
window doesn't have a style object the function fails miserably and produces JavaScript errors.

Copying

So if we want to use this to its full extent we have to take care that the function that uses it
is "owned" by the correct HTML element. In other words, we have to
copy the function to our onclick property.
Traditional event registration takes care of it.

element.onclick = doSomething;

The function is copied in its entirety to the onclick property (which now becomes
a method). So if the event handler is executed this refers to the HTML element and its
color is changed.

------------ window --------------------------------------
|                                                        |
|                                                        |
|                                                        |
|   ----------------                                     |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------          |            |
|          |copy of doSomething()|  <-- copy function    |
|          -----------------------                       |
|                                                        |
----------------------------------------------------------

The trick is of course that we can copy the function to several event handlers. Each time this
will refer to the correct HTML element:

------------ window --------------------------------------
|                                                        |
|                                                        |
|                                                        |
|   ----------------                                     |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------          |            |
|          |copy of doSomething()|  <-- copy function    |
|          -----------------------          |            |
|                                           |            |
|   -----------------------                 |            |
|   | another HTML element| <-- this        |            |
|   -----------------------     |           |            |
|               |               |           |            |
|          -----------------------          |            |
|          |copy of doSomething()|  <-- copy function    |
|          -----------------------                       |
|                                                        |
----------------------------------------------------------

Thus you use this to the fullest extent. Each time the function is called,
this refers to the HTML element that is currently handling the event, the HTML
element that "owns" the copy of doSomething().

Referring

However, if you use
inline event registration

<element onclick="doSomething()">

you do not copy the function! Instead, you refer to it, and the difference is crucial.
The onclick property does not contain the actual function, but
merely a function call:

doSomething();

So it says “Go to doSomething() and execute it.” When
we arrive at doSomething() the this keyword once again refers to the global
window object and the function returns error messages.

------------ window --------------------------------------
|                                          / \           |
|                                           |            |
|                                          this          |
|   ----------------                        |            |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------         / \           |
|          | go to doSomething() |          |            |
|          | and execute it      | ---- reference to     |
|          -----------------------       function        |
|                                                        |
----------------------------------------------------------

The difference

If you want to use this for accessing the HTML element that is handling the event,
you must make sure that the this keyword is actually written into the onclick property.
Only in that case does it refer to the HTML element the event handler is registered
to. So if you do

element.onclick = doSomething;
alert(element.onclick)

you get

function doSomething()
{
this.style.color = '#cc0000';
}

As you can see, the this keyword is present in the onclick method.
Therefore it refers to the HTML element.

But if you do

<element onclick="doSomething()">
alert(element.onclick)

you get

function onclick()
{
doSomething()
}

This is merely a reference to function doSomething(). The this
keyword is not present in the onclick method so it doesn't refer to the HTML element.

Examples - copying

this is written into the onclick method in the following cases:

element.onclick = doSomething
element.addEventListener('click',doSomething,false)
element.onclick = function () {this.style.color = '#cc0000';}
<element onclick="this.style.color = '#cc0000';">

Examples - referring

In the following cases this refers to the window:

element.onclick = function () {doSomething()}
element.attachEvent('onclick',doSomething)
<element onclick="doSomething()">

Note the presence of attachEvent(). The main drawback of the
Microsoft event registration model
is that attachEvent() creates a reference to the function and
does not copy it. Therefore it is sometimes impossible to know which HTML currently handles the event.

Combination

When using inline event registration you can also send this to the function
so that you can still use it:

<element onclick="doSomething(this)">
function doSomething(obj) {
// this is present in the event handler and is sent to the function
// obj now refers to the HTML element, so we can do
obj.style.color = '#cc0000';
}

JavaScript Apply() and Call() Methods

JavaScript 1.3 includes two new methods for the Function object, call() and apply(). The apply() method is a variation on the call() method. The apply() method lets you pass the parameters from one method to the other in a different way than the call() method does it. The call() method requires the full list of parameters.

The apply method allows you to call a function and specify what the keyword this will refer to within the context of that function. The thisArg argument should be an object. Within the context of the function being called, this will refer to thisArg. The second argument to the apply method is an array. The elements of this array will be passed as the arguments to the function being called. The argArray parameter can be either an array literal or the deprecated arguments property of a function.

The apply method can be used to simulate object inheritance as in the following example. We first define the constructor for an object called Car which has three properties. Then the constructor for a second object called RentalCar is defined. RentalCar will inherit the properties of Car and add one additional property of its own - carNo. The RentalCar constructor uses the apply method to call the Car constructor, passing itself as thisArg. Therefore, inside the Car function, the keyword this actually refers to the RentalCar object being constructed, and not a new Car object. By this means,the RentalCar object inherits the properties from the Car object.

Code:
function Car(make, model, year)
{
this.make = make;
this.model = model;
this.year = year;
}

function RentalCar(carNo, make, model, year)
{
this.carNo = carNo;
Car.apply(this, new Array(make, model, year))
}

myCar = new RentalCar(2134,"Ford","Mustang",1998)
document.write("Your car is a " + myCar.year + " " +
myCar.make + " " + myCar.model + ".")

Output:
Your car is a 1998 Ford Mustang.

NOTE: The apply method is very similar to the call method and only differs in that, up until now, you could use the deprecated arguments array as one of its parameters.

Wednesday, May 19, 2010

IE9 Browser

Can IE9 rule the world and end the saga of Mozilla? Will it make Microsoft to dominate in the internet world?

http://news.cnet.com/8301-30685_3-20000433-264.html?tag=mncol

Thursday, May 13, 2010

HTML5 & CSS3 Readiness.

The below site gives info about all the HTML5 features and where the browsers stand in implementing each of them. Just click the below link and hover over each ray.

http://html5readiness.com/





HTML5 & CSS3 Readiness [html5readiness.com] is a relatively simple but cute data dashboard, completely created with CSS3, which tracks how much support exists for each new HTML/CSS feature.

The different browsers are grouped into color schemes based on their underlying rendering engine: all Firefox versions are shades of blue, Chrome/Safari - yellow, IE - pink. Each ray also has a different background color if it is a CSS3 feature. Each browser is represented with a tile on the bar and with an equal width of space. As a result, if a single bar is full, that specific feature has universal support among all the 8 browsers.

Tuesday, May 11, 2010

Show/Hide Performance Using JQuery

The "High Performance JQuery" presentation especially caught my attention when the speaker, Robert Duffy, said that .hide() and .show() were slower than changing the css directly.I benchmarked the various ways to hide DOM elements and looked into the jQuery source to find out what is going on.

The HTML I tested against was a page of 100 div tags with a class and some content, I cached the selector $('div') to use with each method to exclude the time needed to find all the div elements on the page from the test. I used jQuery 1.4.2 for the testing, but keep in mind that the algorithms behind the method calls can change dramatically from version to version. What is true for 1.4.2 is not necessarily true for other versions of the library.

The methods I tested were .toggle(), .show() & .hide(), .css({'display':'none'}) & .css({'display':'block'}), and .addClass('hide') & .removeClass('hide'). I also tested modifying an attribute of a <style> element.

.show() & .hide()

These were, in fact, comparatively slow methods of hiding DOM elements across all browsers. One of the main reasons is that .hide() has to save the notion of what the display attribute was before, so that .show() can restore it. It does this using the .data() jQuery method, storing that information on the DOM element. In order to do so, .hide() loops through every element twice: once to save the current display value, and then once to update the display style to none. According to a comment in the source, this prevents the browser from reflowing with every loop. The .hide() method also checks to see if you pass in a parameter to animate the hiding with an effect. Even passing in a 0 dramatically slows down the performance. Performance was slowest on the first call to .hide(); subsequent calls were faster.

Browser      hide/show
FF3.6 -         29ms / 10ms 
Safari 4.05 -   6ms / 1ms
Opera 10.10 -   9ms / 1ms
Chrome 5.0.3 -  5ms / 1ms
IE 6.0  -       31ms / 16ms 
IE 7.0  -       15ms / 16ms 

.toggle()

This was, by far, the slowest method of hiding all of the div elements. It iterates through every element returned by the selector, checks to see if the element is currently visible, and then calls .hide() on visible elements one at a time and .show() on hidden ones one at a time. It also has to check to see if you are passing in a boolean to force everything to .show() or .hide() and check to see if you are passing in functions to toggle instead of toggling visibility. There seems to be some opportunity for optimization of this function, as one could select all of the hidden elements of the selector and call .show() on them all at once and then select the remaining elements in the selector and call .hide() on them at the same time. If you are so inclined, I encourage you to check out the source and see if you can eke out any performance gains.

Browser      hide/show
FF3.6 -         80ms / 59ms 
Safari 4.05 -   24ms / 30ms 
Opera 10.10 -   67ms / 201ms
Chrome 5.0.3 -  55ms / 20ms 
IE 6.0  -       296ms / 78ms 
IE 7.0  -       328ms / 47ms 

.addClass() & .removeClass()

These are pretty snappy methods of hiding/showing elements of the DOM, twice as fast as .show() & .hide() in Firefox and three times as fast in Safari. The differences in IE 6, IE7, Chrome, and Opera are negligible. It's also worth noting that with 100 DOM nodes, we're talking a total difference of 18ms in Firefox and 4ms in Safari. The speed difference will only be relevant for very large selections. Adding and removing a class requires a bit more management on your part, since you have to create the class that has a display of none and then have to keep track of CSS priority to make sure your elements get hidden. The way jQuery adds and removes a class is through string manipulation, so I imagine that as the number of classes on an element grows, this method will get slower, but that is untested speculation on my part.

Browser      hide/show
FF3.6   -       11ms / 11ms 
Safari 4.05 -   2ms / 2ms
Opera 10.10 -   6ms / 3ms
Chrome 5.0.3 -  3ms / 1ms
IE 6.0  -       47ms / 32ms
IE 7.0  -       15ms / 16ms

.css({'display':'none'}) & .css({'display':'block'});

These methods were very snappy. They showed an improvement over .addClass() and .removeClass() in Opera and IE 6/7 and about the same in other browsers. They work great if you know the current display style of all the elements you are changing, or at least have not changed the display style inline. If you have changed the display style inline, then you will need to make sure you set the correct value when you make the element visible again. If you are just using the elements' default display value or set the display value in the css, then you can just remove the style like so, .css({'display':''}), and it will revert to whatever value it has in the css or by default. As a library, jQuery can't assume that the display element wasn't set inline, so it has to manually keep track of it. That is the main slowness you can avoid since you know you won't be setting the display inline.

Browser      hide/show
FF3.6   -       14ms / 12ms
Safari 4.05 -   2ms / 1ms
Opera 10.10 -   2ms / 2ms
Chrome 5.0.3 -  2ms / 1ms
IE 6.0  -       16ms / 16ms
IE 7.0  -       0ms / 0ms  // The usual caveat about inaccuracy of IE clocks applies.

Disabling stylesheets

For fun, I thought, "What if instead of manipulating every DOM node and changing things, we just futz with the stylesheet?" Could there be speed improvements there? I mean, the methods benchmarked above are plenty fast for everyday use, but what if I had 10,000 nodes on a page I wanted to show and hide? It would be slow just selecting them all. But, if I could manipulate the stylesheet, I could avoid the entire overhead. Let me just tell you that way is fraught with peril.

There are, of course, cross browser issues when manipulating stylesheets, since jQuery doesn't abstract them away for you. First, I tried to see if I could append a style tag with the css class as a string using jQuery, but got inconsistent results across browsers. Then I tried creating the stylesheet node and class using JavaScript, but there were different APIs and it ended up being too slow to justify. So finally, forgoing an attempt to do this in a programmatic way, I ended up just writing a style tag with a class in the head of the document. It's far too slow to create the stylesheet programmatically, but if it's already there then it is trivial to give it an ID and use its disabled attribute.

HTML:
  1. <style id="special_hide">.special_hide { display: none; }</style>
  2. <!--  ...  -->
  3. <div class="special_hide">Special hide DIV</div>

Then in javascript…

JavaScript:
  1. $('#special_hide').attr('disabled, 'true');

and BAM, you just displayed all of your elements with a class of “special_hide”. To hide them all again, just do…

JavaScript:
  1. $('#special_hide').attr('disabled', 'false');

and they are now all hidden. The total javascript processing time was 0-1ms across all browsers. The only javascript you are doing is changing an attribute. Of course, there is still the time the browser takes to reflow and repaint the page, but you've virtually eliminated all the javascript processing time. If you call any of the other methods, .toggle(), .hide(), or .css(), this method will stop working on those elements because they set the css style inline, which has higher precedence than other css. To make this method work again, simply do a .css(‘display’, ‘’) to remove the inline style. This method also requires the most work on your part, because you have to define the class and give the class to all of the elements on the page you want to show/hide at the same time, but if you are dealing with extremely large sets, this might just be worth it.

To recap, here is a list of methods to change the display of elements in order from fastest to slowest:

  1. Enabling/Disabling a stylesheet
  2. .css('display', ''), .css('display', 'none')
  3. .addClass(), .removeClass()
  4. .show(), .hide()
  5. .toggle()

Also note that for the majority of use cases, all of these methods are plenty fast to use. When you start having to manipulate large jQuery collections, .show() and .hide() might become too slow in IE, and you might need to bump up to .addClass() or .removeClass(). Enabling/disabling of stylesheets would only be necessary in the most extreme cases, but if things are hiding to slowly for you, you might want to give it a try.

Courtesy : Josh Powell