Accessing scroll events
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.
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
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.
Also I just discovered this project exists https://github.com/statistiekcbs/scrollytell
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 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.
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... 😅).