I <3 Steve McConnell*
Coding Horror
programming and human factors
by Jeff Atwood

December 20, 2004

The Antidote to ASP.NET Smart Navigation, Part Deux

In The Antidote to ASP.NET Smart Navigation, I mentioned an article that contained a clever solution to the scrolling problem with long ASP.NET forms and postbacks. Well, I recently located an even better version of that solution from Scott Mitchell, aka the founder of 4GuysFromRolla. I refactored it a bit into this:

''' <summary>
''' automatically preserves postback scroll position
''' </summary>
Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRender
  RegisterHiddenField("scrollLeft", Convert.ToString(Request.Form("scrollLeft")))
  RegisterHiddenField("scrollTop", Convert.ToString(Request.Form("scrollTop")))

  Dim sb As New System.Text.StringBuilder(1000)
  Dim nl As String = Environment.NewLine
  With sb
    .Append("<script language = ""javascript"">")
    .Append(String.Concat(nl, "<!--", nl))
    .Append("function SmartScroller_GetCoords() {")
    .Append(String.Concat(nl, "var scrollX, scrollY;", nl))
    .Append("if (document.all) {")
    .Append(String.Concat(nl, "if (!document.documentElement.scrollLeft)", nl))
    .Append("scrollX = document.body.scrollLeft;")
    .Append(String.Concat(nl, "else", nl))
    .Append("scrollX = document.documentElement.scrollLeft;")
    .Append(String.Concat(nl, "if (!document.documentElement.scrollTop)", nl))
    .Append("scrollY = document.body.scrollTop;")
    .Append(String.Concat(nl, "else", nl))
    .Append("scrollY = document.documentElement.scrollTop; }")
    .Append(String.Concat(nl, "else {", nl))
    .Append("scrollX = window.pageXOffset; scrollY = window.pageYOffset; }")
    .Append(String.Concat(nl, nl))
    .Append("document.getElementById('scrollLeft').value = scrollX;")
    .Append(nl)
    .Append("document.getElementById('scrollTop').value = scrollY;")
    .Append(String.Concat(nl, "}", nl))

    .Append(String.Concat(nl, "function SmartScroller_Scroll() {", nl))
    .Append("var x = document.getElementById('scrollLeft').value;")
    .Append(String.Concat(nl, "var y = document.getElementById('scrollTop').value;", nl))
    .Append("window.scrollTo(x, y); }")

    .Append(String.Concat(nl, nl, "window.onload = SmartScroller_Scroll;"))
    .Append(String.Concat(nl, "window.onscroll = SmartScroller_GetCoords;", nl))
    .Append("window.onclick = SmartScroller_GetCoords; window.onkeypress = SmartScroller_GetCoords;")
    .Append(String.Concat(nl, "// -->", nl))
    .Append("</script>")
  End With

  RegisterClientScriptBlock("scrollCode", sb.ToString)
End Sub

Works great! Of course, you can insert this script any number of ways: in an inherited base page, as a server control, or by directly inserting it into a single page.

Posted by Jeff Atwood    View blog reactions
« Is DoEvents Evil?
The Last Configuration Section Handler.. »
Comments

I canīt catch the onclick event. I test width window.onclick=??.. and window.document.click=??.. but the function never runs

Henry C on July 7, 2005 2:16 PM

I'm not sure; this has always worked for us. The only problem we've seen is when other JavaScript on the page happened to use the same variable names. Does this work if you isolate it so there is no (or very little) other JavaScript running on the page?

Jeff Atwood on July 7, 2005 4:52 PM

excelent, thx

minhauzen on July 19, 2005 2:03 AM

This doesn't appear to work in Netscape (i'm using 7.02) - is it supposed to?

Thnx

gordon on September 15, 2005 9:13 AM

awesome! some of the methods used are deprecated in .net 2, but this works very nicely once you fix them.

specifically, you'll need to change:

RegisterClientScriptBlock("scrollCode", sb.ToString)
to
ClientScript.RegisterClientScriptBlock(Me.GetType(), "scrollCode", sb.ToString)

and

RegisterHiddenField("scrollLeft", Convert.ToString(Request.Form("scrollLeft")))
RegisterHiddenField("scrollTop", Convert.ToString(Request.Form("scrollTop")))
to
ClientScript.RegisterHiddenField("scrollLeft", Convert.ToString(Request.Form("scrollLeft")))
ClientScript.RegisterHiddenField("scrollTop", Convert.ToString(Request.Form("scrollTop")))

cheers!

Chris on March 14, 2006 3:37 PM

Works Great !

Thanks

Bikram on August 17, 2006 11:11 AM

I tested this code in both C# and VB.
This code is working fine for VB but not for C#.
Is there any changes required for c#.

Rama on January 31, 2007 4:02 AM

The code works fine but it flickers, sometimes it goes top and then scroll down, did any one have a fix for that.

Sammy Boy on July 9, 2007 8:33 AM

Thanks much for the post. Works great...

Here's the C# version :

private void Page_PreRender(object sender, EventArgs e)
{
ClientScript.RegisterHiddenField("scrollLeft", Convert.ToString(Request.Form["scrollLeft"]));
ClientScript.RegisterHiddenField("scrollTop", Convert.ToString(Request.Form["scrollTop"]));

StringBuilder sb = new StringBuilder(1000);
String nl = Environment.NewLine;

sb.Append(@"<script language = ""javascript"">");
sb.Append(String.Concat(nl, "<!--", nl));
sb.Append("function SmartScroller_GetCoords() {");
sb.Append(String.Concat(nl, "var scrollX, scrollY;", nl));
sb.Append("if (document.all) {");
sb.Append(String.Concat(nl, "if (!document.documentElement.scrollLeft)", nl));
sb.Append("scrollX = document.body.scrollLeft;");
sb.Append(String.Concat(nl, "else", nl));
sb.Append("scrollX = document.documentElement.scrollLeft;");
sb.Append(String.Concat(nl, "if (!document.documentElement.scrollTop)", nl));
sb.Append("scrollY = document.body.scrollTop;");
sb.Append(String.Concat(nl, "else", nl));
sb.Append("scrollY = document.documentElement.scrollTop; }");
sb.Append(String.Concat(nl, "else {", nl));
sb.Append("scrollX = window.pageXOffset; scrollY = window.pageYOffset; }");
sb.Append(String.Concat(nl, nl));
sb.Append("document.getElementById('scrollLeft').value = scrollX;");
sb.Append(nl);
sb.Append("document.getElementById('scrollTop').value = scrollY;");
sb.Append(String.Concat(nl, "}", nl));

sb.Append(String.Concat(nl, "function SmartScroller_Scroll() {", nl));
sb.Append("var x = document.getElementById('scrollLeft').value;");
sb.Append(String.Concat(nl, "var y = document.getElementById('scrollTop').value;", nl));
sb.Append("window.scrollTo(x, y); }");

sb.Append(String.Concat(nl, nl, "window.onload = SmartScroller_Scroll;"));
sb.Append(String.Concat(nl, "window.onscroll = SmartScroller_GetCoords;", nl));
sb.Append("window.onclick = SmartScroller_GetCoords; window.onkeypress = SmartScroller_GetCoords;");
sb.Append(String.Concat(nl, "// -->", nl));
sb.Append("</script>");

ClientScript.RegisterClientScriptBlock(this.GetType(), "scrollCode", sb.ToString());
}

Anon on August 3, 2007 9:29 AM

testing

Anon on August 3, 2007 9:54 AM

Its very nice Post and its working for me,it solves the problem with smartnavigation.

Rajasekhar on August 23, 2007 10:48 PM

Hey, you're stepping on my onload :)


var oldEvtR = window.onload;
window.onload = function() { if (oldEvtR) oldEvtR(); SmartScroller_Scroll(); };

is nicer because it won't block other onloads.

(*untested aircode!)

Jim on September 10, 2008 1:40 PM

Nice stuff to read.

Bharath Reddy VasiReddy on October 29, 2008 2:43 PM

ya this is implemented and working as expected thanks a lot

arunkumardct@hotmail.com

arun on December 1, 2008 11:50 PM

Excellent!!

Maria on December 2, 2008 6:33 AM






(no HTML)


Verification (needed to reduce spam):


Content (c) 2009 Jeff Atwood. Logo image used with permission of the author. (c) 1993 Steven C. McConnell. All Rights Reserved.