Developer

Avoid problems when redirecting via dropdownlists

Tony Patton's skills were put to the test recently when faced with a situation involving redirection and dropdownlists. Check out the solution he used to solve his problem.

 

One of the most important skills a developer needs is the ability to debug and fix problematic code whether it is their own or another developer's handiwork. My own skills were put to the test recently when faced with a situation involving redirection and dropdownlists. I was surprised that I had never encountered the problem, but the fix (which I outline below) is relatively painless.

The scenario

The scenario involves a simple ASP.NET Web form with two DropDownList controls. A user selects a value from the lists, and the page is redirected to the URL associated with the list entry.

The lists are populated with three values when the page is loaded. An entry with the label "Select Site" is inserted as the first entry in the list; no page is loaded for this selection. The following code listing shows a sample page with the two DropDownList controls and a BindData method for initializing the list data.

Two more methods are used to handle the selection of individual items in the lists; they are assigned to the SelectedIndexChanged event of the DropDownList control. Also, the AutoPostBack property of the DropDownList control is set to true to ensure the selection of an individual list entry causes the event code to execute.

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

<title>TechRepublic.com - Redirection with DropDownLists</title>

<script runat="server">

protected void Page_Load(object sender, EventArgs e)  {

if (!Page.IsPostBack) {

BindData();

}  }

protected void BindData() {

ListItem li = new ListItem("Select Site");

ddlRedirect1.Items.Add(new ListItem("CNET", "http://www.cnet.com"));

ddlRedirect1.Items.Add(new ListItem("News", "http://news.cnet.com"));

ddlRedirect1.Items.Insert(0, li);

ddlRedirect2.Items.Add(new ListItem("TechRepublic", "http://www.techrepublic.com"));

ddlRedirect2.Items.Add(new ListItem("GameSpot", "http://www.gamespot.com"));

ddlRedirect2.Items.Insert(0, li);

}

protected void ddlRedirect1_SelectedIndexChanged(object sender, EventArgs e) {

if (ddlRedirect1.SelectedIndex > 0) {

Response.Redirect(ddlRedirect1.SelectedItem.Value);

}  }

protected void ddlRedirect2_SelectedIndexChanged(object sender, EventArgs e)  {

if (ddlRedirect2.SelectedIndex > 0)  {

Response.Redirect(ddlRedirect2.SelectedItem.Value);

} }

</script>

</head>

<body>

<form id="frmRedirectionExample" runat="server">

<asp:DropDownList ID="ddlRedirect1" runat="server"

OnSelectedIndexChanged="ddlRedirect1_SelectedIndexChanged"

AutoPostBack="true" />

<br />

<asp:DropDownList ID="ddlRedirect2" runat="server"

OnSelectedIndexChanged="ddlRedirect2_SelectedIndexChanged"

AutoPostBack="true" />

</form></body></html>

The problem 

When the form is loaded, it performs as expected, but a problem arises when one of the list entries is selected. The user is taken to the corresponding page, and then the user returns to the starting page by selecting the browser's Back button. When the user returns to the page via the Back button and selects another entry in one of the lists, they are taken to the same page from their first visit to the page.

For example, a user loads the page (created without sample code) and selects the first entry in the first list; the user is taken to the CNET home page. Next, the user clicks the browser's Back button and returns to our sample form. Next, the user selects the first entry in the second list (the TechRepublic home page), but they are taken to the CNET home page instead of the expected TechRepublic page.

The problem with this scenario is that the page maintains its state when accessed via the browser's Back button; that is, the page is presented in the state the user left it. In our example, the user returns with the first value in the first list still selected. So, when the first entry in the second list is selected, the ASP.NET code still recognizes the index of the first list being changed and fires its event before ever having the chance to fire the event for the second list.

A quick Web search yields a few sites outlining this problem. There are various resolutions, with the most extreme attempting to disable the Back button in the browser. Fortunately, such drastic action is not necessary; you can simply use a bit of code to reset the DropDownList controls.

The solution

The problem with our page is the DropDownList controls maintaining state when accessed via the Back button. The solution is to reset the lists every time the page loads. This can easily be accomplished with some JavaScript.

The following two lines of JavaScript reset our lists to the first entry:

document.getElementById('ddlRedirect1').selectedIndex = 0;
document.getElementById('ddlRedirect2').selectedIndex = 0;

