views:

183

answers:

6

I have an ASP.NET page that contains two div's. Both have search fields and a search button contained within each of them. When I first come to this page, Div A has the class 'SearchDiv' while Div B has 'SearchDivDisabled'. These classes change the appearance so the user knows which search type they currently have enabled.

When Div B is clicked, JavaScript changes it's class to 'SearchDiv', and changes Div A to 'SearchDivDisabled'. This all works like a charm. The problem I have is when a user changes to Div B, clicks Div B's search button (which obviously redirects to a results page), and then uses the browser's back button. When they return to the search page, Div A is enabled again, and Div B is Disabled, even though they last used Div B. In the search button event handler I set the class attribute of the Divs before I redirect, hoping this will update the page on the server so when the user returns, their last-enabled Div will still be enabled (regardless of which one was enabled when the page was first visited).

I believe this involves the ViewState, but I'm unsure why the class attribute is not saved so when the user returns to the page it is restored. Is there something I'm missing here, or some easy way to guarantee the behavior I want? Thanks!

Edit: Here is the button event handler code:

protected void RedirectToResults(int searchEnum, string resultPage)
{
    ShowContainer(searchEnum);
    Response.Redirect(resultPage + this.webParams.getAllVariablesString(null));
}

  protected void ShowContainer(int searchContainerToShow)
  {
     if (searchContainerToShow < 0 || searchContainerToShow > SearchContainers.Count || SearchContainers.Count == 0)
        return;

     //disable all search panels
     foreach (SearchContainer container in SearchContainers.Values)
     {
        container.searchDiv.Attributes.Add("class", "SearchDivDisabled");
     }

     //enable selected panel
     SearchContainers[searchContainerToShow].searchDiv.Attributes.Add("class", "SearchDiv");
  }

RedirectToResults() is called from the actual button event handler with the enum representing the selected search panel and the results page url. SearchContainers is a dictionary mapping an integer to the search Div. The important code is the last line, where I'm updating the selected search container with the 'active' search class, rather than the disabled one (which I assign to the other div(s) )

Additional Update: I have been battling with this issue for the last couple days. I was sort of able to get the following code to work (in page_load):

        Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
        Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
        Response.AppendHeader("Expires", "0"); // Proxies.

But this really isn't a solution, as everything else that gets cached correctly is lost, which leaves me worse off than when I started. Everything else seems to persist fine, it is just the class of the div that is causing me struggles.

Edit: Just wanted to update this for anyone else that comes across this. While I believe there is a solution here with setting the cacheability of the page to force the browser to postback, I could not get it working 100% with all browsers (primarily Firefox was giving me fits, which is a documented bug). I do believe the cookie solution would work, but I felt that may be a bit more complex than necessary for just trying to store the state of a couple div's.

What I ended up doing was tying the div's class to the state of it's correlating radio button (there are radio buttons next to the div which allow the users a more visual way of enabling search panels). I noticed these radio buttons retained the correct checked value when the back button was used, so I could guarantee they would indicate the correct div to be enabled. So in JavaScript's onload I check which radio button is enabled, and then adjust the classes of the search div's accordingly. This is a pretty big hack, but has worked 100% across all browsers, and only took about 10 lines of JavaScript.

+1  A: 

Hello,

I don't think ViewState is the issue here; it may be something that needs managed in client-side javascript code, as switching states on the client doesn't get reflected automatically on the server...

What you want to look at potentially is the management of browser history through the history points feature: http://msdn.microsoft.com/en-us/library/cc488548.aspx

HTH.

Brian
+4  A: 

Because you are not posting back to change the div's style, when the user hits the back button it is taking it back to how the page was originally posted. The easy way to fix this is to have the button cause a postback that toggles the style.

fizch
As I mention above, 'In the search button event handler I set the class attribute of the Divs before I redirect, hoping this will update the page on the server so when the user returns, their last-enabled Div will still be enabled' So I am doing this. I will update the original post with the code.
Kevin
A: 

You could store the id of the div which is currently set to 'SearchDiv' class in a SESSION variable before doing a redirect in the search button, during onload of the search page get the div and set appropriate class on it.
This way you could continue to use your javascript to toggle classes on the divs and only save the data before redirecting to a different page and avoid unnecessary post

Vinay B R
The problem with this is that OnLoad() doesn't fire when the back button is used. So regardless of what gets stored in the session, I can't change anything when the user goes back to the search page as it's all client side.
Kevin
Kevin, the OnLoad() in the search page is not getting fired cos the page is fetched from cache. Add Response.Cache.SetCacheability(HttpCacheability.NoCache);in your search page's Page_Load function and see if the event is fired or not when you click on back
Vinay B R
+1  A: 

As mentioned in my previous answer you can acheive all that you need using session variables. Since you had mentioned about persisting DIV selection i had asked you to have that in a session variable. If you want other form data to be persisted as well dump them all in session variables(make sure that you clean them up as and when you are done with them)

This would be the easiest way for you to meet your requirement.

Also you are using

Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
    Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
    Response.AppendHeader("Expires", "0"); // Proxies.

you can simple use

Response.Cache.SetCacheability(HttpCacheability.Private);

in your Page_load and acheive the same functionality

Let me know if you have any sppecific concerns implementing this.

Vinay B R
Just wanted to add a comment to this. Firefox is a problem with this, as it basically disregards the 'Cacheability' flag on a page and will cache whatever it wants. In IE this tends to work, but if you want to be supported cross browser I haven't found a good way to force FF to postback when using 'back' to navigate to a page.
Kevin
+3  A: 

Store the setting in a cookie on the client side, then check the cookie via JavaScript on load of the page and change the CSS class. Other ways of fixing this might not work because the page isn't always requested from the server when a user hits the Back button.

using the jQuery cookie plugin

// this function will update the style of the divs based on the cookie's settings
function updateClass(){
    var val = $.cookie('myCookieName');

    // set your div's class
    if (!val || val=='divA')
    {
        $('#divA').removeClass('SearchDivDisabled');
        $('#divA').addClass('SearchDiv');
        $('#divB').removeClass('SearchDiv');
        $('#divB').addClass('SearchDivDisabled');
    }else{
        $('#divB').removeClass('SearchDivDisabled');
        $('#divB').addClass('SearchDiv');
        $('#divA').removeClass('SearchDiv');
        $('#divA').addClass('SearchDivDisabled');
    }
}

// call this passing in 'divA' or 'divB' depending on which is selected
function updatePage(selectedDiv){
    $.cookie('myCookieName', selectedDiv, { path: '/', expires: 10 });
    updateClass();
}

// change the class of the divs when the page is finished rendering
$(document).ready(function(){updateClass();}):
Pete Amundson
+1  A: 

This could be addressed in one two ways.

If each search form posts to a different URL, then each URL should set a session variable, called 'LastSearchMethd, for example, to either 'A' or 'B'. If they both post to the same URL, then simply include a hidden field that contains either 'A' or 'B' for each form respectively, and save the submitted value in the session variable.

Either way, you then need to render the style of each DIV, depending on the value of the session variable.

As mentioned by other posters, getting the caching correct will also be a consideration.

belugabob