Click here to Skip to main content
13,141,068 members (49,757 online)
Click here to Skip to main content
Add your own
alternative version

Stats

77.7K views
34 bookmarked
Posted 9 Sep 2007

Persisting the scroll position of child DIV’s using MS AJAX

, 9 Sep 2007
Rate this:
Please Sign up or sign in to vote.
An article on building a control using MS AJAX to persist the scroll position of child DIV's.

Introduction

While the ScriptManager and UpdatePanel found in Microsoft AJAX do a good job of persisting your pages' scroll position during partial post back operations, you might be surprised to find out the same is not the case for scrollable child DIVs contained within an UpdatePanel.

The PersistentScrollPosition control presented in this article seeks to remedy this issue, using a client-side behavior and ASP.NET server control implemented using Microsoft AJAX.

Background

While it is certainly not my intention to review the internals of the UpdatePanel and PageRequestManager or implementing any of the client-side components (Sys.Component, Sys.UI,Behavior, or Sys.UI.Control), a quick understanding can go a long way into understanding and resolving this particular problem. There are two key items to keep in mind for this control:

  1. Client-side components are disposed of and recreated during the partial post back lifecycle so you can't use the control's instance to store any data you need to survive this.
  2. The HTML output of the UpdatePanel is completely replaced during a partial post back (assuming it was triggered) through the innerHTML property, which is why the scroll position problem exists in the first place.

Using the code

For those who just want the solution, using the code is straightforward. The control has one property you need to set, named ControlToPersist. This is a string which takes the ID of the server-side container DIV (it must have runat="server").

<asp:UpdatePanel runat="server" ID="UpdatePanel" UpdateMode="always">
<ContentTemplate>
<asp:Button runat="server" ID="btnPostBack" Text="Post Back" OnClick="btnPostBack_Click" />
<br />
<div style="width:590px;height:400px;overflow-y:scroll;overflow-x:hidden;" 

     runat="server" id="persistMe">
<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit...</p>

</div>
<mbc:PersistentScrollPosition runat="server" ID="psf1" ControlToPersist="persistMe" /> 
</ContentTemplate> 
</asp:UpdatePanel>

Building the control

The control consists of two parts, both of which, for the most part, are very "cookie cutter". On the server side, we inherit from Control, and implement IScriptControl and INamingContainer, and create a HiddenField during initialization to store our scroll position in between partial post backs.

public class PersistentScrollPosition : Control, IScriptControl, INamingContainer
{
    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);

        // Create hidden control for storage

        storage = new HiddenField();
        storage.ID = "storage";
        Controls.Add(storage);
    }
}

When creating the script descriptors for the client-side initiation, we pass through the scrollable DIV's ClientID, the control's ElementID, and we pass in a reference to the HiddenField's DOM element using the AddElementProprety method of the ScriptComponentDescriptor class.

public IEnumerable<scriptdescriptor /> GetScriptDescriptors()
{
    ScriptComponentDescriptor scd = 
            new ScriptBehaviorDescriptor("Mbccs.WebControls.PersistentScrollPosition", 
            Control.ClientID);
    scd.AddElementProperty("storage", storage.ClientID);
    yield return scd;
}

On the client-side, the control inherits from the Sys.UI.Behavior base class. Upon control initialization, it hooks into two events: the scroll DOM event of the DIV, and the EndRequest event of the Sys.WebForms.PageRequestManager class. The EndRequest event is when the scroll state is restored, but I'll get to that shortly.

initialize : function() {
        Mbccs.WebControls.PersistentScrollPosition.callBaseMethod(this, 'initialize');
        
        this._scrollDelegate = Function.createDelegate(this, this._onScroll);
        this._endRequestDelegate = Function.createDelegate(this, this._onEndRequest);
        
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.add_endRequest(this._endRequestDelegate);
        
        $addHandler(this.get_element(), 'scroll', this._scrollDelegate);
}

When the DIV is scrolled, the x,y scroll position is serialized and stored in the HiddenField server-side control we created earlier.

_onScroll : function(e) {
        this._storage.value = 
             Sys.Serialization.JavaScriptSerializer.serialize(this._getScrollPosition());
    },

_getScrollPosition : function() {
        var el = this.get_element();
        
        if (el) {
            return {
                x: el.scrollLeft || 0,
                y: el.scrollTop || 0
            };
        }
    }

To prevent null's from floating around, the x,y coordinates are coerced into 0's if either the scrollLeft or scrollTop properties are null.

The EndRequest event is fired when "an asynchronous post-back is finished and the control has been returned to the browser", so it's a perfect time to restore our scroll state.

_onEndRequest : function(sender, args) {
        var o = null;        
        if(this._storage.value !== '')
            o = Sys.Serialization.JavaScriptSerializer.deserialize(this._storage.value);
            
        if (o) {
            var el = this.get_element();
            el.scrollLeft = o.x;
            el.scrollTop = o.y;
            this._storage.value = '';
        }
    }

The dispose method isn't usually a place of any particular interest, but its worthy of noting that it you need to unhook the DIV's scroll event prior to calling dispose on the base class.

