clicking same plotly marker twice does not trigger

2020-02-14 11:01发布

问题:

I am using Plotly's event_data("plotly_click") to do stuff (opening a modal) after the user clicked on a marker in a scatter plot. Afterwards (e.g. closing the modal), event_data("plotly_click") does of course not change and clicking on the same marker therefore does not trigger the same action again.

Minimal example:

library(plotly)
ui <- fluidPage(  
  plotlyOutput("plot")
)
server <- function(input, output, session) {
  output$plot <- renderPlotly({
    mtcars %>% plot_ly(x=~disp, y=~cyl)
  }) 

  # Do stuff after clicking on a marker in the plot
  observeEvent(event_data("plotly_click"), {
    print("do some stuff now") # this is not executed after second click on same marker
  })  
}
shinyApp(ui, server)

I have tried workarounds with shinyjs's onclick, to no avail (it works well in empty areas of the plot but not when clicking on markers):

shinyjs::onclick(id="plot", print("clicked"))

I have also tried using a reactive Value that stores the last click and is reset immediately afterwards (e.g. by event_data("plotly_hover")), but all tries fail because event_data("plotly_click") remains in its old value.

Can anyone help?

回答1:

[Edit: The issue has been fixed in Plotly 4.9.0. See answer below. This answer works up to Plotly 4.8.0. From plotly 4.9.0., delete the string .clientValue- from the source code or use below answer.]

I finally solved this issue. I know this is bothering some people, so I'll post my solution here:

Basically I use shinyjs package to reset the data about the last click (the source where event_data("plotly_click") gets its information from) on a certain event (a button in my case).

The definition of the function is (note that "A" needs to be replaced with plotly-source string if used):

extendShinyjs(text = "shinyjs.resetClick = function() { Shiny.onInputChange('.clientValue-plotly_click-A', 'null'); }")

Then this is called upon button click by js$resetClick().

Minimal example:

library(shiny)
library(plotly)
library(shinyjs)

ui <- shinyUI(
  fluidPage(
    useShinyjs(),
    # code to reset plotlys event_data("plotly_click", source="A") to NULL -> executed upon action button click
    # note that "A" needs to be replaced with plotly source string if used
    extendShinyjs(text = "shinyjs.resetClick = function() { Shiny.onInputChange('.clientValue-plotly_click-A', 'null'); }"),
    actionButton("reset", "Reset plotly click value"),
    plotlyOutput("plot"),
    verbatimTextOutput("clickevent")
  )
)


server <- shinyServer(function(input, output) {

  output$plot <- renderPlotly({
    plot_ly(mtcars, x=~cyl, y=~mpg)
  })

  output$clickevent <- renderPrint({
    event_data("plotly_click")
  })

  observeEvent(input$reset, {
    js$resetClick()
  })
})

shinyApp(ui, server)



回答2:

I had the same problem, and came up with a solution where I specified the source argument of the plotly object to be a reactive value as follows:

In plot_ly(data,x,y,...,source = x) and event_data(...,source = x) let x be an element of a reactiveValues object. When your event triggers, change the value of x (increment or hash), which instantiates a new event_data() object.

Worked like a charm.



回答3:

The issue has finally been fixed on Plotly side: https://github.com/ropensci/plotly/issues/1043

event_data("plotly_click", priority = "event") updates on every click, not only on shiny input change (as before). Working from Plotly 4.9.0 on.

Minimal example using Plotly 4.9.0:

library(shiny)
library(plotly)

ui <- shinyUI(
  fluidPage(
    plotlyOutput("plot", height = 200),
    verbatimTextOutput("time_last_click")
  )
)


server <- shinyServer(function(input, output) {

  output$plot <- renderPlotly({
    plot_ly(mtcars[1,], x=~cyl, y=~mpg, size = 1)
  })


  output$time_last_click <- renderPrint({
    tmp <- event_data("plotly_click", priority = "event")
    Sys.time()
  })

})

shinyApp(ui, server)