Full page refreshes have mostly given way to partial updates — fetch a bit of JSON, patch the DOM, done — but the classic full-page post-back is still everywhere: classic ASP.NET Web Forms, legacy PHP apps, server-rendered frameworks doing full reloads. The annoyance is always the same: after the round-trip, the browser dumps the user back at the top of the page and they have to scroll down to where they were.
There is a simple fix that works with any server-side technology: record the scroll position before the form is submitted, send it along with the post, and restore it when the page reloads. It needs two hidden inputs and two small JavaScript functions. This works in PHP, ASP.NET, JSP, Perl — anything that re-renders the page on POST.
On ASP.NET Web Forms there is also the built-in “smart navigation” option, but I’ve had it interfere with other scripts, so I prefer the explicit approach below.
How it works
- On form
submit, copyscrollX/scrollYinto two hidden inputs so they travel with the POST. - The server round-trips the values back (PHP: echo them into the
valueattributes yourself; ASP.NET: mark the inputsrunat="server"and viewstate does it). - On
load, read the values back and callwindow.scrollTo().
Complete PHP example
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script>
function SaveScrollXY() {
document.Form1.ScrollX.value = window.scrollX;
document.Form1.ScrollY.value = window.scrollY;
}
function ResetScrollPosition() {
var x = document.Form1.ScrollX.value;
var y = document.Form1.ScrollY.value;
if (x !== "" && y !== "") {
window.scrollTo(parseInt(x, 10), parseInt(y, 10));
}
}
</script>
</head>
<body onload="ResetScrollPosition()">
<form name="Form1" id="Form1" method="post" action="index.php"
onsubmit="SaveScrollXY()">
<input name="ScrollX" id="ScrollX" type="hidden"
value="<?php echo isset($_REQUEST['ScrollX']) ? (int)$_REQUEST['ScrollX'] : 0; ?>" />
<input name="ScrollY" id="ScrollY" type="hidden"
value="<?php echo isset($_REQUEST['ScrollY']) ? (int)$_REQUEST['ScrollY'] : 0; ?>" />
<p>This is just a paragraph to make a very long page.</p>
...
<p>
<input name="TextBox1" type="text" readonly="readonly"
value="<?php $v = isset($_REQUEST['TextBox1']) ? (int)$_REQUEST['TextBox1'] : 0;
echo $v + 1; ?>" />
</p>
<p><input type="submit" name="Button1" value="Post Form" /></p>
</form>
</body>
</html>
The interesting parts are the two JavaScript functions and the two hidden inputs. The functions save the position before the form is posted and restore it when the page reloads. The hidden inputs give the values a round trip through the server. PHP makes you echo the values back into the value attributes yourself (cast to int — never echo request data unescaped).
ASP.NET / C# version
Same idea, but the hidden inputs are server-side controls, so viewstate persists their values without any code-behind:
<input id="ScrollX" type="hidden" value="0" name="ScrollX" runat="server" />
<input id="ScrollY" type="hidden" value="0" name="ScrollY" runat="server" />
Wire up the same two functions in the client script, hook onsubmit on the form and restore on window.onload. That’s the whole technique.
Modern notes
- Standards-mode browsers: the original script used
document.body.scrollLeft/scrollTop, which reads 0 on modern pages. Usewindow.scrollX/scrollY(ordocument.documentElement.scrollTopin quirks-mode pages). - Per-page restoration: for plain navigation (not form posts), the modern fix is
history.scrollRestoration = "manual"plus your ownsessionStoragepersistence — see MDN on scrollRestoration. - Browsers already try: modern browsers restore scroll on reload natively, but any DOM change between unload and restore (lazy content, late-loading images) shifts anchors and defeats it — which is exactly when the hidden-field trick still earns its keep.
It’s not fancy, and it doesn’t reduce network traffic, but as a usability patch for post-back-heavy pages it removes a real obstacle — and it’s five lines of script.
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?
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.
is there a way to retain the scrollbar of the body..?????
Can you make your question more clear? I think this post explains the exact thing…
thank u very much sir.
I am here at a forum newcomer. Until I read and deal with the forum.
Let’s learn!
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