This is a migrated thread and some comments may be shown as answers.

Grid Scroll programatically not working

9 Answers 1310 Views
Grid
This is a migrated thread and some comments may be shown as answers.
ANDRE
Top achievements
Rank 1
ANDRE asked on 13 Aug 2018, 03:50 PM

Hi,

I spent days about that without finding any solution. 

The case is simple :

I have a Grid using virtual scrolling and a "nav toolbar" with buttons : "First, Previous, Next, Last".

The nav toolbar purpose is to navigate through the records contained in the grid.  

Note that the grid itself is in a kendo TabStrip but at this point, I use the nav toolbar only when the grid is visible. Later, I'll need to resync the grid if the user use the toolbar when the tab ontaining the grid is hidden, but it's another story.

 

I tried several approaches, here is the last.

Working code for "First" button : In any case, I get the first record selected at the top of the grid 

first : function () {
            if(oTab.oGrid.dataSource.page() != 1){
                gotoFirstRow=true;  // => the select will occur in dataBound event       
                oTab.oGrid.dataSource.page(1);
            }
            else {
                oTab.oGrid.select("tr:eq('0')");
            }
        }

 

In databound this code works, simple :

...
     if (gotoFirstRow){
gotoFirstRow=false;                
oTab.oGrid.select("tr:eq('0')");
 }

...

 

The same kind of code works for the "Last" button.

 

For the "Next" (and "Prev") button, it works untill the grid needs to load data :

next : function () {

                // The uidNext is set in change event : When a record is selected, I populate

                // uidCurrent/idproCurrent for the current record

                // uidNext/idproNext for the next record if any (or null if no more record)

                // uidPrev/idproPrev for the previous record if any (or null if no more record)
                // idpro is a unique field idnetifier all records have
            
if (typeof uidNext == "undefined"){ // For example when the current selected record is the last one
                return;
            }
            idproToSelect=idproNext; // => So the select will occur in dataBound event.    
             
            oTab.oGrid.select("tr[data-uid='"+uidNext+"']");           
             

                        // Now trying to put the selected record at the top of the grid. The goal is that if user use the nav bar, the grid always presents the

                       // record at the top

            var scrollContentOffset = oTab.oGrid.element.find("tbody").offset().top;
            var selectContentOffset = oTab.oGrid.select().offset().top;
            var distance = selectContentOffset - scrollContentOffset;
            oTab.oGrid.wrapper.find(".k-scrollbar-vertical").scrollTop(distance);
}

 

When I use scrollTop, if Kendo needs more records, it loads them. So I continue the process in dataBound event :

 

A this point, nothing works at all. This this the code in databound event:

if (idproToSelect != null){
                     
             // After new records are loaded, I search a record matching the unique field idproToSelect assigned previously
oTab.oGrid.element.find("td[data-field='idpro']").each(function(i, item) {
       if($(item).html() == idproToSelect) {

           // When I found it, I get the uid in order to select the row

          uidToSelect=$(item).parent().attr('data-uid');
       }
});

      // The row is selected

oTab.oGrid.select("tr[data-uid='"+uidToSelect+"']");
// RAZ
uidToSelect=null;
 idproToSelect=null;

 

// Now trying to put selected row at the top of the grid : not working

var scrollContentOffset = oTab.oGrid.element.find("tbody").offset().top;
       var selectContentOffset = oTab.oGrid.select().offset().top;
       var distance = selectContentOffset - scrollContentOffset;
       oTab.oGrid.wrapper.find(".k-scrollbar-vertical").scrollTop(distance);

 }

The problem i that grid nehavior becomes erratic, the scroll position is not right, so most of the time, I think the right record is selected, but it isn't showed. I suspect a problem with scrollTop when used form databound event. 

 

9 Answers, 1 is accepted

Sort by
0
Preslav
Telerik team
answered on 15 Aug 2018, 11:05 AM
Hello Andre,

With virtual scrolling, the height and the offset of the content are dynamic. Thus, calculating the distance is not an easy task, I even assume that it is not doable for every corner case.

