4

please I have a R shiny application and one of the pages have a really big table. For this reason, I would need to have the horizontal scrollbar both at the top as well as the bottom of the table. Please, bear in mind I'm very little familiar with HTML, CSS and JS. Also, I already managed to move the horizontal scrollbar to the top of the table using solution: R DT Horizontal scroll bar at top of the table

I'm literally using the example explained there and it works perfectly. I would just need some help in adding the scrollbar at the bottom as well.

css <- HTML(
    "#flipped > .dataTables_wrapper.no-footer > .dataTables_scroll > .dataTables_scrollBody {
        transform:rotateX(180deg);
    }
    #flipped > .dataTables_wrapper.no-footer > .dataTables_scroll > .dataTables_scrollBody table{
        transform:rotateX(180deg);
    }"
)

ui <- fluidPage(
    tags$head(tags$style(css)),
    fluidRow(column(width = 6,
                    h4("Flipped Scrollbar"),
                    br(),
                    DT::dataTableOutput("flipped")
                    ),
             column(width = 6,
                    h4("Regular Scrollbar"),
                    br(),
                    DT::dataTableOutput("regular")
                    )
             )
)

server <- function(input, output, session) {
    output$flipped <- DT::renderDataTable({
        DT::datatable(mtcars, rownames = FALSE,
                      options = list(
                          scrollX = TRUE
                      )
        )
    })
    output$regular <- DT::renderDataTable({
        DT::datatable(mtcars, rownames = FALSE,
                      options = list(
                          scrollX = TRUE
                      )
        )
    })
}

shinyApp(ui, server)

I managed to find a similar question (horizontal scrollbar on top and bottom of table) however, I can't understand how to apply that css and JS code to a Shiny application. Many thanks

