Showing posts with label UI. Show all posts
Showing posts with label UI. Show all posts

Prevent JavaScript code blocking the UI thread with setTimeout + a handy stopWatch to profile JS code

In early January of this year I was implementing some JavaScript code that does a lot of processing on the client side with the help of jQuery. It’s part of a project where my customer asked for the possibility of answering a questionnaire in two ways: using a Wizard (one question at a time) and Listing (all questions at once) in a single form. The client side code for the Listing is the culprit and the subject of this post. It’s also related to this question I posted at StackOverflow on February 19:

Why ASP.NET MVC default Model Binder is slow? It's taking a long time to do its work

That question on SO is the inverse part, that is, the server receiving the client side data, but that’s just the other “part” of the problem which I also managed to solve. I really hope that this post helps you understand the problem with the client side part at least…

One of the things that I didn’t like in that JavaScript code was that while the questions were being processed to be rendered subsequently by the JavaScript/jQuery client side code, the browser UI thread hung/froze a lot of times – I think it’s worth mentioning here that I didn’t get Firefox’s warning prompt for "Unresponsive script". Anyway, the simple fact that the page freezes gives the user and me the developer a feeling that something is wrong with the code. As users we expect things to be fluid – we expect a good UI experience and seeing your browser freezing while loading a web page is not the best impression one may have of your software product.

I’m more experienced with server side code and haven’t ever seen any of my JavaScript code run slow on the client. I then started searching on Google why this was happening and just stumbled on this really helpful and insightful article at O’Reilly Answers:

Yielding with JavaScript Timers

Read it carefully to get a grasp of the concepts involved. I read it maybe thrice at least to understand the code and adapt it to the problem at hand. It fitted so well in my situation that the result I got after adapting the code was mind blowing.

The part that really got my attention was the Timed Code section - way bellow that O’Reilly article. It tells us that batch processing items (questions in my case) instead processing everything once or one at a time is more efficient to avoid blocking the UI.

JavaScript UI Queue and UI Thread lanes depicted: timed code is intercalated taking turnsFigure 1 - JavaScript UI Queue and UI Thread lanes depicted: timed code is intercalated taking turns

The JavaScript code I implemented processes a bunch of questions I receive from the server to prepare them to be shown to the user. Using a AJAX GET request’s success callback I dumped all the questions (usually 100 up to more than 200 sometimes and each one formatted with the help of a ASP.NET partial view) inside a div element $("#questions") like this:

// Loading Questions for the Chapter selected...
function loadQuestions(chapterId)
{
    $("#questions").fadeOut('slow').empty();

    $.ajax({
        type: "GET",
        url: "@Url.Action(MVC.UserAssessment.ActionNames.List, MVC.UserAssessment.Name)",
        data: { assessmentId: @Model.AssessmentId, chapterId : chapterId },
        cache: false,
        success: function(questions)
        {           
            $("#questions").html(questions);
            
            setUpQuestions();
        },
        error: function()
        {
            alert("@Html.Raw(Localization.UnknownErrorAjax)");
        }
    });
}

With this I had all the questions’ HTML beautifully inserted on the page but I needed to process this HTML before showing it to the user. That’s where the setUpQuestions(); method played its role. It does the heavy lifting and was where the whole thingy just got screwed up – the browser hung from time to time while inside that method… How did I discover that the problem was inside that method? I used Firebug’s Console and the JavaScript’s Date object as shown in that O’Reilly article. In setUpQuestions I use jQuery’s find, setup form fields validation with jQuery validation plugin, disable/enable fields, apply CSS styles to the questions, etc and all of this was tackled all at once by the poor browser JavaScript engine.

To profile setUpQuestions I created a stopWatch JavaScript method that receives setUpQuestions method as the func parameter:

