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 02: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 04:52 PM

excelent, thx

minhauzen on July 19, 2005 02: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 09: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 03: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 04: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 08: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 09:29 AM

testing

Anon on August 3, 2007 09: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 01:40 PM







(hear it spoken)


(no HTML)




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