Having said that, if the desired functionality is a must, I strongly recommend switching to Endless Scrolling:
For example:
I hope this information is useful.


Regards,
Preslav
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
ANDRE
Top achievements
Rank 1
answered on 17 Aug 2018, 09:12 AM

Hi Preslav,

Thanks for your answer.

I think I can deal with virtual mode as long as the grid (which is in a tabstrip) is visible. I'll post my code later : Each time I select the first/last visible record in the grid, I scroll up/down using the current row height, so the grid fetches the prev/next page if needed and it seems to work even I have to make more tests.

Of course, I understand that I can't use same method when the tab containing the grid is hidden. So when this is the case (the visible tab is the one containing the form associated with the grid), I don't try to sync scroll position, I just select tr elements to fectch next/prev record, then when there is no more record, I explicitly fetch records by calling page() method.

But doing this with virtual scrolling does not work. If I call page() method, even when the grid is visible, I can't scroll anymore. I just get the page but the position of scrollbar is ever at top, and no way to scroll back. Is it by design or is it a bug ?

Please find attached png. What I did to get this : Starting from first page, when the grid is visible, I call myGrid.dataSource.page(myGrid.dataSource.totalPages());

After that, you can see that I get indeed the last record, but the scrollbar position is wrong. I think it's easy to reproduce.

Please note I did not try endless mode yet. But this behavior with virtual mode seems weird.

Regards,

André.

0
ANDRE
Top achievements
Rank 1
answered on 17 Aug 2018, 09:16 AM
I post new png so you can see the total record number.
0
ANDRE
Top achievements
Rank 1
answered on 17 Aug 2018, 09:17 AM
I post another PNG, so you can see the total number of records.
0
ANDRE
Top achievements
Rank 1
answered on 17 Aug 2018, 10:17 AM

Dear Preslav,

Here the code used to manage First/Prev/Next/Last toolbar using Virtual scrolling.

I manage two cases : When grid is hidden or displayed.

 

 

The function called to sync 

scrollSync : function(){
                  
                oTab.oGrid.resize(true);
                var rowTop          = oTab.oGrid.select().offset().top;
                var rowHeight       = oTab.oGrid.select().height();
                var rowHeightNext   = rowHeight
                var rowHeightPrev   = rowHeight
                var rowBottom       = rowTop + rowHeight;
                var gridTop         = oTab.oGrid.wrapper.find('.k-grid-content').offset().top;
                var gridBottom      = gridTop + oTab.oGrid.wrapper.find('.k-grid-content').height();
                 
                if (typeof oTab.oVars.trNext != "undefined"){
                    if (oTab.oVars.trNext != null){
                        rowHeightNext = oTab.oVars.trNext.height();
                    }
                }
                if (rowHeightNext==null){
                    rowHeightNext   = rowHeight;
                }
                if (typeof oTab.oVars.trPrev != "undefined"){
                    if (oTab.oVars.trPrev != null){
                        rowHeightPrev = oTab.oVars.trPrev.height();
                    }
                }
                if (rowHeightPrev==null){
                    rowHeightPrev   = rowHeight;
                }
                 
                if (rowBottom >= gridBottom){
                    var vs = oTab.oGrid.wrapper.find('.k-grid-content').data('kendoVirtualScrollable');                
                    var scrollTop = vs.verticalScrollbar.scrollTop();
                    var scrollTopNew = (rowBottom - gridBottom) + scrollTop + rowHeightNext;
                    vs.verticalScrollbar.animate({ scrollTop: scrollTopNew}, 250);
                }
                else if (rowTop <= gridTop){
                    var vs = oTab.oGrid.wrapper.find('.k-grid-content').data('kendoVirtualScrollable');                
                    var scrollTop = vs.verticalScrollbar.scrollTop();
                    var scrollTopNew = scrollTop - rowHeightPrev;
                    if (scrollTopNew >= 0){
                        vs.verticalScrollbar.animate({ scrollTop: scrollTopNew}, 250);
                    }
                }
            }

 

Here the code for buttons:

first : function () {
            oTab.oVars.lScroll=false;
            if(oTab.oGrid.dataSource.page() != 1){
                oTab.oVars.idproToSelect=null;
                oTab.oVars.uidToSelect=null;
                oTab.oVars.gotoFirstRow=true;
                oTab.oGrid.dataSource.page(1);
            }
            else {
                oTab.oGrid.select("tr:eq('0')");
            }
        },
        prev : function () {
          if (oTab.oVars.uidPrev == null){ // No previous record ?
                // Grid hidden ? => Load records using datasource.page()
                if (oTab.oTabs.select().index()>0){
                    if (oTab.oGrid.dataSource.page() > 1){
                        oTab.oVars.uidToSelect = "Prev"; // => Will be processed by databound
                        oTab.oVars.idproToSelect = "Prev"; // => Will be processed by databound
                        oTab.oGrid.dataSource.page(oTab.oGrid.dataSource.page() - 1);
                    }
                }
                return;
            }
            oTab.oVars.uidToSelect=oTab.oVars.uidPrev; // => Will be processed by databound
            oTab.oVars.idproToSelect=oTab.oVars.idproPrev; // => Will be processed by databound
            oTab.oGrid.select("tr[data-uid='"+oTab.oVars.uidPrev+"']");  
            oTab.oVars.lButton=false;
        },
        next : function () {
            if (oTab.oVars.uidNext == null){
                // Grid hidden ? => Load via datasource.page()
                if (oTab.oTabs.select().index()>0){
                    if (oTab.oGrid.dataSource.page() < oTab.oGrid.dataSource.totalPages()){
                        oTab.oVars.uidToSelect = "Next"; // => Will be processed by databound
                        oTab.oVars.idproToSelect = "Next"; // => Will be processed by databound
                        oTab.oGrid.dataSource.page(oTab.oGrid.dataSource.page() + 1);
                    }
                return;            
            }
            oTab.oVars.uidToSelect=oTab.oVars.uidNext; // => Will be processed by databound
            oTab.oVars.idproToSelect=oTab.oVars.idproNext; // => Will be processed by databound
            oTab.oGrid.select("tr[data-uid='"+oTab.oVars.uidNext+"']");        
        },
        last : function () {
            if(oTab.oGrid.dataSource.page() < oTab.oGrid.dataSource.totalPages()){
                oTab.oVars.idproToSelect=null;
                oTab.oVars.uidToSelect=null;
                oTab.oVars.lScroll=false;
                // Grid hidden ?
                 if (oTab.oTabs.select().index()>0){
                    oTab.oVars.gotoPrevLastPage=true;
                    oTab.oVars.uidToSelect = "Last"; // => Will be processed by databound
                    oTab.oVars.idproToSelect = "Last"; // => Will be processed by databound
                    oTab.oGrid.dataSource.page(oTab.oGrid.dataSource.totalPages());
                }
                else{
                    oTab.oVars.gotoLastRow=true;
                    var s=oTab.oGrid.wrapper.find(".k-scrollbar-vertical");
                    var h=$(s).prop('scrollHeight');
                    $(s).scrollTop(h);
                }
            }
            else {
                // Get lastrow
                var i=oTab.oGrid.dataSource.view().length - 1;                 
                if (i<0){
                    i=0;
                }
                // Select lastrow
                oTab.oGrid.select("tr:eq('"+i+"')");
            }
        }

 

The change event for the grid :

change: function (e) {
             
            var td;
            /*
             * Save current, prev and next record informations
             */
            oTab.oVars.recordCurrent = this.dataItem(this.select());
            oTab.oVars.recordId=oTab.oVars.recordCurrent.id;
             
              
            oTab.oVars.idproCurrent = oTab.oVars.recordCurrent.idpro;
            oTab.oVars.uidCurrent = oTab.oVars.recordCurrent.uid;
            oTab.oVars.trCurrent=oTab.oGrid.element.find("tr[data-uid='" + oTab.oVars.uidCurrent +                    "']");
            oTab.oVars.trNext = oTab.oVars.trCurrent.next('tr');
            oTab.oVars.uidNext = oTab.oVars.trNext.attr("data-uid");
            td = oTab.oVars.trNext.find("td[data-field='idpro']");
            oTab.oVars.idproNext=td.html();
             
            oTab.oVars.trPrev = oTab.oVars.trCurrent.prev('tr');
            oTab.oVars.uidPrev = oTab.oVars.trPrev.attr("data-uid");
            td = oTab.oVars.trPrev.find("td[data-field='idpro']");
            oTab.oVars.idproPrev=td.html();
 
            // Sync scrollbar
            if (oTab.oVars.lScroll){               
                oTab.oFuns.scrollSync();
            }
            else {
                oTab.oVars.lScroll=true;               
            }
         },

 

This is what happens in databound, that is when new records need to be fetched  :

dataBound: function(e) {     

                 // Last row required

            if (oTab.oVars.gotoLastRow){
              oTab.oVars.gotoLastRow=false;
              // Refresh scrollbar
              oTab.oGrid.resize(true);
              // get last row
              var i=oTab.oGrid.dataSource.view().length - 1;  
              // Select last row
              oTab.oGrid.select("tr:eq('"+i+"')");
        }  

        // First row required

         else if (oTab.oVars.gotoFirstRow){
           oTab.oVars.gotoFirstRow=false;                 
           // Refresh scrollbar
           oTab.oGrid.resize(true);
          // Scroll to top
         oTab.oGrid.wrapper.find(".k-scrollbar-vertical").scrollTop(0);                    
         //  row
         oTab.oGrid.select("tr:eq('0')");
     }

     // Other row required (Next ou Prev)

     else if (oTab.oVars.idproToSelect != null){
 
                    var td;
                     // Nextwhen grid is hidden
                    if (oTab.oVars.idproToSelect == "Next"){
                         
                        // Finding first td whith field "idpro" (Progress rowid of the record)
                        td = oTab.oGrid.element.find("td[data-field='idpro']");
                         
                        // Finding the row = tr parent of td
                        oTab.oVars.trNext = td.parent();
                        oTab.oVars.idproNext=td.html();
                         
                        // Row uid
                        oTab.oVars.uidNext = oTab.oVars.trNext.attr("data-uid");
                         
                        // Grid hidden in thus case => no scroll
                        oTab.oVars.lScroll=false;
                        oTab.oGrid.select("tr[data-uid='"+oTab.oVars.uidNext+"']");
                        // re enable scroll for further oparations
                        oTab.oVars.lScroll=true;
                    }
                    // Prev when grid is hidden
                    else if (oTab.oVars.idproToSelect == "Prev"){
                         
                        // td for field idpro
                        td = oTab.oGrid.element.find("td:last[data-field='idpro']");              
                        // row = tr parent of td
                        oTab.oVars.trPrev = td.parent();
                         
                        // Row uid
                        oTab.oVars.idproPrev=td.html();
                        oTab.oVars.uidPrev = oTab.oVars.trPrev.attr("data-uid");
                         
                        // Grid hidden in this case => no scroll
                        oTab.oVars.lScroll=false;
                        oTab.oGrid.select("tr[data-uid='"+oTab.oVars.uidPrev+"']");
                        // re enable scroll
                        oTab.oVars.lScroll=true;
                    }
                    // Next or Prev when grid is displayed (scroll already occured)
                    else {
                        // uid to select using idproToSelect
                        oTab.oGrid.element.find("td[data-field='idpro']").each(function(i, item) {
                            if($(item).html() == oTab.oVars.idproToSelect) {
                                oTab.oVars.uidToSelect=$(item).parent().attr('data-uid');
                            }
                        });
                        // Scrollalready occured so disabling it
                        oTab.oVars.lScroll=false;
                        oTab.oGrid.select("tr[data-uid='"+oTab.oVars.uidToSelect+"']");
                        oTab.oVars.lScroll=true;
                    }
                    // RAZ
                    oTab.oVars.uidToSelect=null;
                    oTab.oVars.idproToSelect=null;
                }
                     // Row selected using mouse
                else {
                    // Finding uid to select using idproCurrent
                    oTab.oGrid.element.find("td[data-field='idpro']").each(function(i, item) {
                        if($(item).html() == oTab.oVars.idproCurrent) {
                            oTab.oVars.uidCurrent=$(item).parent().attr('data-uid');
                        }
                    });
                    oTab.oVars.lScroll=false;
                    oTab.oGrid.select("tr[data-uid='"+oTab.oVars.uidCurrent+"']");
                    oTab.oVars.lScroll=true;
                }
            }
         },

 

To manage the case when grid is hidden, I try to sync grid whe it is displayed using tab event "activate"

var onActivate = function(e) {
    // Get main object to get grid
    var oTab = window[JSTabsCurrent]["getTab"]();
    // Onglet 0 ?
    if(oTab.oTabs.select().index()==0){    
           // Just resize then call scrollSunc
        oTab.oGrid.resize(true);
        oTab.oFuns.scrollSync();   
         
    }
}

 

I hope it makes it clearer.

I quicly tried the endless mode however, I can figure out how to programmatically scroll as object "kendoVirtualScrollable" does not exist in this mode. 

 

Regards,

André.

0
Konstantin Dikov
Telerik team
answered on 21 Aug 2018, 08:33 AM
Hello Andre,

Thank you for sharing your implementation with the community.

Regarding the endless scrolling, it uses different mechanism and it will not be possible to achieve the desired behavior with it.


Best Regards,
Konstantin Dikov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
ANDRE
Top achievements
Rank 1
answered on 21 Aug 2018, 09:32 AM

Hi,

At this time, I update my code to make it work perfectly using paged mode. I juste replaced 

var vs = oTab.oGrid.wrapper.find('.k-grid-content').data('kendoVirtualScrollable');

 

by 

var vs = oTab.oGrid.wrapper.find('.k-grid-content');

 

