1

我是一个 R 闪亮的应用程序,我想在单击 {ggiraph} 散点图时向数据点显示包含附加信息的叠加页面。

我想像在 R 中一样动态地构建这个覆盖页面renderUI,这就是我不想使用 {ggiraph} 的集成onlick功能的原因。但是,如果有一个涉及 {ggiraph}onlick函数的纯 javascript 解决方案的简单实现,那么我会感兴趣。但是,请记住,我想在所述叠加页面中的 R 中显示 data.frame 的几列,所以如果是纯 javascript 解决方案,我猜该页面需要在 javascript 中构造为 HTML。

我目前的方法使用 {ggiraph}input$plot_selected对基础数据进行子集化,我用它来构建动态覆盖页面。由于这是一个反应式端点,我使用了一个 javascriptmutationObserver来跟踪 DOM 树的变化,一旦它被触发,它就会切换覆盖页面。理想情况下我想听动态覆盖页面的变化。但是,我无法定位相应的节点。任何尝试都会产生错误:

TypeError:参数 1 ('target') 到 MutationObserver.observe 必须是 Node 的实例

我查看了这个SO question以及mutationObserver的文档以及此处提到的所有示例,但我没有看到为什么我的 javascript 无法使用的原因

target = document.getElementById('targetthis')

targetthis动态覆盖页面周围的硬编码 div 在哪里。

但是,如果我document.body改为目标,它可以正常工作,但现在对 DOM 树的任何更改都会切换覆盖页面。在我的示例中,有一个selectInput可以但不应该切换覆盖页面。我也很困惑,mutationObserver唯一的配置适用于childList我认为可以使用的地方characterData(因为覆盖页面的文本会动态变化)。

我看到的另一种可能性是启动mutationObserver图形的 onclick 并在显示覆盖页面后断开它。在这种情况下,我可以document.body用作目标。但对于手头的任务来说,它似乎过于复杂。

我将不胜感激任何帮助。

这是一个可重现的示例:

library(shiny)
library(ggplot2)
library(ggiraph)

jsCode <- "
// select the target node
var target = document.getElementById('targetthis')  

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    if( document.getElementById('overlay')) {
    document.getElementById('overlay').style.display = 'block';
    }
  });    
});

// configuration of the observer:
// it works with childList although I would expect it to work with characterData
var config = {
  childList: true,
  subtree: true,
  // attruibutes: true,
  // characterData: true
};

// pass in the target node, as well as the observer options
// doesn't work if target is used instead of document.body
observer.observe(document.body, config); 
    
function off() {
  document.getElementById('overlay').style.display = 'none';
}
"

shinyApp(ui = fluidPage(
  
  tags$script(jsCode),

  # overlay css
  tags$head(
    tags$style(HTML("
#overlay {
  position: fixed;
  display: none;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0,0,0,0.5);
  z-index: 2;
  cursor: pointer;
}

#profile {
  color: black;
  background-color: white;
  position: absolute;
  top: 50%;
  left: 50%;
  font-size: 50px;
  transform: translate(-50%,-50%);
  -ms-transform: translate(-50%,-50%);
}

    "))
  ),

  sidebarLayout(

  sidebarPanel(
    # test input overlay page should not be shown when input changes
    selectInput("test",
                "This is a test input",
                choices = c("Model A", "Model B", "Model C"))
  ),
  mainPanel(
    # plot
    girafeOutput("plot"),
    # overlay html
    # this hard coded div contains the overlay page 
    div(id = "targetthis",
                 uiOutput("overlay_page")),
    )
)),

  server = function(input, output) {

    # dynamic overlay page
    # preferably I want to build this page inside R (and not javascriptto,)
    output$overlay_page <- renderUI({
      
      info <- subset(mtcars, rownames(mtcars) == input$plot_selected)
      
    tagList(div(id = "overlay",
                onclick = "off()",
                tags$table(id = "profile",
                           style="width:80%",
                  tags$tr(
                    tags$th(colspan = 3,
                            rownames(info))
                  ),
                  tags$tr(
                    tags$td(colnames(info)[1]),
                    tags$td(info[, 1])
                  ),
                  tags$tr(
                    tags$td(colnames(info)[2]),
                    tags$td(info[, 2])
                  ),
                  tags$tr(
                    tags$td(colnames(info)[3]),
                    tags$td(info[, 3])
                  ),
                  tags$tr(
                    tags$td(colnames(info)[4]),
                    tags$td(info[, 4])
                  )
                  )
      )
      )
    
    })

    # plot
    output$plot <- renderGirafe({

      
      data <- mtcars

      p <- ggplot(aes(x = wt,
                      y = mpg,
                      data_id = row.names(mtcars)
                      ),
                  data = data) +
        geom_point_interactive(size = 3) +
        theme_minimal()

      girafe(
        ggobj = p,
        options = list(
          opts_hover(css = "fill:red;cursor:pointer;"),
          opts_selection(type = "single")
        )
      )
    })

  }

)
4

1 回答 1

3

@VictorPerrier 在 Twitter 上评论说只使用带有 的模态窗口modalDialog,我完全没有想到。我玩弄了它,它几乎可以在没有一行 javascript 的情况下完成我想要它做的事情(参见下面的代码)。

我将保留这个问题,以防万一有人有一个“真正的”覆盖页面解决方案(我的用例不需要它,但其他人可能正在寻找它)。当然,在花了一些时间使用mutationObserverAPI 之后,我真的很想知道,为什么我的方法不起作用。

library(shiny)
library(ggplot2)
library(ggiraph)


shinyApp(ui = fluidPage(

  sidebarLayout(

  sidebarPanel(
    # test input overlay page should not be shown when input changes
    selectInput("test",
                "This is a test input",
                choices = c("Model A", "Model B", "Model C"))
  ),
  mainPanel(
    # plot
    girafeOutput("plot")
))),

  server = function(input, output) {

    # dynamic overlay page
    # preferably I want to build this page inside R (and not javascriptto,)
    observeEvent(input$plot_selected, {
      
      info <- subset(mtcars, rownames(mtcars) == input$plot_selected)
      
      showModal(shiny::modalDialog(
      
      tags$table(id = "profile",
                 style="width:80%",
                 tags$tr(
                   tags$th(colspan = 3,
                           rownames(info)
                   )
                 ),
                 tags$tr(
                   tags$td(colnames(info)[1]),
                   tags$td(info[, 1])
                 ),
                 tags$tr(
                   tags$td(colnames(info)[2]),
                   tags$td(info[, 2])
                 ),
                 tags$tr(
                   tags$td(colnames(info)[3]),
                   tags$td(info[, 3])
                 ),
                 tags$tr(
                   tags$td(colnames(info)[4]),
                   tags$td(info[, 4])
                 )
      ),
      easyClose = TRUE,
      footer = NULL
      ))
    })

    # plot
    output$plot <- renderGirafe({

      data <- mtcars

      p <- ggplot(aes(x = wt,
                      y = mpg,
                      data_id = row.names(mtcars)
                      ),
                  data = data) +
        geom_point_interactive(size = 3) +
        theme_minimal()

      girafe(
        ggobj = p,
        options = list(
          opts_hover(css = "fill:red;cursor:pointer;"),
          opts_selection(type = "single")
        )
      )
    })

  }

)
于 2020-09-22T11:57:30.610 回答