Page performance - defer unwanted scripts executions

September 11th, 2009   Filed Under Javascript, browser, jQuery, optimization, page performance, snippet  

In extension to my previous post regarding deferring images I would like to share my thoughts about deferring unwanted scripts executions.

The process hungry operations in DOM manipulation include insertion or deletion of elements and attaching events to them. And if all of such DOM manipulation occurs at the page load time it literally hangs other stuff piped in the download queue; And we blame the network latency to be the culprit.

So how do we solve this problem. It is simple to say, “Yeah! that’s right”. But what’s the right way of doing the right thing is not well known.

I have tried my way of solving this problem. In this regard I suggest two measures to be taken:

1) Use Event Bubbling
2) Defer unwanted event attachments

1) Use Event Bubbling: Event Bubbling is something that happens naturally on the DOM tree. In plain english every event on the page bubbles up to its parent until it reaches the document element. For example if we have an HTML document having body>div>a tag. Then when you click on the “a” element, the javascript engine looks for any onclick handler assigned to the “a” tag if it is assigned then it will trigger it. Then it traverses up to the parent element which is the div tag. Now if the div tag is having any onclick handler then that will also get triggered and so on.

What we generally do is we assign all the click events to the corresponding elements so that when they are clicked the handler executes and performs something. Attaching this kind of event handlers to several elements on the page penalizes on the page download performance.

So what we do is, instead of assigning so many click handlers we can assign only one click handler to the document, where the cue ultimately reaches.

Now how do we know on the document level which element was clicked?

For that you have an event object which carries the information about the element on which the event occured. Reading the event object we can determine what to do. So basically you write a list of if ..else in the document click function.

Code:

//CENTRALIZED CLICK EVENT HANDLER
$(document).bind("click",function(e){
    if(!e.button){//ONLY WHEN LEFT CLICKED
        var et = e.target;
        var qet = $(et);
        if(et.className=="closeBut"){
            window.close();
        }
        if((et.tagName == "LI")&& qet.parents("del")[0]){
            //code to handle click on LI which has a parent DEL
        }
    }
});

The above code is based on jquery way of assigning handlers. In the above code you can see that the event object has a property called target which points to the object where the event occured. Now using that object you can truthify the condition to perform some activity based on the properties of that object or the surrounding objects. For example here we are checking for the className in one if condition and className and parent both in the other condition.

2)  Defer unwanted event attachments: This measure is another trick to unburden your load time script executions. What we do here is club all the hover and mouseover event attachments into one sub-routine and trigger it when the user moves his mouse for the first time.

Generally we see that a user’s pattern of opening links or a web page includes getting distracted to some other links or checking emails while the page opens. So a user open a web page and opens another web page or goes to check his emails or just waits for the page to load.

Here if the user doesn’t move his mouse, the heavy operation of assigning mouseover or hover events to list(s) or whatsoever on the page has an opportunity to get deferred to a later time when actually the user comes to the page and is ready to interact with the page.

Code:

//ON DOCUMENT MOUSEMOVE
$(document).bind("mousemove",function(e){
    if(typeof initHovers=='undefined'){
        if($$("#oc .menu")[0]){//MENU
                mainmenu($$("#oc .menu"));
        }
        $$=$;//DISABLING THE CACHING AFTER PAGE LOAD
        initHovers = 1;
    }
});

The above code does exactly what I have declared above. Here it checks for an undefined variable existence called “initHovers”. If it does not exist only then we execute the sub-routine and define the variable with a global scope. As a result the sub-routine execute only once on the mousemove event on the document. And we save few seconds/milliseconds on the page load.

Make your page lighter – defer images to load

June 21st, 2009   Filed Under Javascript, Tips & Tricks, comparisons, jQuery, optimization, snippet, utilities  

As web professionals we are generally concerned with the page load time, especially when we have a lot of images loading from third party servers which adds up to the page performance.
Portal developers are mostly worried and concerned with this problem.

I and my friend Shon Thomas have tried putting some thoughts and developed a jQuery plugin which will solve this problem.
The technique used here goes as follows:
We do not assign the “src” value to the images we want to load dynamically. Instead assign that URI in a different validator-friendly attribute. For example, you can use “class” or “longdesc”.
Upon scrolling the page, the plug-in detects the images in the viewport area of the browser window and swaps longdesc(or class) with src.

