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")时,会触发递归事件循环:
- 悬停第一个图表,触发
plotly_hover事件,通过共享数据同步高亮第二个图表; - 第二个图表的高亮状态变化会再次触发自身的
plotly_hover事件,反过来同步第一个图表; - 该循环不断重复,导致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
相关产品推荐
相关产品推荐

