Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R Shiny - Scrolling to a given row of datatable with javascript callback

I am running into an issue with datatables and shiny, specifically within a flexdashboard but I think that is irrelevant.

I want to scroll to a given row in the datatable when I click on the corresponding point in a plot. But, the minimal problem I have is to 'simply' scroll to any row. I can select a row using JavaScript with the option initComplete but scrollTo() will not do anything for me.

Looking at a previous question, Scroll to specific row in Datatable API, I got to this example, https://codepen.io/anon/pen/KWmpjj. It showcases the javascript function you could use with initComplete , but this was not made with R/Shiny. Specifically you'll find the following option for a small datatable:

initComplete: function () {
        this.api().row(14).scrollTo();
      $(this.api().row(14).node()).addClass('selected');
    }

Since my goal is to use this in a flexdashboard I have a minimal example in R markdown format. A pretty standard call to DT::renderDataTable with random data. I don't understand why this.api().table().row(15).scrollTo(); will not do anything. I added an alert to confirm that the JavaScript of initComplete actually ran.

---
title: "Scroll to row in datatable"
date: "20 december 2017"
output: html_document
runtime: shiny
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## Datatable automatically scroll to given row
The goal is to have a datatable rendered in a flexdashboard. Upon selecting a point in a scatter plot, the corresponding row in the table gets selected and needs to be scrolled into view. Selecting a row by clicking a point in a plot (with ggplot) works, but scrolling will not.

Preferably without using shinyApp(), since scrolling is a JavaScript functionality rather than a shiny one (?).

```{r}
library(dplyr)
library(DT)

# Generate random data
df <- data.frame(matrix(runif(1000), ncol = 5))

# Render datatable with shiny
DT::renderDataTable({
  DT::datatable(df,
  extensions = 'Scroller',
  # selection = 'single',                 # Eventually only allow single selection
  escape = FALSE,
  # callback = JS('this.api().row(15).scrollTo();'),       # Attempt to use callback instead
  options = list(scrollX = TRUE,
                 scrollY = 200,
                 paging = FALSE,
                 initComplete  = JS('function() {
                                   $(this.api().table().row(15).node()).addClass("selected");
                                   this.api().table().row(15).scrollTo();
                                  alert("scrolled");}')))},
  server = TRUE) # Setting server = TRUE results in the selection with initComplete breaking

```

What I have noticed is that if you scroll the table in the previously linked example the text at the bottom will actually update and say "Showing 1 to 6 of 20 entries" or "Showing 6 to 11 of 20 entries", etc. This does not happen in my example datatable, that always says Showing 1 to 200 of 200 entries. That leads me to think that it does not scroll to the specified row because everything is already 'in view', even though it is not really.

like image 355
Lodewic Van Twillert Avatar asked Dec 20 '17 17:12

Lodewic Van Twillert


3 Answers

You need to set scroller = TRUE and paging = TRUE in the datatable() options argument. This is working for me:

---
title: "Scroll to row in datatable"
date: "20 december 2017"
output: html_document
runtime: shiny
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## Datatable automatically scroll to given row
The goal is to have a datatable rendered in a flexdashboard. Upon selecting a point in a scatter plot, the corresponding row in the table gets selected and needs to be scrolled into view. Selecting a row by clicking a point in a plot (with ggplot) works, but scrolling will not.

Preferably without using shinyApp(), since scrolling is a JavaScript functionality rather than a shiny one (?).

```{r}
library(dplyr)
library(DT)

# Generate random data
df <- data.frame(matrix(runif(1000), ncol = 5))

# Render datatable with shiny
DT::renderDataTable({
  DT::datatable(df,
  extensions = 'Scroller',
  # selection = 'single',                 # Eventually only allow single selection
  escape = FALSE,
  # callback = JS('this.api().row(15).scrollTo();'),       # Attempt to use callback instead
  options = list(scrollX = TRUE,
                 scrollY = 200,
                 paging = TRUE,
                 scroller = TRUE,
                 initComplete  = JS('function() {
                                   $(this.api().table().row(15).node()).addClass("selected");
                                   this.api().table().row(15).scrollTo();
                                  alert("scrolled");}')))},
  server = TRUE) # Setting server = TRUE results in the selection with initComplete breaking
like image 197
Andy Merlino Avatar answered Nov 17 '22 17:11

Andy Merlino


I don't know why DataTables's .scrollTo() method doesn't work, but I just tested the native .scrollIntoView() method on HTML nodes, and it worked well for me. I changed your

this.api().table().row(15).scrollTo();

to

this.api().table().row(15).node().scrollIntoView();

Full example:

---
title: "Scroll to row in datatable"
date: "20 december 2017"
output: html_document
runtime: shiny
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## Datatable automatically scroll to given row
The goal is to have a datatable rendered in a flexdashboard. Upon selecting a point in a scatter plot, the corresponding row in the table gets selected and needs to be scrolled into view. Selecting a row by clicking a point in a plot (with ggplot) works, but scrolling will not.

Preferably without using shinyApp(), since scrolling is a JavaScript functionality rather than a shiny one (?).

```{r}
library(dplyr)
library(DT)

# Generate random data
df <- data.frame(matrix(runif(1000), ncol = 5))

# Render datatable with shiny
DT::renderDataTable({
  DT::datatable(df,
  extensions = 'Scroller',
  # selection = 'single',                 # Eventually only allow single selection
  escape = FALSE,
  # callback = JS('this.api().row(15).scrollTo();'),       # Attempt to use callback instead
  options = list(scrollX = TRUE,
                 scrollY = 200,
                 paging = FALSE,
                 initComplete  = JS('function() {
                                   $(this.api().table().row(15).node()).addClass("selected");
                                   this.api().table().row(15).node().scrollIntoView();
                                  }')))},
  server = TRUE) # Setting server = TRUE results in the selection with initComplete breaking

```
like image 42
Yihui Xie Avatar answered Nov 17 '22 17:11

Yihui Xie


You can also use this method

this.api().table().scroller.toPosition(15);
like image 1
David Rubinger Avatar answered Nov 17 '22 17:11

David Rubinger