Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Client side scrollable dataset

0.00/5 (No votes)
4 Apr 2005 1  
How to achieve a scrolling effect for you dataset from JavaScript.

Sample Image

Introduction

Rich clients in web applications seem to be the �trendy� way of constructing user interfaces. The pursuit of seamless user experience without the start-stop nature of the traditional webapps imposes various �restrictions� on how to do things.

I had to construct a UI for a web application and it had to be fast, at least that was what I wanted to achieve. It was until the very-end that I realized that the �UI responsiveness� effect that I was trying to achieve with the various JavaScript tricks was undermined by the long times of loading data from the server. It would be much faster to use paging in the large datasets, but it�s obvious that there is no desktop application with paging in its tables.

I thought it would be beautiful to have a scrollbar and load each time only a fraction of the total recordset. This would give to the user the experience I wanted, as he could scroll the table at any speed without having to wait for several seconds at the beginning �

Solution

But there is no scrollbar widget you can use in an HTML page and there was only one way to display a scrollbar in an HTML page, and this was through the overflow CSS property. In order to show a scroll bar, we need an HTML code like the one below:

<div id="scrollBar" style="overflow:scroll;height:100px;width:17px">
<div id=�valueBar� style=�height=200px�></div>
</div>

Then an event handler can be attached on the onscroll event of scrollBar and we can calculate the percentage of the scrolling. I created a JavaScript to do that:

VerticalScrollBar.prototype.attach = function (oId) {

    if (document.getElementById(oId)!=null) {
        elem = document.getElementById(oId);
        if (elem.tagName!="DIV") {
            alert("VerticalScrollBar Init : Please attach me to a div");
            return;
        }
        elem.onscroll = this._onScroll;
        elem.object = this;
        this.scrollBar = elem;
        vbar = document.createElement('div');
        vbar.style.height =  elem.style.pixelHeight*this.maxValue;
        elem.appendChild(vbar);
    }
};

oId is the parent div ID. The parent div should have its style set to �overflow:scroll;width:17px;height:someValue�.

Then I create a second div with size parentSize*maxValue, and I append it inside the parent div and attach an event handler on the onscroll event of the parent.

VerticalScrollBar.prototype._onScroll = function () {
    st = event.srcElement.scrollTop ;
    sh = event.srcElement.scrollHeight ;
    //calculating the % percentage of the position 

    sp = st * (100/sh);
    s = (event.srcElement.object.maxValue)*(sp/100);
    event.srcElement.object.value=Math.round(s);
    if (event.srcElement.object.onScroll!=null)
        event.srcElement.object.onScroll.call();
};

The only point of interest here is the following snippet:

    sp = st * (100/sh);
    s = (event.srcElement.object.maxValue)*(sp/100);

where the percentage of scrolling is calculated and multiplied with the maximum value of the scrollbar. I must notice here that even when the scrollbar is fully scrolled, the scrollHeight is more than the scrollTop (I was expecting to be the same). I don�t know why this is happening and would really like to now.

So after the scrollbar is created, we can use it to get the scroll position and request the appropriate part of the recordset.

The webservice

The webservice that returns the record set should be something like this:

public struct QueryResult 
{
    public int offset;
    public int count;
    public int totalCount;
    public string[] result;
}
/// <summary>

/// A webservice method that returns the data 

/// takes two parameters 

/// </summary>

/// <param name="s">From where to start fetching records</param>

/// <param name="count">The recordcount</param>

/// <returns></returns>

[WebMethod(EnableSession=true)]
public QueryResult GetRecords(int s,int count)
{
    QueryResult qres = new QueryResult();
    string[] ar = GetRecordset();
    qres.totalCount = ar.Length;
    string[] res = new string[count];
    for (int i=0;i<count;i++) 
    {
        if ((i+s)<ar.Length)
            res[i]=ar[i+s];
        else 
        {
            qres.count = i;
            qres.offset = s;
            qres.result = res;
            return qres;
        }
    }
    qres.count = count;
    qres.offset = s;
    qres.result = res;
    return qres;
}

The full recordset is stored in the session, and depending on the supplied parameters, we fetch only a portion of it. A struct is used in order to avoid the creation of a second method that returns the total count of the records.

The client side code

I am used to the webservice behavior so I am going to use it to create the client side code that interacts with the webservice. The method call to GetRecords will be synchronous as we do not want to mess with asynchronous calls' handling. So we create a JavaScript function _getRecords that calls synchronously the webservice and returns the result set.

function _getRecords(s,count) 
{ 
    //creating the call object for the synch call to the W/S 

    var callObj = new Object(); 
    callObj.funcName = "GetRecords"; // Name of the remote function. 

    callObj.async = false; // A Boolean that specifies the type of call 

    callObj.timeout = 5; // Timeout value for the method call (seconds) 

    callObj.SOAPHeader = ""; callObj.SOAPHeader += ""; 
    callObj.SOAPHeader += 5; 
    callObj.SOAPHeader += ""; callObj.SOAPHeader += ""; 
    result = service.Service.callService(callObj, s,count); 
    if (!result.error) {
        return result.value; 
    } else { 
        alert("Error:"+result.errorDetail.string); 
    } 
}

On the body onload event, we assign an event handler that initializes the webservice behavior and the scrollbar. When the webservice is available, the setup() function is executed that initializes the scrollbar.

function setup(){ 
    res1 = _getRecords(0,0); 
    totalRecords =res1.totalCount; 
    vb = new VerticalScrollBar(totalRecords);
    vb.onScroll = vbscroll; vb.attach('sbar'); 
};

After that the event handler for the scrolling is trivial.

function vbscroll() { 
    getRecords(vb.value); 
};

The only problem here that I couldn�t figure out how to solve, is how to determine automatically the number of the requested rows depending on the table�s size. That's why the number of the requested rows is manually set to a number that does not expand the table a lot. From a few tries, I found that 16 is okay for the table height I�ve specified.

Conclusion

Due to the nature of the medium, a lot of times we have to give up to workarounds like this to achieve some specific effect.

So, I doubt whether this is an elegant way to create the desired effect but it�s pretty catchy especially from the user�s point of view, as it provides the responsiveness the user demands from a rich client.

That�s all.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here