Search code examples
javascriptshiny

Is there a way to plot using only Javascript commands in shiny


I am trying use only only javascript to plot a graph in shiny. Can anyone help me in achieving this below

library(shiny)

ui <- fluidPage(
#htmlOutput("d"),
tags$p(id = 'g', class =  'newClass',  'This is a plot'),tags$p(id = "demo"),
tags$script(HTML("var tit = document.getElementById('g');tit.onclick = function (){document.getElementById('demo').innerHTML  = Date()}"))
)

server <- function(input, output, session) {
}

shinyApp(ui, server)

This is an application when the user clicks on the content, the date displays below

Now, can we change this, so that when the user click on the content, a plot plot_ly(iris, x = ~Species, y = ~Petal.Length) should appear. I know we can achieve somehow using shiny functionality (using button). But Wanted to know if we can do this using only JS.


Solution

  • The approach seems flawed to me. The whole idea of shiny is to encapsulate the JavaScript functionality and to allow for passing R objects conveniently to the underlying JavaScript layer. You are trying to re-implement the wheel.

    Having said that, it is, however, of course possible to run arbitrary JS code from within shiny as well. plotly is yet another JavaScript based plotting library, so you could (I am not saying you should) call it via JS.

    You will run into some troubles of how to pass R data (iris) to JS, but again it is possible. In the end you will end up with some custom code which tries to mimic what shiny does for you already out of the box and more reliably.

    But as you asked for it, here's a MWE:

    library(shiny)
    library(jsonlite)
    library(dplyr)
    library(plotly)
    
    make_plotly <- HTML(
      "
      $(function() {
        $('#plotly_byhand').click('on', function() {
          const data = JSON.parse($('#iris').text());
          Plotly.newPlot(this, [{...data, type: 'scatter', mode: 'markers'}]);
        })
      })
      ")
    
    ui <- fluidPage(
      tags$script(src = "https://cdnjs.cloudflare.com/ajax/libs/plotly.js/2.35.0/plotly.min.js"),
      tags$script(
        id = "iris",
        type = "application/json",
        toJSON(iris %>% select(x = Species, y = Petal.Length), dataframe = "columns")    
      ),
      tags$script(
        make_plotly
      ),
      div(
        id = "plotly_byhand",
        style = "width: 100%; height: 500px;"
      )
    )
    
    server <- function(input, output, session) {
    }
    
    shinyApp(ui, server)
    

    Idea is that you first store the data as JSON object in your file, which you then parse back in your Plotly code, which does the plotting. Another approach would be to use Shiny.addCustomMessageHandler to add a new JS handler, which you could then call from R with the data as an argument, but again, you are really re-inventing the wheel here.

    Update

    • Added on click event listener to the div to make the plot appear on click. This requires, however, to give the div a dedicated height to be able to click on it.