Skip to content

Instantly share code, notes, and snippets.

@daattali
Last active March 12, 2026 07:51
Show Gist options
  • Select an option

  • Save daattali/9440f0b278dbbf538b3587e026811426 to your computer and use it in GitHub Desktop.

Select an option

Save daattali/9440f0b278dbbf538b3587e026811426 to your computer and use it in GitHub Desktop.
# I'm trying to let the user select points and paint them in a certain colour, and if the user clicks on a point then paint that point a different colour.
# It looks like the pointNumber and curveNumber data that plotly returns are different for the same points. I'm not sure how curveNumber works, but to me it doesn't make sense yet :)
# Any help is appreciated!
library(plotly)
library(shiny)
ui <- fluidPage(
plotlyOutput("plot")
)
server <- function(input, output, session) {
output$plot <- renderPlotly({
click_data <- event_data("plotly_click", source = "select")
select_data <- event_data("plotly_selected", source = "select")
data <- mtcars
data$col <- "black"
if (!is.null(select_data)) {
cat(str(select_data))
idx <- select_data$pointNumber + 1
data[idx, "col"] <- "blue"
}
if (!is.null(click_data)) {
cat(str(click_data))
idx <- click_data$pointNumber + 1
data[idx, "col"] <- "red"
}
p <- ggplot(data, aes(mpg, wt, col = I(col))) + geom_point()
ggplotly(p, source = "select")
})
}
shinyApp(ui, server)
# This is very similar to the previous file, but I tried applying Carson's advise of setting the key aesthetic to row names
library(plotly)
library(shiny)
ui <- fluidPage(
plotlyOutput("plot")
)
server <- function(input, output, session) {
output$plot <- renderPlotly({
click_data <- event_data("plotly_click", source = "select")
select_data <- event_data("plotly_selected", source = "select")
data <- mtcars
key <- row.names(data)
data$col <- "black"
if (!is.null(select_data)) {
cat(str(select_data))
idx <- select_data$pointNumber + 1
data[idx, "col"] <- "blue"
}
if (!is.null(click_data)) {
cat(str(click_data))
idx <- click_data$pointNumber + 1
data[idx, "col"] <- "red"
}
p <- ggplot(data, aes(mpg, wt, col = I(col), key = key)) + geom_point()
ggplotly(p, source = "select")
})
}
shinyApp(ui, server)
@jpcompartir
Copy link

Similar problem to romanhaa - does anybody have a neat solution for this? When using renderPlotly and not rendering a ggplot first, it doesn't seem trivial to keep track of rows efficiently. It's possible for example when encoding color with an integer, but the order gets thrown around within plotly if color is encoded as a string, or factor.

@asadow
Copy link

asadow commented Apr 16, 2024

Is it possible to maintain the same zoom state after click/selection?

@ismirsehregal
Copy link

ismirsehregal commented Mar 10, 2026

@asadow a little late but you can maintain the zoom state by using plotlyProxy to modify the plotly object instead of re-rendering it:

library(shiny)
library(plotly)

ui <- fluidPage(
  plotlyOutput("my_plot"),
  verbatimTextOutput("click"),
  verbatimTextOutput("selected")
)

server <- function(input, output, session) {
  
  nms <- row.names(mtcars)
  
  output$my_plot <- renderPlotly({
    p <- plot_ly(
      mtcars,
      x = ~mpg,
      y = ~wt,
      customdata = nms,
      selected = list(marker = list(color = "red")),
      type = "scatter",
      mode = "markers"
    ) |>
      layout(dragmode = "select") |>
      event_register("plotly_selected") |>
      event_register("plotly_click") |>
      event_register("plotly_doubleclick")
  })
  
  output$click <- renderPrint({
    d <- event_data("plotly_click")
    if (is.null(d)) "Click events appear here (double-click to clear)" else d
  })
  
  output$selected <- renderPrint({
    d <- event_data("plotly_selected")
    if (is.null(d)) "Selected points appear here (double-click to clear)" else d
  })
  
  plot_proxy <- plotlyProxy("my_plot", session, deferUntilFlush = TRUE)
  
  plotly_click <- reactive(event_data("plotly_click"))
  plotly_doubleclick <- reactive(event_data(event = "plotly_doubleclick", source="my_plot"))
  plotly_selected <- reactive(event_data(event = "plotly_selected", source="my_plot"))
  
  observeEvent(plotly_click(), {
    # plotlyProxyInvoke(plot_proxy, "restyle", list(selectedpoints = -1)) # deselect all points for all traces
    plotlyProxyInvoke(plot_proxy, "restyle", list(selectedpoints = list(I(plotly_click()$pointNumber))), plotly_click()$curveNumber)
  })
}

shinyApp(ui, server)

Here my related SO answer can be found.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment