How do I maintain scroll position in MVC?
Im working on a project in MVC and have enjoyed learning about it. There are a few growing pains but once you figure them out it's not bad. One thing that is really simple in the WebForms world is maintaining the scroll position on a page. All you do is set the MaintainScrollPositionOnPostback property to true. However, in MVC, Im not using postbacks so this will not work for me. What is the standard way of handling this?
Edit: Ajax is acceptable, but I was also wondering how you would do it without AJAX.
I've resolved this in JS :
$(document).scroll(function(){
localStorage['page'] = document.URL;
localStorage['scrollTop'] = $(document).scrollTop();
});
Then in document ready :
$(document).ready(function(){
if (localStorage['page'] == document.URL) {
$(document).scrollTop(localStorage['scrollTop']);
}
});
The way MaintainScrollPositionOnPostback works is that it has a pair of hidden fields: __SCROLLPOSITIONX and __SCROLLPOSITIONY
On a postback, it sets these,
function WebForm_GetScrollY() {
if (__nonMSDOMBrowser) {
return window.pageYOffset;
}
else {
if (document.documentElement && document.documentElement.scrollTop) {
return document.documentElement.scrollTop;
}
else if (document.body) {
return document.body.scrollTop;
}
}
return 0;
}
function WebForm_SaveScrollPositionSubmit() {
if (__nonMSDOMBrowser) {
theForm.elements['__SCROLLPOSITIONY'].value = window.pageYOffset;
theForm.elements['__SCROLLPOSITIONX'].value = window.pageXOffset;
}
else {
theForm.__SCROLLPOSITIONX.value = WebForm_GetScrollX();
theForm.__SCROLLPOSITIONY.value = WebForm_GetScrollY();
}
if ((typeof(this.oldSubmit) != "undefined") && (this.oldSubmit != null)) {
return this.oldSubmit();
}
return true;
}
and then it calls RestoreScrollPosition:
function WebForm_RestoreScrollPosition() {
if (__nonMSDOMBrowser) {
window.scrollTo(theForm.elements['__SCROLLPOSITIONX'].value, theForm.elements['__SCROLLPOSITIONY'].value);
}
else {
window.scrollTo(theForm.__SCROLLPOSITIONX.value, theForm.__SCROLLPOSITIONY.value);
}
if ((typeof(theForm.oldOnLoad) != "undefined") && (theForm.oldOnLoad != null)) {
return theForm.oldOnLoad();
}
return true;
}
But as most people said, MVC should be avoiding postbacks anyway.
Actually there is no standard way of handling this, this was a Microsoft hack to support their post back model. They needed this because every control did a post back and the user would constantly be pushed back to the top of the page.
The recommendation for use with MVC is to do most of your post back to servers using AJAX. So that the page doesn't have to rerender the the focus is not moved. jQuery makes AJAX really easy, and there is even default forms like
<% Ajax.BeginForm(...) %>
Which will take care of the AJAX side of things for you.
Taking inspiration from WebForms and the answer provided by Richard Gadsden, another approach using javascript and the form collection could look something like this:
@{
var scrollPositionX = string.Empty;
if(IsPost) {
scrollPositionX = Request.Form["ScrollPositionX"];
}
}
<form action="" method="post">
<input type="hidden" id="ScrollPositionX" name="ScrollPositionX" value="@scrollPositionX" />
<input type="submit" id="Submit" name="Submit" value="Go" />
</form>
$("#Submit").click(function () {
$("#ScrollPositionX").val($(document).scrollTop());
});
$("#ScrollPositionX").each(function () {
var val = parseInt($(this).val(), 10);
if (!isNaN(val))
$(document).scrollTop(val);
});
The code provided is for inspiration and is in no way prettified. It could probably be done in a few different ways, I guess it all comes down to how you decide to persist the scrollTop value of your document across the POST. It is fully working and should be cross browser safe since we are using jQuery to do the scrolling. I believe the code provided is self-explanatory, but I will be happy to provide a more detailed description on whats going on, just let me know.