You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 09:37:31