The trick is to run these two lines every time the page loads. This is accomplished by placing it in the onload event of the HTML page's BODY element. So, the following HTML snippet would do the trick:

<html>

<head>

<script language='JavaScript'>

function onloadFunction()

{

document.getElementById('ddlRedirect1').selectedIndex = 0;

document.getElementById('ddlRedirect2').selectedIndex = 0;

}

</script>

</head>

<body onload="onLoadFunction">

Since we are using ASP.NET, we can take advantage of its programming model and use the ClientScript object (use Page object in ASP.NET 1.x) to register a JavaScript function to run when the page is loaded. We can place the following code block in the Web form's PAGE_LOAD event:

if (!Page.IsPostBack) {

if (!(ClientScript.IsClientScriptBlockRegistered("onload"))) {

System.Text.StringBuilder sb = new System.Text.StringBuilder();

sb.Append("<script language='JavaScript'>");

sb.Append("  document.getElementById('ddlRedirect1').selectedIndex = 0;");

sb.Append("  document.getElementById('ddlRedirect2').selectedIndex = 0;");

sb.Append("</script>");

ClientScript.RegisterStartupScript(this.GetType(), "onload", sb.ToString());

} }

The code first checks to be sure the onload event has not already been registered via the ClientScript's IsClientScriptBlockRegistered method. If not, a StringBuilder object is created, and the JavaScript code is added to it. Finally, the script is added to the page via the RegisterStartupScript method of the ClientScript class. The result is the JavaScript code executes whenever the page loads, so the lists are reset each time, and the problem is avoided.

Browser support

The problem with designing a solution is determining whether it runs as planned in all supported browsers. A major problem with our solution is that it fails in Firefox and even Chrome because those browsers do not fire the onload event every time the page loads, but these browsers do provide the pageshow event that is fired whenever a page loads. We can add this event listener to the form to make the solution work in Firefox and Chrome.

The following JavaScript code block can be added to the HEAD element of the page:

<script type="text/javascript">

window.addEventListener( 'pageshow', function pageShowTest() {

document.getElementById('ddlRedirect1').selectedIndex = 0;

document.getElementById('ddlRedirect2').selectedIndex = 0;}, false);

</script>

This script block can be used by Firefox, Chrome, and any derivatives.

Internet Explorer 7

While the solution up to this point does work with IE 6, it does not perform as well with IE 7. Support for IE 7 may be installed by setting up the window.onload event with JavaScript. This event is recognized by IE 7. The following script shows how it may be used:

<script type="text/javascript">

window.onload = function(){

document.getElementById('ddlRedirect1').selectedIndex = 0;

document.getElementById('ddlRedirect2').selectedIndex = 0;

</script>

I have not had the chance to test every browser and version, so please let me know of issues with other browsers.

Putting my skills to the test

A good developer has a vast skill set, with problem solving being one of their most important assets. The behavior described in this column is not exactly a bug, but it does present an issue that requires critical thinking and knowledge of how a Web page works to find a solution.

Have you encountered the situation (or something similar) that I outline above? If so, how did you address it? Share your experience with the Web Developer community.

Tony Patton began his professional career as an application developer earning Java, VB, Lotus, and XML certifications to bolster his knowledge.

-------------------------------------------------------------------------------------------------------------------

Get weekly development tips in your inbox Keep your developer skills sharp by signing up for TechRepublic's free Web Developer newsletter, delivered each Tuesday. Automatically subscribe today!

About

Tony Patton has worn many hats over his 15+ years in the IT industry while witnessing many technologies come and go. He currently focuses on .NET and Web Development while trying to grasp the many facets of supporting such technologies in a productio...

3 comments
paulor0
paulor0

Though it's interesting for the problem and solution, using a drop down list as a menu doesn't seem a good practice.

Justin James
Justin James

Tony - You just gave me the answer to something that I've been working around forever, thanks! J.Ja

D Walker
D Walker

A menu may not be as usable if the selections for the dropdown list is variable - most likely built from a result or resource file. Then the dropdown list would more likely be the same position regardless of the number if items. Navigation could then use a dropdown list for each related group of web sites in fixed page positions without having to expand a menu. Just a thought from a relative newbie to web programming. Makes mainframe CICS seem simple. Dennis

Editor's Picks