In both the attributes(class and longdesc) I don’t see any problem and they can be used as per your convenience and belief.There could be some argument regarding usage of both of these attributes; But as far as I think I have my reasonable words for both.
1) class: 99% of the time you don’t assign a class name to any image, instead you control them with their parent elements. Even then, if you are so specific, use longdesc.
2) longdesc: longdesc is a very rarely used attribute. I don’t often see people using this attribute on the content images. Even then, if you are specific then either use class attribute or don’t use this functionality for that particular image(as simple as that).

The image html that your server spits should be somewhat like this:

    <img src="http://o.aolcdn.com/shopping.aol.com/img/notavailable.gif" longdesc="http://ai.pricegrabber.com/pi/7/19/48/71948063_160.jpg" alt="Dansko Reese Dress Pumps - Calfskin Leather - For Women" title="Dansko Reese Dress Pumps - Calfskin Leather - For Women"/>

Source Code: The source code below is the plug-in which can go in your jquery plugin file(s) or can get included as a standalone include.

/**
 * jQuery plugin: Lazy Image Loader
 * version: 3.0
 * Author: Vivek Pohre,Shon Thomas
 * Website: (http://www.vivekpohre.com)
 * Example: call lzload() at or after document ready
 */

function lzload(){
    var w=window,adv=30,vph,tmpLD,nowView,collImg,imgTop,scrTop,ci;
    function getVPH(){
	    //DETERMINE WINDOW VIEWPORT HEIGHT ALWAYS TO AVOID RESIZING BROWSER ERROR
	    if(typeof (vph = w.innerHeight?w.innerHeight:$(w).innerHeight()) != "Number")
	        vph = document.documentElement.clientHeight;
    };

    //SAVE THE Y AXIS IN TOP ATTRIB
    getVPH();//GET THE VIEWPORT HEIGHT
    imgTop = 0;//INIT IMGTOP TO ZERO--TEMP VAR
    collImg = $("#content img").filter(':[longdesc]');//COLLECT ALL IMAGES HAVING longdesc ATTR
    collImg.each(function(){
    	imgTop = $(this).offset().top;
    	$(this).attr("top",imgTop);
    });
    $(w).bind("scroll",function(){loadImgs()}).bind("resize",function(){getVPH();loadImgs()});//ATTACH SCROLL & RESIZE EVENT TO WINDOW
    $.loadImgs = loadImgs = function(imgSet){//MAKE loadImgs FUNCTION PUBLIC
    	ci=(imgSet)?imgSet.show():collImg;//DECIDE COLLECTION FROM PASSED OR ALL DOM IMAGES
        scrTop = $(w).scrollTop();
        //DETERMINE THE Y-AXIS IN THE VIEWABLE AREA
        nowView = (scrTop+vph+adv);
        ci.filter(':[longdesc]').filter(':visible').each(function(i){
            if($(this).attr("top")<nowView){
            tmpLD = $(this).attr("longdesc");
            if(typeof tmpLD!='undefined')
               $(this).attr("src",tmpLD)[0].removeAttribute("longdesc",0);
            }
        });
        ci=collImg;
    };
    loadImgs();
    $("#content img").error(function(){
		$(this).attr("src","http://o.aolcdn.com/shopping.aol.com/img/notavailable.gif");
	});
}

Smart javascript arrays

September 16th, 2008   Filed Under Good to know, Javascript, Tips & Tricks, gyan, snippet, vivek  

Today I want to show you some really professional and efficient way to populate your javascript array.

When you declare an array you can do it simply by writing a short hand like:

var arrSalaries = [];

Notice that the square brackets[] itself delcares the array and you don’t have to write “new Array()”. So you save some characters like that. Thanks to javascript.

Sometimes you need to populate an array in a loop and generally people populate it like this:

for(i=0; i<something.length; i++)
{
  arrSalaries[i] = something[i].amount;
}

There is nothing wrong in the code but a more robust way would be:

for(i=0; i<something.length; i++)
{
  arrSalaries[arrSalaries.length] = something[i].amount;
}

This ensures that any exceptions(if any), would be taken care and the array would be properly populated. To be in simple words, let us say you have an exceptional continue statement in the for loop and the statement is executed on some event. In such a case, the array would be populated wrongly in the following fashion:

