如何检测renderUI生成的Shiny Dashboard折叠框的折叠状态?
解决Shiny动态生成可折叠Box的状态检测问题
当通过renderUI/uiOutput动态生成box时,页面初始化阶段这些box还未渲染到DOM中,提前在ui里调用collapseInput绑定的事件监听器找不到对应元素,自然无法触发状态通知。以下是两种可行的解决方法:
方案1:将事件绑定脚本与动态Box一同生成
把collapseInput的调用放到renderUI的输出内容里,确保box渲染完成后再执行事件绑定代码,让监听器能找到目标元素。
修改后的完整代码:
library(shiny) library(shinydashboard) library(shinyjs) jscode <- " shinyjs.collapse = function(boxid) { $('#' + boxid).closest('.box').find('[data-widget=collapse]').click(); }" collapseInput <- function(inputId, boxId) { tags$script( sprintf( "$('#%s').closest('.box').on('hidden.bs.collapse', function () {Shiny.onInputChange('%s', true);})", boxId, inputId ), sprintf( "$('#%s').closest('.box').on('shown.bs.collapse', function () {Shiny.onInputChange('%s', false);})", boxId, inputId ) ) } ui <- dashboardPage( dashboardHeader(), dashboardSidebar(), dashboardBody( useShinyjs(), extendShinyjs(text = jscode, functions = c("collapse")), actionButton("bt1", "Collapse box1"), actionButton("bt2", "Collapse box2"), br(), br(), uiOutput("boxes"), verbatimTextOutput(outputId = "res") ) ) server <- function(input, output) { output$boxes <- renderUI({ tagList( box(id = "box1", collapsible = TRUE, p("Box 1")), box(id = "box2", collapsible = TRUE, p("Box 2")), # 把事件绑定脚本和box一起输出 collapseInput(inputId = "iscollapsebox1", boxId = "box1"), collapseInput(inputId = "iscollapsebox2", boxId = "box2") ) }) observeEvent(input$bt1, { js$collapse("box1") }) observeEvent(input$bt2, { js$collapse("box2") }) output$res <- renderPrint({ list(box1_collapsed = input$iscollapsebox1, box2_collapsed = input$iscollapsebox2) }) } shinyApp(ui, server)
方案2:使用事件委托(适配大量动态Box场景)
如果需要生成数量不确定的box,事件委托更高效——通过监听父容器的事件,自动匹配后续动态生成的子元素,无需为每个box单独编写绑定代码。
修改后的完整代码:
library(shiny) library(shinydashboard) library(shinyjs) jscode <- " shinyjs.collapse = function(boxid) { $('#' + boxid).closest('.box').find('[data-widget=collapse]').click(); } // 事件委托:监听页面中所有box的折叠/展开事件 $(document).on('hidden.bs.collapse', '.box .collapse', function() { var boxId = $(this).closest('.box').attr('id'); Shiny.onInputChange('iscollapse_' + boxId, true); }); $(document).on('shown.bs.collapse', '.box .collapse', function() { var boxId = $(this).closest('.box').attr('id'); Shiny.onInputChange('iscollapse_' + boxId, false); }); " ui <- dashboardPage( dashboardHeader(), dashboardSidebar(), dashboardBody( useShinyjs(), extendShinyjs(text = jscode, functions = c("collapse")), actionButton("bt1", "Collapse box1"), actionButton("bt2", "Collapse box2"), br(), br(), uiOutput("boxes"), verbatimTextOutput(outputId = "res") ) ) server <- function(input, output) { output$boxes <- renderUI({ tagList( box(id = "box1", collapsible = TRUE, p("Box 1")), box(id = "box2", collapsible = TRUE, p("Box 2")) ) }) observeEvent(input$bt1, { js$collapse("box1") }) observeEvent(input$bt2, { js$collapse("box2") }) output$res <- renderPrint({ list(box1_collapsed = input$iscollapse_box1, box2_collapsed = input$iscollapse_box2) }) } shinyApp(ui, server)
方案1适合少量固定数量的动态box,方案2无需为每个box单独写绑定脚本,适配任意数量的动态生成box。
内容的提问来源于stack exchange,提问作者Hannah
相关产品推荐
相关产品推荐

