Jump to content

scrolling big numbers

Recommended Posts

  • Moderators

Got challenged the other day by trying to scroll large numbers of rows in a listbox.

Browsers are very good at scrolling, but when row numbers become large and the scroll context becomes complex, scroll behaviour deteriorates.

A simple un-optimised scrolling div is, rule of thumb, able to scroll comfortably up to a couple of hundred rows. Depending on browser and complexity.

In the native framework I tweaked that a bit by setting all rows outside the visible viewport to 'display:none'. That extends comfortable scrolling somewhat, say to numbers in the low thousands. 

A better way is to completely separate display from content. Meaning that the content needs to be held in some kind of memory structure, not in visual components, and that scrolling is redefined to re-using a small set of visual rows confined to the scroll-window.

A structure to make that happen would be a <div> like component (TW3Panel) on a form with NativeScrolling set to true. Set up 2 child-panels on this component, where the first child-panel has dimensions equal to the maximum scrollable area, and a second childpanel dimensioned equal to this parent. The first child-panel is the scroller, which will always be completely empty. The second child-panel contains the visible rows, which will be refreshed on scroll-events. To keep this panel always in view, set the position attribute to '-webkit-sticky'.

This demo shows that Chrome can readily handle 500,000 rows, even on mobile. FF a bit less.

The good thing is that the number of rows doesn't really matter, scrolling behaviour stays the same regardless.

Would be interesting to see if performance gets even better by pushing scrolling to the GPU, pretty sure Chrome does that by default.

Infinite scrolling will be easy to implement using this structure too.


procedure TForm1.W3Button1Click(Sender: TObject);
  var column1 := TVariant.CreateArray;
  for var i := 0 to 500000 do begin

  var column2 := TVariant.CreateArray;
  for var i := 1 to 500001 do begin
  //column2.sort();                                       //sorting works too

//scrolling setup
  var rowHeight := 30;

  W3Panel.NativeScrolling := true;                        //Parent panel (in visual designer : width 408, height 266)

  var Panel1 : TW3Panel := TW3Panel.Create(W3Panel);      //scroller (always empty!)
  Panel1.SetBounds (0,0,380,column1.length*rowHeight);    //but with large height
  Panel1.BorderRadius := 0;

//set up viewport
  var Panel2 : TW3Panel := TW3Panel.Create(W3Panel);      //viewport, only the visual rows
  Panel2.SetBounds(0,0,380,262);                          //dimensions same as grid-parent
  Panel2.BorderRadius := 0;

  Panel2.handle.style.position := '-webkit-sticky';
  Panel2.handle.style.position := '-moz-sticky';
  Panel2.handle.style.position := '-ms-sticky';
  Panel2.handle.style.position := '-o-sticky';
  Panel2.handle.style.position := 'sticky';
  Panel2.handle.style.top := '0px';

//set up columns
  Var CPanel1 : TW3Panel := TW3Panel.Create(Panel2);     //column 1
  CPanel1.BorderRadius := 0;

  Var CPanel2 : TW3Panel := TW3Panel.Create(Panel2);     //column 2
  CPanel2.BorderRadius := 0;

  //initial fill viewport prior to first onscroll event
  for var j := 0 to 8 do begin                           //only first couple of rows
    var x : TW3Panel := TW3Panel.Create(CPanel1);        //column 1
    x.BorderRadius := 0;
    x.innerHTML := column1[j];
    x.onclick := procedure(sender:TObject) begin showmessage((sender as TW3Panel).innerHTML); end;  
    var y : TW3Panel := TW3Panel.Create(CPanel2);        //column 2
    y.BorderRadius := 0;
    y.innerHTML := column2[j];
    y.onclick := procedure(sender:TObject) begin showmessage((sender as TW3Panel).innerHTML); end;

//fill viewport while scrolling
  var c1 := CPanel1.handle.children;
  var c2 := CPanel2.handle.children;

  W3Panel.onscroll := procedure(sender:tobject)
    var d : integer := trunc(W3Panel.handle.scrollTop/rowHeight);
    for var k := 0 to 8 do begin
      c1[k].innerHTML := column1[d+k];
      c2[k].innerHTML := column2[d+k];

    //fall back : ide chrome browser and iOS don't handle 'sticky' very well,
    //in that case set viewport position manually
    if Panel2.handle.style.position <> 'sticky' then
      if not w3_getIsSafari then
        Panel2.top := W3Panel.handle.scrollTop;



infinity scroll :

add to end of W3Panel.onscroll procedure

    //infinity scroll :
    if W3Panel.handle.scrollHeight - W3Panel.handle.scrollTop = W3Panel.handle.clientHeight then
      W3Panel.handle.scrollTop := 0;






Link to post
Share on other sites
  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...