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

Shiny中DT数据表selectInput更新后保持selectize样式的方法

DT数据表更新后selectize样式丢失的修复方案

问题根源

initComplete 回调仅在数据表首次渲染时执行一次,负责给初始的selectInput添加selectize样式和搜索功能。但通过replaceData()更新数据表后,新生成的selectInput节点不会触发initComplete,因此没有被初始化selectize,导致样式丢失。

修复步骤

  1. 将selectize初始化逻辑移至drawCallback:drawCallback会在每次数据表重绘(包括更新后)时执行,确保新生成的selectInput都能被处理。
  2. 添加旧实例销毁逻辑:避免重复初始化selectize导致的DOM异常或功能冲突。
  3. 调整执行顺序:先初始化selectize,再绑定Shiny事件,保证绑定的是处理后的元素。

修改后的完整代码

library(shiny)
library(DT)

# 生成selectize初始化JS:销毁旧实例+初始化新实例
selectize_ids <- function(ids) {
  tmp <- paste0("  let $el = $('#", ids, "');")
  tmp <- paste0(tmp, " if($el.data('selectize')) $el[0].selectize.destroy();")
  tmp <- paste0(tmp, " $el.selectize();")
  c(
    "function(settings){",
    tmp,
    "}"
  )
}

shinyApp(
  ui = fluidPage(
    div(style = "display: none;", selectInput(inputId = "dummy", label = NULL, choices = 1:2)),
    actionButton(inputId = "my_button", label = "Click Me"),
    DT::dataTableOutput(outputId = "table")
  ),
  server = function(input, output, session) {
    
    input_ids <- c("A_1", "A_2")
    df_reactive <- reactive({
      data.frame(
        A = sapply(input_ids, function(id) as.character(selectInput(
          inputId = id, 
          label = NULL, 
          choices = paste0(c("a", "b", "c"), input$my_button)
        )))
      )
    })
    
    output$table <- DT::renderDataTable({
      DT::datatable(
        data = isolate(df_reactive()),
        selection = "none",
        rownames = F,
        escape = F,
        options = list(
          # 保留initComplete确保首次加载正常
          initComplete = JS(selectize_ids(input_ids)),
          preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
          drawCallback = JS('function() { 
            ', JS(selectize_ids(input_ids)), '
            Shiny.bindAll(this.api().table().node()); 
          } ')
        )
      )
    })
    
    proxy <- DT::dataTableProxy(outputId = "table")
    observe({
      DT::replaceData(proxy = proxy, data = df_reactive(), rownames = F)
    })
  }
)

更灵活的通用方案(适配动态变化的selectInput)

如果selectInput的id会随用户输入动态增减,无需手动维护id列表,直接选中数据表内所有select元素进行处理:

library(shiny)
library(DT)

# 通用selectize初始化:处理数据表内所有select元素
selectize_all <- function() {
  c(
    "function(settings){",
    "  $(settings.nTable).find('select').each(function(){",
    "    let $el = $(this);",
    "    if($el.data('selectize')) $el[0].selectize.destroy();",
    "    $el.selectize();",
    "  });",
    "}"
  )
}

shinyApp(
  ui = fluidPage(
    div(style = "display: none;", selectInput(inputId = "dummy", label = NULL, choices = 1:2)),
    actionButton(inputId = "my_button", label = "Click Me"),
    DT::dataTableOutput(outputId = "table")
  ),
  server = function(input, output, session) {
    
    # 模拟动态变化的input_ids
    input_ids <- reactive(paste0("A_", 1:(input$my_button + 1)))
    
    df_reactive <- reactive({
      data.frame(
        A = sapply(input_ids(), function(id) as.character(selectInput(
          inputId = id, 
          label = NULL, 
          choices = paste0(c("a", "b", "c"), input$my_button)
        )))
      )
    })
    
    output$table <- DT::renderDataTable({
      DT::datatable(
        data = isolate(df_reactive()),
        selection = "none",
        rownames = F,
        escape = F,
        options = list(
          initComplete = JS(selectize_all()),
          preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
          drawCallback = JS('function() { 
            ', JS(selectize_all()), '
            Shiny.bindAll(this.api().table().node()); 
          } ')
        )
      )
    })
    
    proxy <- DT::dataTableProxy(outputId = "table")
    observe({
      DT::replaceData(proxy = proxy, data = df_reactive(), rownames = F)
    })
  }
)

内容的提问来源于stack exchange,提问作者Ben Ernest

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 04:54:54