jQuery Document Ready, the saga continues…

Further to my earlier post jQuery Document Ready…use it wisely! one reader pointed out a different kind of architecture:

The html, test1.html:

<html>
    <head>
        <title>Document Ready Demo</title>

        <script src = "jquery-1.4.2.js"></script>
        <script src = "test1.js"></script>

        <script>
            $(document).ready(function () {
                //make sure there is no caching to avoid confusion
                $.ajaxSetup({cache: false});
                
                $('#fetchView1').click(function () {
                    $.get('view1.html', function (data) {
                        $('#viewText1').html(data);
                    });
                });
            });
        </script>
    </head>

    <body>
        <button id = "fetchView1">Fetch View1</button>

        <div id = "viewText1">
            View1 content goes here
        </div>
    </body>
</html>

When the button is clicked, an AJAX call is made. This call fetches the contents of view1.html and puts it in the div with id viewText1. Now let’s look at the view:

view1.html:

<div>
   <input type="text" id="inputView1" value="Click button to fill with 1111" size="50" />
   <button id="buttonView1">Fill Input with number 1111</button>
</div>


A very simple view file indeed. Just an input text field and a button. The objective being that when this button is clicked the event handler will fill the input field with '1111'. So where is the event binding and handler? That is in the included test1.js.

The included test1.js looks like this:

(function($) { 
   $(document).ready(function(event) {

   console.log('test1.js document.ready executed');

   $('#buttonView1').click(function() {
        console.log('buttonView1 view clicked');
        $('#inputView1').val('1111');
   });
   console.log('buttonView1 button bound now');

//possibily more processing code and bindings

   });
    
})(jQuery);

Note here that the button is attached to the click event handler which fills the input with '1111'. There is also a document.ready binding and its handler which will fire when the document is good and ready. The question is which document. The answer in a minute:-)

When I run test.html1 in a browser the console displays the following:

Console [1]= test1.js document.ready executed

Console [2]= buttonView1 button bound now

Now if I were to click on the button titled "Fetch View1" the AJAX call fires and the browser output should look something like this:

If you now click the button you probably expect to see the input box filled with '1111'. But when you do, nothing happens. However the console statement above does indicate that the button was bound!

The problem is that the binding in test1.js took place when the document.ready fired which was when test1.html was loaded. At this time view1 did not exist and obviously neither did the input field and the button. So the binding in test1.js was ineffective and of no use. Imagine 55 JS included files with an average of 5 binding in each and each file with a document.ready handler. Every handler firing when the outer document is loaded. Waste of resources!

The binding should actually take place after view1.html is loaded. Not fully understanding this concept programmers resort to using live event binding or the livequery plugin. Mind you, both these are superb tools but if used without proper understanding your application will suffer from severe performance issues.

Two simple solutions come to mind.

1. Put the JS code in view1.html wrapped in document ready.

<script>
 $(document).ready(function(event) {

   console.log('test1.js document.ready executed');

   $('#buttonView1').click(function() {
        console.log('buttonView1 view clicked');
        $('#inputView1').val('1111');
   });
   console.log('buttonView1 button bound now');


//possibily more processing code and bindings

   });
</script>
<div>
   <input type="text" id="inputView1" value="Click button to fill with 1111" size="50" />
   <button id="buttonView1">Fill Input with number 1111</button>
</div>

Remove the test1.js include from test1.html:

<html>
    <head>
        <title>Document Ready Demo</title>

        <script src = "jquery-1.4.2.js"></script>

        <script>
            $(document).ready(function () {
                //make sure there is no caching to avoid confusion
                $.ajaxSetup({cache: false});

                $('#fetchView1').click(function () {
                    $.get('view1.html', function (data) {
                        $('#viewText1').html(data);
                    });
                });
            });
        </script>
    </head>

    <body>
        <button id = "fetchView1">Fetch View1</button>

        <div id = "viewText1">
            View1 content goes here
        </div>
    </body>
</html>

If you run this, you will see that the bindings work fine and the input field is filled with '1111' when the button is clicked. Reason being that jQuery considers view1.html as a document and fires the document.ready event when the view1.html DOM has loaded. Remember this will only work if you are using jQuery methods such as html, append etc. It will not work if you use core dom methods/properties such as innerHTML.

2. Make test1.js a function call.

You could also change test1.js as a function and call it after the AJAX call completes like so:

test1.js will now contain:

function test1(){ 
   console.log('test1.js function executed');

   $('#buttonView1').click(function() {
        console.log('buttonView1 view clicked');
        $('#inputView1').val('1111');
   });
   console.log('buttonView1 button bound now');

//possibily more processing code and bindings
}

test1.html will now become:

<html>
    <head>
        <title>Document Ready Demo</title>

        <script src = "jquery-1.4.2.js"></script>
        <script src = "test1.js"></script>

        <script>
            $(document).ready(function () {
               $.ajaxSetup({cache: false});
               
                $('#fetchView1').click(function () {
                    $.get('view1.html', function (data) {
                        $('#viewText1').html(data);
                        test1();
                    });
                });
            });
        </script>
    </head>

    <body>
        <button id = "fetchView1">Fetch View1</button>

        <div id = "viewText1">
            View1 content goes here
        </div>
    </body>
</html>

and finally view1.html will go back to its original form:

<div>
   <input type="text" id="inputView1" value="Click button to fill with 1111" size="50" />
   <button id="buttonView1">Fill Input with number 1111</button>
</div>

If you now run test1.html it will work as expected. Note the function call test1.js() after the AJAX call completes.

This is only the beginning. I am sure you have thought of other ways of doing this. My main purpose however of writing this post is to emphasize not to use document.ready, live bindings or livequery before fully understanding as to what is involved. For everything there is a purpose and a matching tool. Be prudent and pragmatic.

Happy programming!

Advertisements

One thought on “jQuery Document Ready, the saga continues…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s