dispose : function() {
        $removeHandler(this.get_element(), 'scroll', this._scrollDelegate);
        
        Mbccs.WebControls.PersistentScrollPosition.callBaseMethod(this, 'dispose');
        
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.remove_endRequest(this._endRequestDelegate);
        
        delete this._endRequestDelegate;
        delete this._scrollDelegate;
    }

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Steven Berkovitz
Web Developer
Canada Canada
Steven is VP Development at MBC Computer Solutions Ltd. (http://www.mbccs.com), a Richmond Hill based company specializing in e-Business Application Development, e-Store Solutions, Managed Co-Location and Proactive IT services.

Steven has over 10 years experience in software and hardware design and is experienced with a large array of platforms, technologies and languages.

In his spare time, Steven enjoys a wide array of music, is an avid skier and enjoys spending time with friends.

Steven is the primary contributor of MBC's blog which can be read at http://blogs.mbccs.com/mbccomputersolutions

You may also be interested in...

Pro
Pro

Comments and Discussions

 
Generaldll for framework 3.5 Pin
MaryVeranis29-Apr-10 8:35
memberMaryVeranis29-Apr-10 8:35 
Generalstorage Error Pin
AzEric14-Jul-09 9:56
memberAzEric14-Jul-09 9:56 
GeneralRe: storage Error Pin
Steven Berkovitz14-Jul-09 9:58
memberSteven Berkovitz14-Jul-09 9:58 
QuestionDoesn't Work with Web Parts? Pin
glusk11-Aug-08 10:31
memberglusk11-Aug-08 10:31 
AnswerRe: Doesn't Work with Web Parts? Pin
Steven Berkovitz11-Aug-08 10:33
memberSteven Berkovitz11-Aug-08 10:33 
GeneralYour article inspired me to create a new one. Pin
perels18-Mar-08 16:49
memberperels18-Mar-08 16:49 
GeneralUsing in webcontrols Pin
miketong30-Nov-07 5:23
membermiketong30-Nov-07 5:23 
GeneralRe: Using in webcontrols [modified] Pin
Steven Berkovitz30-Nov-07 5:24
memberSteven Berkovitz30-Nov-07 5:24 
GeneralRe: Using in webcontrols Pin
miketong30-Nov-07 5:40
membermiketong30-Nov-07 5:40 
GeneralRe: Using in webcontrols Pin
Steven Berkovitz30-Nov-07 5:44
memberSteven Berkovitz30-Nov-07 5:44 
QuestionRe: Using in webcontrols Pin
qazzer19-Jun-08 4:21
memberqazzer19-Jun-08 4:21 
AnswerRe: Using in webcontrols Pin
Steven Berkovitz19-Jun-08 4:24
memberSteven Berkovitz19-Jun-08 4:24 
Questionreset to the top? Pin
thrakazog29-Oct-07 10:15
memberthrakazog29-Oct-07 10:15 
AnswerRe: reset to the top? Pin
Steven Berkovitz29-Oct-07 10:16
memberSteven Berkovitz29-Oct-07 10:16 
GeneralConvertion to VB.Net Pin
JonDays5-Oct-07 0:00
memberJonDays5-Oct-07 0:00 
GeneralRe: Convertion to VB.Net Pin
Steven Berkovitz5-Oct-07 3:54
memberSteven Berkovitz5-Oct-07 3:54 
QuestionHow set scroll posisiton to bottom when child controls update Pin
olejohan19-Sep-07 21:56
memberolejohan19-Sep-07 21:56 
AnswerRe: How set scroll posisiton to bottom when child controls update Pin
Steven Berkovitz20-Sep-07 3:32
memberSteven Berkovitz20-Sep-07 3:32 
GeneralRe: How set scroll posisiton to bottom when child controls update Pin
olejohan20-Sep-07 20:42
memberolejohan20-Sep-07 20:42 
GeneralRe: How set scroll posisiton to bottom when child controls update Pin
Steven Berkovitz21-Sep-07 4:07
memberSteven Berkovitz21-Sep-07 4:07 
GeneralRe: How set scroll posisiton to bottom when child controls update Pin
olejohan22-Sep-07 10:13
memberolejohan22-Sep-07 10:13 
GeneralRe: How set scroll posisiton to bottom when child controls update Pin
olejohan23-Sep-07 21:21
memberolejohan23-Sep-07 21:21 
GeneralRe: How set scroll posisiton to bottom when child controls update Pin
walter113-Aug-08 2:30
memberwalter113-Aug-08 2:30 
GeneralUpdate Panel Usage Note Pin
binhex10-Sep-07 16:16
memberbinhex10-Sep-07 16:16 
GeneralRe: Update Panel Usage Note Pin
Steven Berkovitz10-Sep-07 16:24
memberSteven Berkovitz10-Sep-07 16:24 
Generalcool Pin
andalmeida10-Sep-07 5:51
memberandalmeida10-Sep-07 5:51 
GeneralGood Idea Pin
merlin98110-Sep-07 3:18
membermerlin98110-Sep-07 3:18 
GeneralRe: Good Idea Pin
Steven Berkovitz10-Sep-07 3:34
memberSteven Berkovitz10-Sep-07 3:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170915.1 | Last Updated 9 Sep 2007
Article Copyright 2007 by Steven Berkovitz
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid