Search code examples
javascriptrshinyhtmlwidgets

Run javascript code when output is rendered for default render methods (like onRender for widgets)


In shiny apps we can add javascript code to run when a widget has rendered using onRender from library(htmlwidgets). The same does not seem to work for the default render* functions from shiny. Is there a way to achieve something similar for these render functions?

The app below shows alerts whenever the plotly and leaflet widgets are rendered, but not for renderUI and renderPlot.

library(shiny)
library(plotly)
library(leaflet)
library(htmlwidgets)

ui <- fluidPage(
  actionButton('a1','a1'),
  fluidRow(
    column(
      width=6,
      plotlyOutput('plt'),
      plotOutput('p2')
    ),
    column(
      width=6,
      uiOutput('foo'),
      leafletOutput('map')
    )
  )
)

server <- function(input, output, session) {
  
  output$foo <- renderUI({
    input$a1
    tagList(runif(1)) %>% 
      onRender('function() {alert("renderUI works");}')
  })
  output$plt <- renderPlotly({
    input$a1
    plot_ly(type='scatter', mode='markers', x=runif(10)) %>% 
      onRender('function() {alert("plotly works");}')
  })
  output$p2 <- renderPlot({
    input$a1
    hist(runif(50)) %>% 
      onRender('function() {alert("renderPlot works");}')
  })
  output$map <- renderLeaflet({
    input$a1
    leaflet() %>% 
      addTiles() %>% 
      onRender('function() {alert("leaflet works");}')
  })
}

shinyApp(ui, server)

Solution

  • onRender refers to rendering a htmlwidget not to shiny's render* functions.

    You can use it without shiny.

    onRender expects a HTML widget object and accordingly doesn't work with e.g. tags - see ?onRender:

    Use this function to supplement the widget's built-in JavaScript rendering logic with additional custom JavaScript code, just for this specific widget object.

    You can use library(shinyjs) instead to realize a similar behaviour:

    library(shiny)
    library(plotly)
    library(leaflet)
    library(htmlwidgets)
    library(shinyjs)
    
    ui <- fluidPage(
      useShinyjs(),
      actionButton('a1','a1'),
      fluidRow(
        column(
          width=6,
          plotlyOutput('plt'),
          plotOutput('p2')
        ),
        column(
          width=6,
          uiOutput('foo'),
          leafletOutput('map')
        )
      )
    )
    
    server <- function(input, output, session) {
      output$foo <- renderUI({
        input$a1
        runjs('alert("renderUI works");')
        tagList(p("p-tag"))
      })
      output$plt <- renderPlotly({
        input$a1
        plot_ly(type='scatter', mode='markers', x=runif(10)) %>% 
          onRender('function() {alert("plotly works");}')
      })
      output$p2 <- renderPlot({
        input$a1
        runjs('alert("renderPlot works");')
        hist(runif(50))
      })
      output$map <- renderLeaflet({
        input$a1
        leaflet() %>% 
          addTiles() %>% 
          onRender('function() {alert("leaflet works");}')
      })
    }
    
    shinyApp(ui, server)