function stopWatch(func)
{
    var start = +new Date(), stop;

    func();

    stop = +new Date();
if (stop - start < 50)
{
//alert("Just about right.");
console.log("Just about right.");
} else
{
//alert("Taking too long.");
console.log(“Taking too long.");
} }

According to that O’Reilly article, the author recommends never letting any Javascript code execute for longer than 50 milliseconds continuously, just to make sure the code never gets close to affecting the user experience – blocking the UI thread.

When I ran the code selecting different chapters with varying number of questions I kept getting “Taking too long” and then I found where the problem was. I knew it was time to adapt the code presented in the Timed Code section of that O’Reilly article. So here it is and commented where appropriate to make understanding it a little bit easier:

/// More about it here: http://answers.oreilly.com/topic/1506-yielding-with-javascript-timers/
function
timedProcessArray(items, process, callback) { var todo = $.makeArray(items); // The first call to setTimeout() creates a timer to process the first item in the array. setTimeout(function () { var start = +new Date(); do { // Calling todo.shift() returns the first item and also removes it from the array. process(todo.shift()); } // After processing the item, a check is made to determine whether there are more items to process and if the time hasn't exceeded the threshold of 50 milliseconds while (todo.length > 0 && (+new Date() - start < 50)); if (todo.length > 0) { // Because the next timer needs to run the same code as the original, arguments.callee is passed in as the first argument. setTimeout(arguments.callee, 25); }
else { //If there are no further items to process, then a callback() function is called. if (callback) { callback(items); } } }, 25); }

This code made the whole thing fluid and now the user has a much better experience while interacting with the page despite it having a lot of form controls. What it basically does is: process a batch of items/questions and then allows the UI thread to take some processing time and then it repeats until there’s no more questions left in the todo array. The process method is actually the setUpQuestions method that gets passed as a parameter called process.

In my specific case, each question has 4 HTML input elements (input/text, select, etc). If the user selects a Manual’s Chapter to answer and this Chapter contains 230 questions for example, the <form> element will contain about 920 controls = 4 x 230.That's a lot of controls to be processed by the JavaScript code inside the setUpQuestions method. Now no matter how many controls are present in the HTML code. timedProcessArray will handle this easily allowing the UI thread to breath from time to time.

This is the modified version of the loadQuestions method that makes use of this life saving timedProcessArray method where setTimeout shines:

// Loading Questions for the Chapter selected...
function loadQuestions(chapterId)
{
    $("#questions").empty();

    $.ajax({
        type: "GET",
        url: "@Url.Action(MVC.SAvE.UserAssessment.ActionNames.Answer, MVC.SAvE.UserAssessment.Name)",
        data: { assessmentId: '@Model.AssessmentId', chapterId : chapterId, format: '@Assessment.Format.List' },
        cache: false,
        success: function(data)
        {
            var questions = $(data);

            questions.hide().appendTo("#questions");

            //stopWatch(function(){ return timedProcessArray(questions, setupQuestion, stats)});

            timedProcessArray(questions, setupQuestion);

            //stats(questions);
}, error: function() { alert("@Html.Raw(Localization.UnknownErrorAjax)"); } }); }

I didn’t pass a callback function to timedProcessArray but that’s up to you.

Hope it helps you take the most out of your highly intensive processing JavaScript code.

As a last note, with the arrival of HTML5 we now have Web Workers but browser support is still limited. Things are getting better for us developers. Smile In the near future this will be standard for sure but till then we must find a way to solve the problem with the proven tools/code. setTimeout is one of them.

Hide table column and colorize rows based on value with jQuery

This is a handy piece of code that I used last year in one of my projects. I had scheduled to post it but it was just after I saw this question at StackOverflow that I decided to write about it. So here it is…

Let’s say you want a nice UI experience and to achieve that you wanna colorize/highlight a table row according to a given value present in a column of this row.
This is a simple task when we use jQuery.

I use the WebGrid that comes with ASP.NET MVC to display data on a web page using an HTML <table> element. It’s available in the namespace System.Web.Helpers. The approach described in this post is useful not only with the WebGrid but with any framework/language you use to output HTML code since the manipulation is done on the client side with jQuery.

Take this screen as an example:

HTML table with last column Active to be hidden displaying values Yes and NoFigure 1 - HTML table with last column Active to be hidden displaying values Yes and No

We want hide the last column “Active” and color the row which has a value Yes in this same column.

The above statement can be accomplished with the following code:

(function ($)
{ hideColumnColorRow = function (column)
{ $(
'td:nth-child(' + column + '),th:nth-child( ' + column + ')').hide(); $('tr').find('td:nth-child(' + column + '):contains(Yes)').parent().css('backgroundColor', 'LightGreen'); // Could be an hexadecimal value as #EE3B3B };
})(jQuery);

The hideColumnColorRow function* takes the column number as a parameter. It hides the column <td> and its header <th> using jQuery’s supper useful nth-child selector. Then for each table row <tr> it traverses the row’s columns and looks at the value of each column using :contains selector. If it finds a value = ‘Yes’ it’ll assign a background color to the column’s parent, that in this case is the <tr> (the row) using its CSS backgroundColor property.

So, taking Figure 1 as an example, the above code can be used in an ASP.NET MVC view this way:

<script type="text/javascript">

    $(document).ready(function ()
{
hideColumnColorRow(5); // Hiding the 5th column and colorizing the row for which this column has a value = Yes
});
</script>

* I’ve placed the jQuery/JavaScript function inside a file named custom.js. It resides inside the Scripts folder of the sample app available here. There’s no need to reference this script file in the view page because with the introduction of ASP.NET 4.5 we now have an all new Bundling and Minification Support for CSS and JavaScript files.

When the the app is run, this is the result:

HTML table with column Active hidden and highlighted rows based on its valueFigure 2 - HTML table with column Active hidden and highlighted rows based on its value

This is a really interesting requirement that one can implement in no time thanks to the power of jQuery. jQuery is one of the most fascinating things when we talk about software development. Its creator “John Resig” should be awarded a Computer Science Nobel Prize if that existed. Well it could be the Turing Award.

Anyone should take a look at jQuery and start using it as early as possible. It’s a must have today. I simply love it! Coração vermelho

Source code
I’ve put together a sample ASP.NET MVC 4 (uses NET Framework 4.5) so that you can try this out. You can run the app using the recently launched Visual Studio 11 Beta. You can download the free Visual Studio 11 Express Beta for Web here and the app code here.

Hope it helps.

Using jQuery to disable/enable and check/uncheck Radio Buttons on Date selected

Motivated by this question on StackOverflow - Disable radio button depending on date, I decided to help and here I’m with another code snippet post.

This time I show you how to use jQuery UI and its Datepicker control to control a set of radio buttons (only 2 in this post to make things easier) that have their state (enabled/disabled) changed depending on the date selected by the user.

Here’s the code:


<!DOCTYPE html>
<html>
<head>

   
<meta charset="UTF-8" />
    
   
<title>jQuery UI - Datepicker & Radio Buttons</title>

   
<!-- Linking to jQuery stylesheets and libraries -->
   
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/themes/base/jquery-ui.css" type="text/css" media="all" />

   
<link rel="stylesheet" href="http://static.jquery.com/ui/css/demo-docs-theme/ui.theme.css" type="text/css" media="all" />
   
   
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" type="text/javascript"></script>

   
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.min.js" type="text/javascript"></script>
   
   
<script src="http://jquery-ui.googlecode.com/svn/tags/latest/external/jquery.bgiframe-2.1.2.js" type="text/javascript"></script>

   
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/i18n/jquery-ui-i18n.min.js" type="text/javascript"></script>

</head>

<body>

   
<script>

   
$(function()
    {
       
$("#datepicker").datepicker
        ({          
           
// Event raised everytime a date is selected in the datepicker
            onSelect: function(date)
            {
               
// Self explanatory :) - used to get today's date
                var today = new Date();

               
// Business logic to change radio buttons' state                 if($("#datepicker").datepicker("getDate") > today)
                {
                   
$("#radioButton1").attr('disabled', true);
                   
$("#radioButton2").attr('disabled', false); 
                }
               
else
                {
                   
$("#radioButton1").attr('disabled', false);
                   
$("#radioButton2").attr('disabled', true); 
                }
            }
        });

       
// Just setting the default localization for the datepicker
        $.datepicker.setDefaults($.datepicker.regional['']); 
    });
    
   
</script>

   
<p>Date: <input id="datepicker" type="text"></p>

   
<input id="radioButton1" type="radio" value="myValue1" name="radioButton1"/>Radio button 1<br/>
   
<input id="radioButton2" type="radio" value="myValue2" name="radioButton2"/>Radio button 2

</body>

</html>

If you wanted to control the state (checked/unchecked) you’d have to make a small change in the code as follows:


// Business logic to change radio buttons' state                 if($("#datepicker").datepicker("getDate") > today)

   
$("#radioButton1").attr('checked', true); 
   
$("#radioButton2").attr('checked', false);  

else 

   
$("#radioButton1").attr('checked', false); 
   
$("#radioButton2").attr('checked', true);  
}

When you open the page for the first time you get this screen:

Page when viewed for the first time (both radio buttons are enabled)
Figure 1 - Page when viewed for the first time (both radio buttons are enabled)

If you pick a date that is greater than today’s date, Radio button 1 is disabled (turns to gray) and Radio button 2 is enabled according to the logic implemented.

Radio button 1 is disabled (turns to gray) and Radio button 2 is enabled
Figure 2 - Radio button 1 is disabled (turns to gray) and Radio button 2 is enabled

Otherwise, Radio button 2 is disabled and Radio button 1 is enabled:

Radio button 2 is disabled and Radio button 1 is enabled
Figure 3 - Radio button 2 is disabled and Radio button 1 is enabled

Hope you make good use of it.