Update (that still doesn't work) as a follow-up to Stéphane Laurent suggested solution. My current code now is:

library(shiny)
library(DT)

wideTable <- as.data.frame(matrix(rnorm(1000), nrow = 10, ncol = 100))

js <- "
$(document).ready(function(){
  $('#dtable').on('shiny:value', function(e){
    setTimeout(function(){
      $('#dtable table').wrap('<div id=\"scrolldiv\"></div>');
      $('#scrolldiv').doubleScroll({
        contentElement: $('table'),
          scrollCss: {                
              'overflow-x': 'scroll',
              'overflow-y': 'hidden'
          },
          contentCss: {
              'overflow-x': 'scroll',
              'overflow-y': 'hidden'
          },
        resetOnWindowResize: true
      });
      setTimeout(function(){$(window).resize();}, 100);
    }, 0);
  });
});
"

CSS <- "
.doubleScroll-scroll-wrapper {
  clear: both;
}
"

ui <- fluidPage(
  tags$head(
    tags$script(src = "jquery.doubleScroll.js"),
    tags$script(HTML(js)),
    tags$style(HTML(CSS))
  ),
  br(),
  dataTableOutput("dtable")
)

server <- function(input, output, session){
  
  output$dtable <- DT::renderDataTable({
    datatable(wideTable, 
              rownames = T,
              filter = 'top',
              caption = paste0("All columns of CSV report")
)
      })
      
    }
    
    shinyApp(ui, server)
Angelo
  • 1,594
  • 5
  • 17
  • 50

1 Answers1

6

Here is a solution using the DoubleScroll JavaScript library.

Download the file jquery.doubleScroll.js from here. Put it in the www subfolder of your shiny app.

Then here is the app:

library(shiny)
library(DT)

wideTable <- as.data.frame(matrix(rnorm(1000), nrow = 10, ncol = 100))

js <- "
$(document).ready(function(){
  $('#dtable').on('shiny:value', function(e){
    setTimeout(function(){
      $('#dtable table').wrap('<div id=\"scrolldiv\"></div>');
      $('#scrolldiv').doubleScroll({
        contentElement: $('table'),
          scrollCss: {                
              'overflow-x': 'scroll',
              'overflow-y': 'hidden'
          },
          contentCss: {
              'overflow-x': 'scroll',
              'overflow-y': 'hidden'
          },
        resetOnWindowResize: true
      });
      setTimeout(function(){$(window).resize();}, 100);
    }, 0);
  });
});
"

CSS <- "
.doubleScroll-scroll-wrapper {
  clear: both;
}
"

ui <- fluidPage(
  tags$head(
    tags$script(src = "jquery.doubleScroll.js"),
    tags$script(HTML(js)),
    tags$style(HTML(CSS))
  ),
  br(),
  DTOutput("dtable")
)

server <- function(input, output, session){
  
  output[["dtable"]] <- renderDT({
    datatable(wideTable)
  })
  
}

shinyApp(ui, server)

If the output id of your datatable is not "dtable", then in the JS code (js) replace dtable (two occurences) with the output id of your datatable.

enter image description here

Stéphane Laurent
  • 75,186
  • 15
  • 119
  • 225
  • Hi Stephane, thank you very much for sharing all this information. I just tried, but I still see only one scroll bar at the bottom of the table. I noticed the only difference seems to be the fact my table is of type "dataTableOutput(outputId="dtable")". Could that explain the issue? Thanks – Angelo Jul 06 '20 at 12:25
  • @Angelo Sorry I don't see what you mean. Did you remove the `scrollX` option? Could you edit your post to show what you did? – Stéphane Laurent Jul 06 '20 at 12:34
  • Sure Stephane, I just added my code in the postt. I removed scrollX from option, so now the table doesn't have any horizontal scroll at all – Angelo Jul 06 '20 at 13:31
  • @Angelo Are you sure you have the file **jquery.doubleScroll.js** in the **www** subfolder? – Stéphane Laurent Jul 06 '20 at 13:41
  • Yes, I also tried to move it out from that folder and the page was coming up completely empty in such case. It means it does read the file. The concern//doubt I have is that perhaps the js code doesn't work / is not compatible with the renderDataTable. Could that be the case? – Angelo Jul 06 '20 at 13:54
  • @Angelo You should always use `renderDT` and `DTOuput` instead of `renderDataTable` and `dataTableOutput`. What is the browser you use? If you use the RStudio browser or Explorer/Edge then it's unlikely to work. Use Chrome. – Stéphane Laurent Jul 06 '20 at 14:03
  • oh really? I didn't know that. I just tried to switch to renderDT (I'm using Chrome) but that triggers an error message: 'renderDT' is not an exported object from 'namespace:DT' I tried to google it but can't see much. Would you have any clues on why I get this error? – Angelo Jul 06 '20 at 15:13
  • @Angelo What is the version of `DT` you are using? If it's an old one, upgrade. – Stéphane Laurent Jul 06 '20 at 15:23
  • thank you Stephane, what version would you recommend? I'm asking because this app is deployed on a linux server and I need to ask the admin permission to upgrade a library and usually they avoid to have the most recent versions to avoid potential bugs. Thank you for your help – Angelo Jul 06 '20 at 18:28
  • @ Stéphane Laurent sorry to bother again, I've been communicated that my company's server cannot update the DT library unfortunately, so I cannot use renderDT. Do you think there are other ways to achieve this goal? Thanks in advance, much appreciated – Angelo Jul 07 '20 at 20:14
  • I finally managed to have the new version of DT and it works like a charm. Thank you so much for this incredibly useful solution, I would have never been able to do it without you. Thank you so much – Angelo Jul 10 '20 at 16:46
  • @StéphaneLaurent Where is the **www** subfolder located? I am building a package of Shiny apps and can not see this folder? – lmsimp Oct 13 '20 at 10:10
  • 1
    @lmsimp You have to create it. In the folder containing your R code for your app. – Stéphane Laurent Oct 13 '20 at 14:01
  • Note if you are using UI tabs, you must call `setTimeout(function(){$(window).resize();}, 100);` on the `click` event of the UI tab: `$("#tbh-tab-id").click(function(){ setTimeout(function(){$(window).trigger('resize');}, 100); });` – rboeg Dec 30 '20 at 12:22
  • @StéphaneLaurent How can I implement this in a module? – Mohamad Sahil Sep 21 '22 at 15:15
  • @MohamadSahil In the JS code, you have to replace `#dtable` with `#NS-dtable`, where `NS` is the namespace you use. – Stéphane Laurent Sep 21 '22 at 15:40
  • @StéphaneLaurent Thanks. I guess in that case, I would have to convert js code to a function with the table id as an argument. And then inside the UI refer to it as ns('dtable'). How can I refer to the table id inside the function? `paste0("$(#'",dtid,"'")` – Mohamad Sahil Sep 21 '22 at 16:28