Retain scroll position after browser refresh

Retain scroll position after browser refresh
As Joel Spolsky points out, updating web pages by getting just the bits of information that have changed instead of refreshing the whole page is the wave of the future. Whether we’re calling web services within a client-side page or using a gmail-like JavaScript technique (darn cool), it’s going to be a major architectural shift. It’s going to be hard.

In the meantime, there is a really easy way to fix the problem of having to scroll back down to the part of the page you were using when a refresh occurred. Because ASP.NET’s post-back model forces a trip to the server for anything interesting to happen, this technique is essential if you’re using ASP.NET. However, it works just as well in Perl, PHP, JSP – whatever your server-side technology. It requires client-side JavaScript to be enabled, but so do the fancier techniques we are looking forward to.
Note that you could also just turn “smart navigation” on in ASP.NET, but I’ve had issues where smart navigation messes up other JavaScript. I prefer not to use it.
I’ve implemented an example of the technique for this post separately in PHP and ASP.NET/C#. You can see the PHP example in action or download a zip file of the ASP.NET/C# example. For both examples, I use hidden fields and client-side script. They imply an http POST, and typically you will be posting to the same page. You could make it work with GET and/or with another page, but the problem itself gets more confusing in those scenarios.

Here is the complete PHP example:

Here is the complete PHP example:

<HTML>

<HEAD>

<TITLE>Test</TITLE>

<script>

function SaveScrollXY() {

document.Form1.ScrollX.value = document.body.scrollLeft;

document.Form1.ScrollY.value = document.body.scrollTop;

}

function ResetScrollPosition() {

var hidx, hidy;

hidx = document.Form1.ScrollX;

hidy = document.Form1.ScrollY;

if (typeof hidx != ‘undefined’ && typeof hidy != ‘undefined’) {

window.scrollTo(hidx.value, hidy.value);

}

}

</script>

</HEAD>

<BODY onload=”ResetScrollPosition()”>

<form name=”Form1″ id=”Form1″ method=”post”

onsubmit=”SaveScrollXY()” action=”index.php”>

<input name=”ScrollX” id=”ScrollX” type=”hidden”

value=”<?php echo $_REQUEST[‘ScrollX’] ?>” />

<input name=”ScrollY” id=”ScrollY” type=”hidden”

value=”<?php echo $_REQUEST[‘ScrollY’] ?>” />

<p>This is just a paragraph to make a very long page.</p>

<P>This is just a paragraph to make a very long page.</P>

<P>

<input name=”TextBox1″ type=”text”

value=”<?php $v = $_REQUEST[‘TextBox1’]; echo $v ? $v + 1 : 1 ?>”

readonly=”readonly” id=”TextBox1″ /></P>

<P>

<input type=”submit” name=”Button1″ value=”Post Form”

id=”Button1″ /></P>

</form>

</BODY>

</HTML>


The interesting parts are bolded. You need two JavaScript methods, one to save the scroll position before the form is posted and one to reset the page to that scroll position when it’s reloaded. (I can’t remember what the checks for undefined are for – I think I didn’t want to initialize the hiddens in ASP.NET.) Then you need two hidden inputs to store the scroll position in such a way that it will get sent to the server and make the return trip back.
Here’s where PHP and ASP.NET differ. PHP requires you to set the hidden’s value on your own (at least as far as I know how to do it):

<!– /* Font Definitions */ @font-face {font-family:”Cambria Math”; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:161; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:-1610611985 1107304683 0 0 159 0;} @font-face {font-family:Calibri; panose-1:2 15 5 2 2 2 4 3 2 4; mso-font-charset:161; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-1610611985 1073750139 0 0 159 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:””; margin-top:0cm; margin-right:0cm; margin-bottom:10.0pt; margin-left:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:”Calibri”,”sans-serif”; mso-fareast-font-family:”Times New Roman”; mso-bidi-font-family:”Times New Roman”;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:10.0pt; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt; mso-ascii-font-family:Calibri; mso-hansi-font-family:Calibri;} @page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.Section1 {page:Section1;} –>

<input name=”ScrollY” id=”ScrollY” type=”hidden” value=”<?php echo $_REQUEST[‘ScrollY’] ?>” />

ASP.NET just requires you to specify that the hidden is a “server-side control”:

<!– /* Font Definitions */ @font-face {font-family:”Cambria Math”; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:161; mso-generic-font-family:roman; mso-font-pitch:variable; mso-font-signature:-1610611985 1107304683 0 0 159 0;} @font-face {font-family:Calibri; panose-1:2 15 5 2 2 2 4 3 2 4; mso-font-charset:161; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-1610611985 1073750139 0 0 159 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:””; margin-top:0cm; margin-right:0cm; margin-bottom:10.0pt; margin-left:0cm; line-height:115%; mso-pagination:widow-orphan; font-size:11.0pt; font-family:”Calibri”,”sans-serif”; mso-fareast-font-family:”Times New Roman”; mso-bidi-font-family:”Times New Roman”;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:10.0pt; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt; mso-ascii-font-family:Calibri; mso-hansi-font-family:Calibri;} @page Section1 {size:612.0pt 792.0pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:36.0pt; mso-footer-margin:36.0pt; mso-paper-source:0;} div.Section1 {page:Section1;} –>

<input id=”ScrollY” type=”hidden” value=”0″ name=”ScrollY” runat=”server”>

Other than that, the techniques are nearly identical on the two platforms. The actual action taken by posting the form is implemented differently as well, but that is not relevant to the technique.
This isn’t a fancy technique, but it gets one job done reasonably well – on a fast connection, quite well in fact. It doesn’t reduce the amount of network traffic, and it doesn’t eliminate some annoyances with the brower’s back button, but every little usability enhancement removes another obstacle to a project’s success.

8 thoughts on “Retain scroll position after browser refresh”

  1. Hi,

    This code is great. However, it doesn’t seem to work on pages containing more than one form. I am trying to maintain a scroll position using PHP and javascript for a page containing multiple forms. Any ideas on how I could adjust this code to work for this case?

  2. Try passing the values ScrollX, ScrollY in every form you need to retain position….
    just add the hidden fields and add the SaveScrollXY() function onSubmit

    just make forms with different names, but you can keep the name and values of the hidden fields.

  3. Unfortunately, I have found a couple of problems with this script, and I really really really want it to work.

    1) You have to have your php.ini set so that register_globals = on. This was not a problem for me, just info for anybody having difficulties in getting it working.

    2) I found that having the opening html lines keeps this script from functioning.

    I don’t know why it keeps it from working. If I take out those lines, it breaks my CSS and my page goes to hell in a hand-basket ;-( If anybody has a work around, I’d love to hear it.

    Thanks,

    Mitch

  4. I really dont know why wordpress wont let me insert html code (or any other invalid html character into a post)
    I use the html decode tool, but unfortunately the second time i try to edit a single line … everything goes boom …

    I ll soon transfer to a new domain, hosting my blog … So i ll be able to host live examples in every web related post …

Leave a Reply

Your email address will not be published. Required fields are marked *