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

R Shiny中Plotly图表hover联动触发递归栈溢出问题求助

问题:Shiny中Plotly双图表悬停联动触发栈溢出导致页面无响应

问题背景

需求为:鼠标悬停第一个Plotly图表的数据点时,自动高亮第二个图表中对应的同组数据点。使用highlight_key()(或crosstalk的SharedData())绑定共享数据,配合highlight("plotly_hover")实现联动后,功能看似生效,但页面很快无响应。浏览器控制台报栈溢出错误,若将触发事件改为非plotly_hover(如plotly_click),则可正常运行(仅轻微卡顿)。

浏览器报错信息

Uncaught RangeError: Maximum call stack size exceeded
at JSON.stringify (<anonymous>)
at e.value (shinyapp.ts:382:22)
at e.value (inputBatchSender.ts:63:23)
at e.value (inputBatchSender.ts:41:16)
at e.value (inputNoResendDecorator.ts:45:19)
at e.value (inputEventDecorator.ts:45:21)
at e.value (inputRateDecorator.ts:63:19)
at e.value (invoke.ts:38:17)
at e.value (inputRateDecorator.ts:39:75)
at e.value (inputValidateDecorator.ts:55:19)

可复现代码

library(shiny)
library(plotly)

ui <- fluidPage(
  sliderInput("rows", label = "# Rows", min = 50, max = 150, value = 100),
  plotlyOutput("scatter1"),
  plotlyOutput("scatter2")
)

server <- function(input, output, session) {
  
  iris_new <- reactive({
    highlight_key(iris[1:as.numeric(input$rows),])
  })
  
  output$scatter1 <- renderPlotly({
    plot_ly(
      iris_new(),
      x = ~Sepal.Length, 
      y = ~Sepal.Width,
      color = ~Species,
      type = "scatter",
      mode = "markers"
    ) %>% 
      highlight("plotly_hover")
  })
  
  output$scatter2 <- renderPlotly({
    plot_ly(
      iris_new(),
      x = ~Petal.Length, 
      y = ~Petal.Width,
      color = ~Species,
      type = "scatter",
      mode = "markers"
    ) %>% 
      highlight("plotly_hover")
  })
}

shinyApp(ui, server)

环境版本:plotly 4.10.2,shiny 1.7.4


问题分析与解决方案

原因

当两个图表都绑定highlight("plotly_hover")时,会触发递归事件循环:

  1. 悬停第一个图表,触发plotly_hover事件,通过共享数据同步高亮第二个图表;
  2. 第二个图表的高亮状态变化会再次触发自身的plotly_hover事件,反过来同步第一个图表;
  3. 该循环不断重复,导致JavaScript调用栈溢出,最终页面无响应。

解决方案

仅在单个图表上设置highlight("plotly_hover")作为触发源,另一个图表仅基于共享数据被动高亮,不主动监听plotly_hover事件:

修改后的代码:

library(shiny)
library(plotly)

ui <- fluidPage(
  sliderInput("rows", label = "# Rows", min = 50, max = 150, value = 100),
  plotlyOutput("scatter1"),
  plotlyOutput("scatter2")
)

server <- function(input, output, session) {
  
  iris_new <- reactive({
    highlight_key(iris[1:as.numeric(input$rows),])
  })
  
  output$scatter1 <- renderPlotly({
    plot_ly(
      iris_new(),
      x = ~Sepal.Length, 
      y = ~Sepal.Width,
      color = ~Species,
      type = "scatter",
      mode = "markers"
    ) %>% 
      highlight("plotly_hover") # 仅第一个图表作为悬停触发源
  })
  
  output$scatter2 <- renderPlotly({
    plot_ly(
      iris_new(),
      x = ~Petal.Length, 
      y = ~Petal.Width,
      color = ~Species,
      type = "scatter",
      mode = "markers"
    ) # 第二个图表不设置highlight,被动响应共享数据的高亮状态
  })
}

shinyApp(ui, server)

补充说明

  • 使用crosstalk::SharedData替代highlight_key时,需遵循同样逻辑:仅单个图表监听plotly_hover事件,避免双向触发循环。
  • 若需要双向悬停高亮(悬停任意一个图表都能高亮另一个),需通过自定义JavaScript事件处理,手动控制触发逻辑以避免递归循环。

内容的提问来源于stack exchange,提问作者Marcin Karliński

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 13:11:00