Search code examples
htmlrshinyshinydashboardrhandsontable

Synchronize vertical scrolling of two handsontables in shiny


I'd like to synchronize the vertical scroll of two handsontables through the shiny app (using the rhandsontable library). I'm aware that there might be some HTML and CSS code involved, but I'm not experienced in those two coding languages.

Here is an example of the code:

library(shiny)
library(shinydashboard)
library(rhandsontable)

ui = dashboardPage(skin="red",
                   dashboardHeader(title="App"),
                   dashboardSidebar(),
                   dashboardBody(
                     div(style="white-space:nowrap;overflow-x:auto;overflow-y:hidden",
                         div(style="display:inline-block",
                             rHandsontableOutput("A")
                         ),
                         div(style="display:inline-block;margin-left:35px",
                             rHandsontableOutput("B")
                         )
                     )
                   )
)

server = shinyServer(function(input, output, session){
  
  A = as.data.frame(matrix(rnorm(1600),40,40))
  B = as.data.frame(matrix(rnorm(1600),40,40))
  
  output$A <- renderRHandsontable({
    rhandsontable(A,
                  height = 500,
                  width = 1000,)
  })
  output$B <- renderRHandsontable({
    rhandsontable(B,
                  height = 500,
                  width = 1000,)
  })
  
})


runApp(list(ui=ui, server=server))

There is supposed to be a solution here for the horizontal scroll sync, but when I run the example code it doesn't work. Also, here is a solution for the horizontal scroll but using DataTables and not for handsontables. (I tried changing the code to get vertical scroll sync but without success).


Solution

  • Here is a way but the problem is that there are multiple horizontal scrollbars.

    library(shiny)
    library(shinydashboard)
    library(rhandsontable)
    
    ui = dashboardPage(skin="red",
                       dashboardHeader(title="App"),
                       dashboardSidebar(),
                       dashboardBody(
                         tags$div(
                           style = "max-height:500px; overflow-y: scroll; overflow-x: scroll;",
                           splitLayout(
                             rHandsontableOutput("A"),
                             rHandsontableOutput("B")
                           )
                         )
                       )
    )
    
    server = shinyServer(function(input, output, session){
      
      A = as.data.frame(matrix(rnorm(1600),40,40))
      B = as.data.frame(matrix(rnorm(1600),40,40))
      
      output$A <- renderRHandsontable({
        rhandsontable(A,
                      height = 500,
                      width = 1000)
      })
      output$B <- renderRHandsontable({
        rhandsontable(B,
                      height = 500,
                      width = 1000)
      })
      
    })
    
    
    runApp(list(ui=ui, server=server))
    

    Edit: with jQueryScroll

    library(shiny)
    library(shinydashboard)
    library(rhandsontable)
    
    js <- "
    var myInterval = setInterval(function() {
      var containers = $('#A .ht_master .wtHolder, #B .ht_master .wtHolder');
      if (containers.length === 2) {
        clearInterval(myInterval);
        containers.scrollsync();
      }
    }, 200);
    "
    
    css <- "
    .ht_master .wtHolder {overflow: scroll !important}
    "
    
    ui = dashboardPage(skin="red",
                       dashboardHeader(title="App"),
                       dashboardSidebar(),
                       dashboardBody(
                         tags$head(
                           tags$script(src = "https://cdn.jsdelivr.net/gh/zjffun/jquery-ScrollSync/dist/jquery.scrollsync.js"),
                           tags$script(HTML(js)),
                           tags$style(HTML(css))
                         ),
                         div(#style="white-space:nowrap;overflow-x:auto;overflow-y:hidden",
                             div(style="display:inline-block", class = "ysync",
                                 rHandsontableOutput("A")
                             ),
                             div(style="display:inline-block;margin-left:35px", class = "ysync",
                                 rHandsontableOutput("B")
                             )
                         )
                       )
    )
    
    server = shinyServer(function(input, output, session){
      
      A = as.data.frame(matrix(rnorm(1600),40,40))
      B = as.data.frame(matrix(rnorm(1600),40,40))
      
      output$A <- renderRHandsontable({
        rhandsontable(A,
                      height = 500,
                      width = 1000,)
      })
      output$B <- renderRHandsontable({
        rhandsontable(B,
                      height = 500,
                      width = 1000,)
      })
      
    })
    
    runApp(list(ui=ui, server=server))