使用 initComplete 在 Shiny 应用程序中使用 Javascript 定位特定 DT::datatable table

Target specific DT::datatable table with Javascript in Shiny app using initComplete

假设我们有一个带有两个 DT table 的 Shiny 应用,我们想在呈现 table 之后用一些 Javascript 对其进行修改.我们在每个 table 中将不同的上标附加到 header;我们在 table 1 上附加一个上标 '1',在 table 2 上附加一个上标 '2'。这个 JS 是使用 initComplete 触发的,即,当 table 是渲染。

我的 JS 技能不存在,所以我目前正在 select 将 table header 添加到 th:contains(<header-string>)。例如,将上标附加到包含字符串 'Sepal':

的 table header
jsc <- '
    function(settings, json) {
        $("th:contains(\'Sepal\')").append(\'<sup>1</sup>\');
    }
'

由于我们需要为不同的 table 设置不同的上标,因此我们需要定义单独的 Javascript 来为每个 table 触发。这个问题是 table headers 都包含我要查找的字符串,导致两个上标都附加到 table headers.

假设不同的 table 将始终在相关的 table header 中包含相同的字符串(因此 th:contains 将不是一个可行的选择 - - table header 可能完全相同),我们如何 select 一个特定的 table 来应用 Javascript?我们不能在 Shiny 应用程序中为 datatable 调用提供 elementId,例如,

output$table1<- renderDT({datatable(iris, elementId = 'table1')})

导致警告

Warning in renderWidget(instance) : Ignoring explicitly provided widget ID "table1"; Shiny doesn't use them

下面是一个可重现的例子。两个 table 具有相同的列名,因此 Javascript 将两个上标附加到每个 table 的 Sepal.Length header。期望的输出是第一个 table 的 Sepal.Length header 带有上标“1”,第二个 table 的 Sepal.Length header 带有上标“2”,而当前示例将“12”附加到 table 的 headers.

有趣的是,当应用程序在 RStudio 查看器中打开时,只有第一个 initComplete 是 运行,因此两个 table header 都有一个上标 '1 '.我忽略了这一点并在 Firefox 和 Chrome.

上检查结果
library(shiny)
library(DT)

jsc <- '
    function(settings, json) {
        $("th:contains(\'Sepal\')").append(\'<sup>1</sup>\');
    }
'

jsc2 <- '
    function(settings, json) {
        $("th:contains(\'Sepal\')").append(\'<sup>2</sup>\');
    }
'

ui <- {
    fluidPage(
        fluidRow(
            column(6,
               DTOutput('table1')
            ),
            column(6,
               DTOutput('table2')
            )
        )
    )
}

server <- function(input, output, session) {
    output$table1 <- renderDT({
        datatable(iris[, c('Species', 'Sepal.Length')],
            options(
                initComplete = JS(jsc))
        )
    })
    
    output$table2 <- renderDT({
        datatable(iris[, c('Species', 'Sepal.Length')],
            options(
                initComplete = JS(jsc2)  
            )
        )
    })
}

options(shiny.launch.browser = TRUE)
shinyApp(ui, server)

请注意,我知道我们可以直接将 <sup>1</sup> 写入 table header 中 escape = FALSE,但还有其他 JS 函数可以在 table 上执行,所以这种方法不会被 suitable。感谢您的任何建议。

首先,options必须是一个列表:

datatable(iris[, c('Species', 'Sepal.Length')],
          options = list(
            initComplete = JS(jsc)
          )
)

现在,您可以通过在 jQuery 选择器中提供 ID 来定位 table:

jsc <- '
    function(settings, json) {
        $("#table1 th:contains(\'Sepal\')").append(\'<sup>1</sup>\');
    }
'

否则,$("th:contains(\'Sepal\')") 将选择所有 th,其中包含 Sepal,并且可以在整个页面上找到。

但是这种方式改id需要改JS代码。更实际的做法是:

jsc <- '
    function(settings, json) {
        var $table = $(this.api().table().node());
        $table.find("th:contains(\'Sepal\')").append(\'<sup>1</sup>\');
    }
'

完整代码:

library(shiny)
library(DT)

jsc <- '
    function(settings, json) {
        var $table = $(this.api().table().node());
        $table.find("th:contains(\'Sepal\')").append(\'<sup>1</sup>\');
    }
'

jsc2 <- '
    function(settings, json) {
        var $table = $(this.api().table().node());
        $table.find("th:contains(\'Sepal\')").append(\'<sup>2</sup>\');
    }
'

ui <- {
  fluidPage(
    fluidRow(
      column(6,
             DTOutput('table1')
      ),
      column(6,
             DTOutput('table2')
      )
    )
  )
}

server <- function(input, output, session) {
  output$table1 <- renderDT({
    datatable(iris[, c('Species', 'Sepal.Length')],
              options = list(
                initComplete = JS(jsc)
              )
    )
  })
  
  output$table2 <- renderDT({
    datatable(iris[, c('Species', 'Sepal.Length')],
              options = list(
                initComplete = JS(jsc2)  
              )
    )
  })
}

shinyApp(ui, server)