I'd really appreciate some help on this issue. I've scoured SO and Google looking for a solution to this problem and haven't found anything that works 100%. Here's my problem (bear with me on the description of how the page is setup):
I have a page that is contained within a master page. This page uses the AjaxControlToolkit TabContainer that is within an UpdatePanel. Each TabPanel loads a different user control and each user control can contain 1:M input forms that display based on whether a user wants to add/edit data within the control. All of this functionality works great but sometimes those forms go out of view within the viewport.
To minimize the amount of scrolling a user has to do, I've implemented a jQuery animate event handler using the PageRequestManager. Essentially what I am doing is calling a ShowForm server side method which in turn builds some JavaScript and injects it into the page on endRequest. Here's the server side code:
private void ShowForm(bool pShowForm) {
fsAdd.Visible = pShowForm;
if (pShowForm) {
LoadScript(FocusOnFormScript("control_client_id"), "control_client_id");
}
}
protected void DropDownList_OnSelectedIndexChanged(object sender, EventArgs e) {
//SelectedIndexChanged processing.
MaintainFocusOnControl(KeepFocusOnFormScript("control_client_id"), "control_client_id");
}
private void LoadScript(string pScript, string pControlId) {
ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "focusControl_" + pControlId, pScript, true);
}
private void MaintainFocusOnControl(string pScript, string pControlId) {
ScriptManager.RegisterStartupScript(this.Page, this.Page.GetType(), "maintainFocus_" + pControlId, pScript, true);
}
/// <summary>
/// Scrolls to the form when it is made visible
/// </summary>
/// <param name="pControlId">The ClientID of the control to focus on after the form is made visible</param>
/// <returns></returns>
private string FocusOnFormScript(string pControlId) {
string script = @"
function FocusOnForm() {
var offset = $('#" + pControlId + @"').offset();
$('html, body').animate({
scrollTop: offset.top+200,
scrollLeft: offset.left+200},
'slow',
'easeOutQuart'
);
/* This removes the event from the PageRequestManager immediately after the desired functionality is completed so that multiple events are not added */
prm.remove_endRequest(FocusOnFormCaller);
}
prm.add_endRequest(FocusOnFormCaller);
function FocusOnFormCaller(sender, args) {
if (args.get_error() == undefined) {
FocusOnForm();
}
}";
return script;
}
/// <summary>
/// Scrolls to the form when it is made visible
/// </summary>
/// <param name="pControlId">The ClientID of the control to focus on after the form is made visible</param>
/// <returns></returns>
private string KeepFocusOnFormScript(string pControlId) {
string script = @"
function KeepFocusOnForm() {
var offset = $('#" + pControlId + @"').offset();
window.scrollTo(offset.left+500, offset.top+500); //just jump to the position; scrolling not needed
/* This removes the event from the PageRequestManager immediately after the desired functionality is completed so that multiple events are not added */
prm.remove_endRequest(KeepFocusOnFormCaller);
}
prm.add_endRequest(KeepFocusOnFormCaller);
function KeepFocusOnFormCaller(sender, args) {
if (args.get_error() == undefined) {
KeepFocusOnForm();
}
}";
return script;
}
This code works great 99% of the time. The 1% issue occurs for one particular form I have that requires cascading DropDownLists. When DropDownList1 is selected, DropDownList2 is populated correctly but the page's scroll position is lost and the view of DropDownList2 is moved to the very bottom of the viewport (there's another list and form that are below the form I am describing now).
I've tried a lot of things including:
What I find weird is that if I set up a manual link that calls window.scrollTo or document.documentElement.scrollTop, it works. I can also just use the jQuery animate event handler that's already registered but then the page scrolls to the top after the UpdatePanel refresh and then scrolls down again. If I replace the animate code with window.scrollTo or document.documentElement.scrollTop, it doesn't work.
What I normally do is to store the scrollPos in hidden textboxes and when the page is updated through the postback, get the values from the textboxes before rending and then scroll to that part of the screen via some javascript...