shiny icon indicating copy to clipboard operation
shiny copied to clipboard

Accessing scroll events

Open rodrigoheck opened this issue 6 years ago • 6 comments

Is there a way to keep track of changes in the scrolling of a shiny app webpage? I would like to use that information as an input to a reactive item.

For example, I want to turn some code actionable when the user is scrolling after a certain mark.

rodrigoheck avatar May 25 '19 04:05 rodrigoheck

shiny doesn't have first-class support for accessing scroll events, but you could leverage a JS library like those described here

https://pudding.cool/process/how-to-implement-scrollytelling/ https://pudding.cool/process/introducing-scrollama/

This would be a good question to ask on https://community.rstudio.com/ as other users may have some examples to share

cpsievert avatar May 28 '19 14:05 cpsievert

I could use Waypoint.js together with Shiny.onInputChange

Here is example code:

JS:

var toggle = false;
var waypoint = new Waypoint({
  element: $('#marker'),
  handler: function(direction) {
    Shiny.onInputChange('scrollInput', toggle);
    toggle = !toggle;
  }
});

R:

observeEvent(input$scrollInput, {
    print("srolled into element")
})

You should be easy write function that is attached to item with certain class and use id of the element to trigger waypoint and send data to R:

$('.waypoint').each(function() {
   var toggle = false;
   var id = this.id;
   var waypoint = new Waypoint({
     element: this,
     handler: function(direction) {
       Shiny.onInputChange(id, toggle);
       toggle = !toggle;
    }
  });
});

and you can generate html:

<div id="foo" class="waypoint">
</div>
<div id="bar" class="waypoint">
</div>

and you will have two reactiveInput that respond to scroll.

jcubic avatar Jun 11 '19 12:06 jcubic

Also I just discovered this project exists https://github.com/statistiekcbs/scrollytell

cpsievert avatar Jun 11 '19 14:06 cpsievert

Sorry for resurrecting this thread, but I believe the question was not answered. Is it possible to get the scrolling position as a reactive item, as in having an input$scroll_position input that returns the position of the scroll bars in pixels (i.e. 0 at the top)?

I need this because I want to make some plots at the bottom of a shiny App to load only when the user has scrolled the website, otherwise it's wasteful to load elements that are still not in view.

bastianolea avatar Feb 29 '24 13:02 bastianolea

@bastianolea I don't use shiny anymore, but this should work:

$(document).on('scroll', () => {
  Shiny.onInputChange('scroll_position', document.documentElement.scrollTop);
});

But note that some browsers may use body tag for scrolling.

If you have some performance issues you may want to throttle the event.

But it's probably just better to use waypoints and point into your plot. You can also use IntersectionObserver to trigger when the container that should have the plot is in view.

jcubic avatar Feb 29 '24 14:02 jcubic

Thanks @jcubic, I eventually was able to get scroll position as a Shiny input using the following code:

# get vertical scroll position using javascript
  tags$head(
    tags$script(
      "$(document).ready(function() {
          $(document).on('scroll', function() {
      Shiny.onInputChange('y_offset', $('body').position());
          });
        });")
  ) 

Then I can access vertical position of the user in the Shiny app like this:

 # get valid scrolling value
  vertical <- reactive({
    if (length(input$y_offset) > 0) {
      return(input$y_offset[1])
    } else {
      return(0)
    }
  })

# debounce so that the value only updates after the user stops scrolling
  vertical_position <- vertical |> debounce(200)
  
# print message in the console about user scroll position
  observe({
    message("vertical scroll position debounced: ", vertical_position())
  })

Then the app decides to load certain reactives and outputs only if if vertical_position() > 1500; i.e., if the user has scrolled down enough to put the content in view (assuming the user is not using a vertical ultra wide monitor... 😅).

bastianolea avatar Jul 29 '24 12:07 bastianolea