如何从event_data获取plotly点击事件的source?解决循环事件源错误
问题解答
1. 能否从event_data获取事件源?
默认情况下,event_data()返回的结果(比如你提到的clickData)确实不包含source字段,但你调用event_data时传入的source参数值,本身就是触发事件的图表标识。
如果希望让返回的clickData直接携带source信息,可以在创建Plotly图表时通过自定义逻辑补充,但更简便的方式是直接复用你传入的source参数值(比如paste0("heatmap_", heatmapName))作为事件源的判断依据。
2. 循环创建多图表时点击事件指向最后一个图表的问题解决
你的示例代码核心问题是Shiny闭包作用域导致循环变量共享:for循环里的name变量在循环结束后会保留最后一个值,所有的observe/render逻辑都会引用这个最终值。以下是两种解决方法:
方法一:用local()包裹循环内代码,创建独立作用域
通过local()为每次循环迭代生成独立的作用域,确保每个图表的逻辑绑定当前迭代的name值:
# 修改创建图表的observe observe({ for(name in input$selections) { local({ current_name = name p = ggplot(mtcars[, c(3,4)]) + geom_point(aes(x = disp, y = hp)) p_ly = ggplotly(p, source = current_name) output[[paste0(current_name, "_plot")]] = renderPlotly({p_ly}) }) } }) # 修改处理点击的observe observe({ for(name in input$selections) { local({ current_name = name clickData = event_data("plotly_click", source = current_name) if(!is.null(clickData)) { showNotification(paste0("Clicked ", current_name)) session$userData$plotlyInputStore[[paste0("plotly_click-", current_name)]] = NULL } }) } })
方法二:改用lapply代替for循环
lapply的每个迭代会自动创建独立作用域,避免闭包变量共享问题,代码更简洁:
# 修改创建图表的observe observe({ lapply(input$selections, function(name){ p = ggplot(mtcars[, c(3,4)]) + geom_point(aes(x = disp, y = hp)) p_ly = ggplotly(p, source = name) output[[paste0(name, "_plot")]] = renderPlotly({p_ly}) }) }) # 修改处理点击的observe observe({ lapply(input$selections, function(name){ clickData = event_data("plotly_click", source = name) if(!is.null(clickData)) { showNotification(paste0("Clicked ", name)) session$userData$plotlyInputStore[[paste0("plotly_click-", name)]] = NULL } }) })
完整修正后的代码
library(shiny) library(ggplot2) library(plotly) ui = fluidPage( fluidRow(selectInput("selections", NULL, choices = c("A", "B", "C"), multiple = T)), fluidRow(uiOutput("plots")) ) server = function(input, output, session) { # Add plot rows for each dataset selected observe({ output$plots = renderUI({ lapply(input$selections, function(name) { fluidRow(plotlyOutput(paste0(name, "_plot"))) }) }) }) # Make plots upon selection observe({ lapply(input$selections, function(name){ p = ggplot(mtcars[, c(3,4)]) + geom_point(aes(x = disp, y = hp)) p_ly = ggplotly(p, source = name) output[[paste0(name, "_plot")]] = renderPlotly({p_ly}) }) }) # Process clicks observe({ lapply(input$selections, function(name){ clickData = event_data("plotly_click", source = name) if(!is.null(clickData)) { showNotification(paste0("Clicked ", name)) session$userData$plotlyInputStore[[paste0("plotly_click-", name)]] = NULL } }) }) } shinyApp(ui, server)
内容的提问来源于stack exchange,提问作者tomato_soup
相关产品推荐
相关产品推荐

