Archive for March, 2012

Sorting a Listbox with jQuery

One of the web pages for a recent project required the use of several listboxes and I found myself needing a way to quickly sort a given listbox due to the large number of options in some cases. Changing the underlying data source would certainly work for say, ascending order (and I did do that), but what if you wanted to toggle between ascending and descending? I could used Ajax, but since the sorting was only to aid selecting an item, I decided to just use jQuery client-side.

The following is an example of a listbox with several options and a button to perform the sort.

<select size="4" name="lbCarrier" id="lbCarrier" class="listbox">
    <option value="0">None</option>
    <option value="8">Principal</option>
    <option value="14">Other</option>
    <option value="3">Cigna</option>
    <option value="2">Blue Cross</option>
    <option value="11">Trustmark</option>
    <option value="10">Starmark</option>
    <option value="1">Atena</option>
    <option value="5">Humana</option>
</select>

<input type="submit" id="btnSortItems" value="Sort Items" />

Using jQuery I simply wired up a handler for the SortItems button click event and added a Sort function to do the work.

$(function() {
    $("#btnSortItems").click(function(e) {
        e.preventDefault();
        Sort("lbCarrier");
    });
});

function Sort(elementId) {
    // Convert the listbox options to a javascript array and sort (ascending)
    var sortedList = $.makeArray($("#" + elementId + " option"))
        .sort(function(a, b) {
            return $(a).text() < $(b).text() ? -1 : 1;
        });
    // Clear the options and add the sorted ones
    $("#" + elementId).empty().html(sortedList);
}

To sort descending, you simply reverse the operator in the comparison as shown below. You can also sort the listbox by the option value using $(a).val() > $(b).val() but that’s not as meaningful since those values aren’t visible and if the values are actually numbers, as in my case, they won’t sort properly because they are treated as strings.

// Descending sort
return $(a).text() < $(b).text() ? -1 : 1;

Here’s the carrier listbox with the default, ascending and descending sorts.

Once the sorting is complete, you may want to select an option based on it’s Text, Value or Index. Here are a few of the many different ways to select an option.

// Select by Text
$("#lbCarrier").find("option:contains('Other')").attr("selected", "selected")

// Select by Value
$("#lbCarrier option[value='8']").attr("selected", "selected")

// Select by Index
$("#lbCarrier option").eq(3).attr("selected", "selected")

// Select the first option
$("#lbCarrier option:first").attr("selected", "selected")

For my actual implementation, I wired up the listbox dblclick event to so that I could easily toggle between ascending and descending sorts instead of using a button, but the process is basically the same and it works for the standard select list/combobox as well.

jQuery makes it easy to add functionality or quickly try things out, it reduces the amount of code you would typically have to write and makes working with JavaScript something to look forward to instead of dreading.

Tags: , ,


Handling Session Timeout without Using CookiesHandling Session Timeout without Using Cookies

Recently I’ve been working on the early stages of development for a website. While testing, the session state timeout was beginning to be quite a pain. Sure I can extend it, but if I walk away from my computer long enough for a timeout, I come back expecting to be able to use the site and am unpleasantly surprised when I realize my session has been lost. I needed to come up with a more graceful way to handle this situation.

My first decision was – why not just keep the session open indefinitely?  I’ll refresh the session every few minutes; this way if the user is logged in, they could walk away for as long as they want, come back, and be able to use the site as expected. If the browser is closed, the session will be unable to refresh and the timeout will take care of ending the session. To implement, I followed this example by Malcom Sheridon.

This method works, however, I just couldn’t get past the security hole that will exist if the session is constantly open. What if the user is on a public computer and leaves the browser open? Anyone could sit down at that computer and have complete access to the user’s account. This method wasn’t going to cut it; I had to end the session somehow. This is when I decided that a session timeout warning (popup) was going to be needed.

The alert would be needed on all pages, so I incorporated it into a user control on my master page. Here is what the front end looks like, where “divTimeoutPopup” is the semi-transparent background, and “innerPopup” is the alert itself:

<div id="divTimeoutPopup"></div>
<div id="innerPopup">
    <div id="divTimeLeft"></div>
    <br />
    <asp:Button ID="btnOK" runat="server" Text="OK" OnClick="btnOK_Click" />
    <asp:Button ID="btnLogout" runat="server" Text="Logout" OnClick="btnLogout_Click" />