It works because in paged mode, you can use page() method. 

Endless mode is not usable because it seems to load and keep all loaded records locally. Right ? So not possible to use that with tables containing millions records.  

However, virtual mode stays the best one for me. It's almost working. The only problem is when I use page() method to go to the last record, it loads only the last page instead of loading the last page PLUS some records from previous page as it does when you use the scrollbar. Thus, it prevents user to scrollback. That's a pity because this is the only case causing problem.

 

In conclusion, you simply can't use page() method in virtual mode. Do you have any way to fix this or any workaround ? It would be great.   

Regards,

André.

0
Konstantin Dikov
Telerik team
answered on 23 Aug 2018, 07:36 AM
Hi Andre,

Your statement about the endless scrolling is correct and the new pages are appended to the previous pages and that functionality is not suitable for huge amount of data.

As for the Virtual Scrolling, changing the position of the scrollbar should be enough for going to a particular page, without calling the page method of the DataSource. I have created an  example that will go to a particular page entered in the input above the Grid:
It should be easy to replace the input element with the buttons that you have.


Regards,
Konstantin Dikov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
ANDRE
Top achievements
Rank 1
answered on 23 Aug 2018, 09:18 AM

Hello Konstantin,

Yes, I already did something like this to go to last page when the grid is visible.

The method you provide is working for any page.

I have now to try it when the grid is not visible, that is only when the tab containing the grid is activated again because when the grid is hidden, I have no other choice than page() method.

When the grid becomes visible again, I plan to first come back to first page, then resize the grid, then go to current page from within dataBound event.

The only problem I can see at this time is when row height is not the same for all rows.  So "rowHeight = scrollbarHeight/oTab.oGrid.dataSource.total()" is not always accurate.

I'll let you know if it suits to my case.

Regards,

André.

Tags
Grid
Asked by
ANDRE
Top achievements
Rank 1
Answers by
Preslav
Telerik team
ANDRE
Top achievements
Rank 1
Konstantin Dikov
Telerik team
Share this question
or