Search code examples
javascriptrplotlyr-plotly

Make div appear on click in R plotly


I am new to the world of combining Javascript with R and my Javascript knowledge is quite limited. I am trying to make a plotly plot of chess openings and when you click on the opening it shows a board with the initial moves of the opening.

library(plotly)
library(htmlwidgets)
library(data.table)
library(rchess)

data("chessopenings")
setDT(chessopenings)
mychess = chessopenings[1:10]
mychess[, Pop := sample(c(1,2,4), nrow(mychess), replace = T)]
mychess[, fens := sapply(pgn, function(x) {chsspgn <- Chess$new(); chsspgn$load_pgn(x); chsspgn$fen()})]

The code above just creates a sample dataset of 10 openings. I then plot those using plotly and based on the guidance I found here https://plotly-r.com/supplying-custom-data.html#fig:hover-annotate I tried to create a new function that shows the chessboard when you click on the points. The chessboard function is the one specified here https://chessboardjs.com/examples#1002 and if I understand it correctly (please correct me if I am wrong) it returns a 'div' element which is the one I have to show.

q = plot_ly(mychess, x = ~eco, y = ~Pop) %>% 
  add_markers(text = ~name,
              customdata = ~fens)  
    
onRender(
  q, "
     function(el) {
      el.on('plotly_click', function(d) {
      var pt = d.points[0]; 
      var fen =  d.points[0].customdata
      var newboard = Chessboard('myBoard', fen);
      newboard.style.visibility = 'visible';
      newboard.style.display = 'block';
      })}")

Unfortunately nothing shows. I used the information provided here Show/hide 'div' using JavaScript but it seems I am missing something.

Update: I tried the suggestion by @Bas to convert the images by using the following code:

png(pic1 <- tempfile(fileext = ".png"));chessboardjs(fen = mychess$fens[1]); dev.off() 
text = knitr::image_uri(pic1) 
text = sub(".*?,", "", text) 
html <- sprintf('<html><body><img src="data:image/png;base64,%s"></body></html>', text) 
cat(html, file = tf2 <- tempfile(fileext = ".html")) 
browseURL(tf2)

But this doesn't work either.


Solution

  • You have to include the Chessboard JavaScript and CSS-Files in order to use the Chessboard function. The function will not create a div but append the chessboard-visualization to a given CSS-selector. So the div has to be created beforehand.


    Shiny version:

    library(plotly)
    library(htmlwidgets)
    library(data.table)
    library(rchess)
    library(shiny)
    
    data("chessopenings")
    setDT(chessopenings)
    mychess = chessopenings[1:10]
    mychess[, Pop := sample(c(1,2,4), nrow(mychess), replace = T)]
    mychess[, fens := sapply(pgn, function(x) {chsspgn <- Chess$new();
    chsspgn$load_pgn(x); chsspgn$fen()})]
    
    ui <- fluidPage(
      tags$head(tags$link(href = "https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.css", rel = "stylesheet", type = "text/css")),
      tags$head(tags$script(src = "https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.js")),
      plotlyOutput("plt"),
      div(id = "myBoard", style="width: 400px")
    )
    
    server <- function(input, output, session) {
      output$plt <- renderPlotly({
        q = plot_ly(mychess, x = ~eco, y = ~Pop) %>% 
          add_markers(text = ~name,
                      customdata = ~fens)  
        onRender(
          q, "
         function(el) {
          el.on('plotly_click', function(d) {
          var pt = d.points[0];
          var fen =  d.points[0].customdata;
          var newboard = Chessboard('myBoard', fen);
          })}")
      })
    }
    
    shinyApp(ui, server)
    

    To see the images of chess pieces you have the include them in the www/img/chesspieces/wikipedia/ folder of your Shiny App. Otherwise you will see some errors in the browser console.

    GET http://127.0.0.1:5247/img/chesspieces/wikipedia/wK.png HTTP/1.1 404 Not Found 11ms


    A Markdown version:

    To see the chess positions you have to download the png files and put them in this folder structure /img/chesspieces/wikipedia/ based on where the .Rmd document is.

    ---
      title: "Chess Openings"
      output: html_document
    ---
    
    <style type="text/css">
    @import url("https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.css");
    </style>
    
      <script src="https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.js"></script>
    
    ```{r setup, include=FALSE}
    library(plotly)
    library(htmlwidgets)
    library(data.table)
    library(rchess)
    library(shiny)
    data("chessopenings")
    setDT(chessopenings)
    mychess = chessopenings[1:10]
    mychess[, Pop := sample(c(1,2,4), nrow(mychess), replace = T)]
    mychess[, fens := sapply(pgn, function(x) {chsspgn <- Chess$new();
    chsspgn$load_pgn(x); chsspgn$fen()})]
    ```
    
    ```{r out.width='100%', echo=FALSE, warning = FALSE}
    q = plot_ly(mychess, x = ~eco, y = ~Pop) %>%
      add_markers(text = ~name,
                  customdata = ~fens)
    onRender(
      q, "
     function(el) {
      el.on('plotly_click', function(d) {
      var pt = d.points[0];
      var fen =  d.points[0].customdata;
      var newboard = Chessboard('myBoard', fen);
      })}")
    ```
    
      <div id="myBoard" style="width: 400px"></div>