arrSalaries[0] = 200;
arrSalaries[1] = 200;
arrSalaries[3] = 200;// just now there was the exception
arrSalaries[4] = 200;
arrSalaries[6] = 200;// just now there was the exception

If you use the second method to populate the array you can avoid this problem.

Another thing in array(not everyone remembers) is the literal arrays.
So you can delcare an array with a literal like this:

var arrSalaries = [];
arrSalaries["vivek"] = 5000;
arrSalaries["shon"] = 5000;

And you can access them with a dot operator like:

alert(arrSalaries.vivek);

or the same way you declared it:

alert(arrSalaries["vivek"]);

One should always think to minimize the code as far as possible. Even a single character shows your optimization attitude.

Now after knowing this one obvious question comes to your mind that how to loop through this literal arrays:

The answer is the following code:

for(props in arrSalaries) //in case you have forgotten for ... in in javascript this is a reminder
{
    alert(arrSalaries[props]);//props represent the literal
}

The above way can be implemented to dig out any built-in or custom object to scan their attributes and methods.

But as commented by Andrew Dupont in his article associative arrays should not be used for storing values with literals at all.

Andrew is technically correct in saying that because you loose all the basic methods in this kind of array.

Better use  it like this:

var arrSalaries = {};
arrSalaries["vivek"] = 5000;
arrSalaries["shon"] = 5000;

So, what you replace is [] with {}. And hence, you come to know that {} is a shortcut of declaring an object. Enjoy madi !!

jquery extend

June 16th, 2008   Filed Under Good to know, Javascript, Tips & Tricks, browser, gyan, jQuery, snippet  

I have learnt the way to extend any jquery object today. I’ll show an example of adding an extra browser check to the browser object.

jquery has browser object as $.browser. So when you query this object you get mozilla, msie, opera, safari and version as the members of this object. Now if you want to add another browser say aol to the object you need to extend it like this:

$.extend($.browser, {aol: /AOL/.test(navigator.userAgent)});

The extend object does the job pretty neatly for you. The first parameter that goes is the object to be extended and the second parameter is the prop object which goes and sits in the extendee object.

So, to query you need to invoke is:

$.browser.aol // returns boolean

That’s great to know for the guys new to jquery. Enjoy madi !!!

Image Slideshow with SEO

June 16th, 2008   Filed Under Good to know, Javascript, Thinking Out Loud, Tips & Tricks, gyan, snippet  

I was doing some experiment with dhtml slideshow a couple of weeks back. Then someone said that all the images should be trackable by search engines(i.e., the image urls should be written on the html page) so that the crawlers can crawl them. The biggest challenge was the load time of all the images and the user experience.Then I applied a trick to overcome the problem. Which pretty much solves my problem and doesn’t shoot up the loading time.

The algorithm goes something like this:

1) Write the image src on the page.
2) Just after the image tag is written on the page with its source, change the image src to nothing.
3) While src is changed, store the original src in some collection for using it for slideshow purpose.

And it works pretty well in both IE and FF.

The code goes like this:

<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
    <head>
        <title> new document </title>
        <script type="text/javascript">
            function addToSS()
            {
                var obj = document.images[document.images.length-1];
                obj.style.display = 'none';
                var temp = obj.src;
                obj.src="";
                slideShow.imgArr[slideShow.imgArr.length]=temp;
            }
        </script>
    </head>
    <body>
        <img src="img/simpson1.jpg" class="hide"/>
        <script>addToSS();</script>
        <img src="img/simpson2.jpg" class="hide"/>
        <script>addToSS();</script>
        <img src="img/simpson3.jpg" class="hide"/>
        <script>addToSS();</script>
        <img src="img/simpson4.jpg" class="hide"/>
        <script>addToSS();</script>
        <img src="img/simpson5.jpg" class="hide"/>
        <script>addToSS();</script>
        <img src="img/simpson6.jpg" class="hide"/>
        <script>addToSS();</script>
        <img src="img/simpson7.jpg" class="hide"/>
        <script>addToSS();</script>
        <img src="img/simpson8.jpg" class="hide"/>
        <script>addToSS();</script>
        <img src="img/simpson9.jpg" class="hide"/>
        <script>addToSS();</script>
        <img src="img/simpson10.jpg" class="hide"/>
        <script>addToSS();</script>
    </body>
</html>

So whenever next you consider making an image slideshow, please visit this article for reference in case you just forget this trick. Bookmark it please.