</div>

On Page_Load on my master page, I had the following:

//  we only want the popup to show if the user is logged in
If (UserLoggedIn)
{
	//  this line is taken from the blog mentioned above
	//  when a postback occurs the session will be refreshed
	//  but not if the user clicks the back/forward buttons
	//  in their browser, so we will call this just to make
	//  sure the session gets refreshed
	ScriptManager.RegisterStartupScript(Page, Page.GetType(), "keepAlive", "KeepSessionAlive('" + ResolveUrl("~/KeepSessionAlive.ashx") + "');", true);
	
	//  this line will show the timeout message after a
	//  given time (I have it set to 5 seconds for testing
	//  purposes
	ScriptManager.RegisterStartupScript(Page, Page.GetType(), "initiateTimeout", "setTimeout(function(){showTimeoutMessage('" + ResolveUrl("~/Logout.aspx?msg=sessiontimeout") + "')}, 5000);", true);
}

I should note that I use subdirectories in my site so I use ResolveUrl to help with the issue of absolute/relative paths within a master page. If you don’t use subdirectories you don’t need to pass in the URLs that you need and can put them directly into your javascript. The first function KeepSessionAlive is in my master page (or linked .js file) and looks like this:

function KeepSessionAlive(pageURL) {
    $.post(pageURL, null, function () { });
}

If you didn’t get a chance to look at the blog post mentioned above, the code for KeepSessionAlive.ashx is shown below. Basically all this does is set a session value, which refreshes the current session and keeps it active.

<%@ WebHandler Language="C#" Class="KeepSessionAlive" %>

using System;
using System.Web;
using System.Web.SessionState;

public class KeepSessionAlive : IHttpHandler, IRequiresSessionState
{
    public void ProcessRequest(HttpContext context)
    {
        context.Session["KeepSessionAlive"] = DateTime.Now;
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Next showTimeoutMessage is the code that actually displays the popup, this I have in my user control

function showTimeoutMessage(logoutURL) {
		//  show the popup
        $('#divTimeoutPopup').css('display', 'block');
        $('#innerPopup').css('display', 'block');
        $('#divTimeoutPopup').css('opacity', '0.5');
		
		//  second value at which our countdown begins
        var secondsLeft = 3;
        UpdateTime();
        setInterval(UpdateTime, 1000); //every second update the seconds left

        function UpdateTime() {
            if (secondsLeft > 0) {
                $('#divTimeLeft').text('You have ' + secondsLeft + ' seconds before your session expires! Click OK to continue your session.');
                secondsLeft--;
            } else if (secondsLeft == 0) {
                window.location.href = logoutURL; // if time has run out, redirect the user to the logout page
            }
            
        }
    }

OK, so now we have everything in place to actually display the popup, now all we need to do is write the code for the buttons in the popup. Here is the code behind for my user control:

protected void btnLogout_Click(object sender, EventArgs e)
    {
        Response.Redirect("~/Logout.aspx");
    }

    protected void btnOK_Click(object sender, EventArgs e)
    {
        ScriptManager.RegisterStartupScript(Page, Page.GetType(), "refreshSession", "RefreshSession('" + ResolveUrl("~/KeepSessionAlive.ashx") + "', '" + ResolveUrl("~/Logout.aspx?msg=sessiontimeout") + "');", true);
    }

As you can see the logout button simply redirects the user to the logout page. The OK button will refresh the user’s session. Here is the javascript for RefreshSession() which I have located in my user control.

	function RefreshSession(pageURL, logoutURL) {
		// refresh the session
        $.post(pageURL, null, function () { });
		
		//  hide the popup divs
        $('#divTimeoutPopup').css('display', 'none');
        $('#innerPopup').css('display', 'none');
		
		//  call this again so that the popup can display again if needed
        setTimeout(function () { showTimeoutMessage(logoutURL) }, 5000)
    }

The finished product:

Session Timeout Popup

It may seem like a lot, but it is actually pretty simple. Keep in mind I have my session set to timeout after 1 minute and my popup to display after 5 seconds – this is merely for testing purposes as it would be a nuisance to have a session popup so often! I suggest keeping the popup display time as close to your session timeout as possible to minimize unnecessary posts. You will have to decide on a value for your session timeout (in web.config) – this is important to know because this will be the minimum amount of time it will take to end the session if the browser has been closed.

 

Tags: , , , ,