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

如何在Shiny的DT表格中冻结前两行以避免排序与过滤

How to Freeze the First Two Rows in DT (No Sort/Filter Impact)

Hey there! I totally get where you're coming from—you want those top two non-data rows in your DT table to stay put, unaffected by sorting or filtering, and you already know about freezing columns so that's not what you need. DT doesn't have an out-of-the-box setting for this, but we can implement a couple of solid workarounds depending on your needs.


Option 1: Split Your Table Into Two Separate Components (Most Reliable)

The cleanest approach is to create a static table for your two non-data rows, then place your interactive DT data table below it. This way, the top rows are completely independent of the data table's sorting/filtering logic.

Here's an example using R (since DT is widely used with R/Shiny):

library(DT)
library(htmltools)

# Your non-data header rows
top_summary_rows <- data.frame(
  Metric = c("Total Entries", "Average Value"),
  Value = c("150", "67.2"),
  Status = c("Active: 120", "Inactive: 30")
)

# Your actual dataset
main_data <- data.frame(
  Metric = paste0("Row ", 1:20),
  Value = sample(40:90, 20, replace = TRUE),
  Status = sample(c("Active", "Inactive"), 20, replace = TRUE)
)

# Render static top table (no interactivity)
top_table <- datatable(
  top_summary_rows,
  options = list(
    searching = FALSE,
    ordering = FALSE,
    paging = FALSE,
    info = FALSE,
    dom = "t" # Only show the table itself, no controls
  ),
  rownames = FALSE,
  style = "bootstrap"
)

# Render interactive main data table
main_dt_table <- datatable(
  main_data,
  options = list(
    searching = TRUE,
    ordering = TRUE,
    paging = TRUE
  ),
  rownames = FALSE,
  style = "bootstrap"
)

# Combine both tables into one output
tagList(
  top_table,
  main_dt_table
)

If you want the top rows to stay visible while scrolling the main table, add this custom CSS to make them sticky:

.fixed-top-table {
  position: sticky;
  top: 0;
  background-color: white;
  z-index: 10;
  margin-bottom: 0 !important;
}

Then update the top table's container to use this class:

top_table <- datatable(
  top_summary_rows,
  options = list(searching = FALSE, ordering = FALSE, paging = FALSE, info = FALSE, dom = "t"),
  rownames = FALSE,
  style = "bootstrap",
  container = tags$table(class = "fixed-top-table display")
)

Option 2: Inject Rows Into the Table Header (Single Table Structure)

If you prefer to keep everything in one table, you can add your non-data rows to the header section (above the column headers) using DT's headerCallback option. These rows will be part of the header, so they won't be touched by sorting/filtering.

Example:

main_dt_table <- datatable(
  main_data,
  options = list(
    searching = TRUE,
    ordering = TRUE,
    paging = TRUE,
    headerCallback = JS(
      "function(thead, data, start, end, display) {",
      "  // Add your two custom rows above the column headers",
      "  const row1 = '<tr><td>Total Entries</td><td>150</td><td>Active: 120</td></tr>';",
      "  const row2 = '<tr><td>Average Value</td><td>67.2</td><td>Inactive: 30</td></tr>';",
      "  $(thead).before(row1 + row2);",
      "}"
    )
  ),
  rownames = FALSE,
  style = "bootstrap"
)

Note: If your top rows are dynamic (e.g., calculated from the data), you'll need to pass the values from R to JavaScript using htmlwidgets::JS() with dynamic content.


Which Option to Choose?

  • Go with Option 1 if you want full control over the top rows' styling and behavior, and you don't mind having two separate table components. It's the most robust solution.
  • Use Option 2 if you need a single table DOM structure, and your top rows are either static or easy to pass dynamically to JS.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:46:17