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

如何更新DataTable时保留滚动位置?Shiny+DT开发问题

问题1:DT更新时保持滚动位置

解决方案思路

replaceData() 会触发表格重绘,导致滚动位置重置。需要手动记录更新前的滚动位置,在表格更新完成后恢复该位置。

修改后的完整代码

library(shiny)
library(DT)

ui <- fluidPage(
  # 添加自定义JS用于保存和恢复滚动位置
  tags$script(HTML("
    // 获取当前滚动位置并发送给Shiny
    Shiny.addCustomMessageHandler('getScrollPosition', function(message) {
      var scrollBody = $('#' + message.id).find('.dataTables_scrollBody');
      Shiny.setInputValue(message.id + '_scrollTop', scrollBody.scrollTop());
      Shiny.setInputValue(message.id + '_scrollLeft', scrollBody.scrollLeft());
    });
    // 恢复之前保存的滚动位置
    Shiny.addCustomMessageHandler('restoreScrollPosition', function(message) {
      var scrollBody = $('#' + message.id).find('.dataTables_scrollBody');
      // 延迟执行确保表格已完成更新
      setTimeout(function() {
        scrollBody.scrollTop(Shiny.inputBindings.bindingNames['shiny.action'].getValue(message.id + '_scrollTop'));
        scrollBody.scrollLeft(Shiny.inputBindings.bindingNames['shiny.action'].getValue(message.id + '_scrollLeft'));
      }, 100);
    });
  ")),
  DTOutput("graph_table_1")
)

server <- function(input, output, session) {
  graph_data_1 <- reactiveVal(data.frame(
    A = 1:10, B = rnorm(10), C = rnorm(10), D = rnorm(10),
    E = rnorm(10), F = rnorm(10), G = rnorm(10), H = rnorm(10)
  ))
  
  # 创建DT代理
  proxy <- dataTableProxy("graph_table_1")

  output$graph_table_1 <- renderDT({
    datatable(
      graph_data_1(),
      selection = list(target = "cell"),
      options = list(scrollX = TRUE, scrollY = "300px", paging = FALSE, searching = FALSE, stateSave = FALSE)
    )
  })

  observeEvent(input$graph_table_1_cells_selected, {
    selected_cells <- input$graph_table_1_cells_selected
    if (nrow(selected_cells) > 0) {
      temp_data <- graph_data_1()
      for (cell in 1:nrow(selected_cells)) {
        row <- selected_cells[cell, 1]
        col <- selected_cells[cell, 2]
        if (row <= nrow(temp_data) && col <= ncol(temp_data)) {
          temp_data[row, col] <- NA
        }
      }
      # 1. 记录当前滚动位置
      session$sendCustomMessage(type = "getScrollPosition", message = list(id = "graph_table_1"))
      # 2. 更新数据源
      graph_data_1(temp_data)
      # 3. 更新表格内容
      replaceData(proxy, temp_data, resetPaging = FALSE)
      # 4. 恢复滚动位置
      session$sendCustomMessage(type = "restoreScrollPosition", message = list(id = "graph_table_1"))
    }
  })
}

shinyApp(ui, server)

关键修改说明

  • 新增JS代码:通过自定义消息处理器获取表格滚动容器的scrollTop和scrollLeft值,保存为Shiny输入变量;更新完成后再读取这些值恢复滚动位置。
  • 延迟执行恢复操作:用setTimeout确保表格完成重绘后再设置滚动位置,避免无效操作。

问题2:修复无法运行的DT渲染代码

问题原因

代码中直接在格式化函数里调用响应式对象graph_data_test_1(),未做隔离处理,且重复调用可能导致依赖混乱;同时列名处理逻辑存在潜在问题。

修复后的代码

output$test_graph_table_1 <- renderDT({ 
  # 先隔离响应式数据,避免不必要的重绘触发
  data <- isolate(graph_data_test_1())
  
  datatable(
    data,
    editable = TRUE,
    selection = list(target = 'cell'),
    options = list(
      dom = 't',
      paging = FALSE,
      scrollY = "600px",
      scrollX = TRUE,
      scrollCollapse = TRUE,
      columnDefs = list(
        list(className = 'dt-center', targets = '_all')
      )
    ),
    rownames = FALSE,
    class = 'cell-border stripe'
  ) %>%
    formatStyle(
      columns = names(data),  # 使用隔离后的数据集列名
      backgroundColor = '#000000',
      color = '#FFFFFF'
    ) %>%
    formatPercentage(
      columns = setdiff(names(data)[-1], "time"),  # 基于隔离数据处理列筛选
      digits = 2
    )
})

关键修复点

  1. 提前隔离响应式数据:将isolate(graph_data_test_1())赋值给变量data,后续所有列名引用都基于该变量,避免重复调用响应式对象。
  2. 统一列名来源:格式化函数的columns参数使用隔离后数据集的列名,避免因响应式依赖导致的渲染错误。
  3. 优化列筛选逻辑:确保setdiff操作基于实际存在的列名,减少潜在报错风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 21:08:11