简体   繁体   English

在 Shiny R 中显示 ggiraph plot 的叠加页面 onclick

[英]Show Overlay Page onclick of ggiraph plot in Shiny R

I an R shiny app, I want to show an overlay page with additional information to a data point when clicking on a {ggiraph} scatterplot.我是一个 R shiny 应用程序,我想在单击 {ggiraph} 散点图时显示一个包含数据点附加信息的叠加页面。

I want to dynamically construct this overlay page as renderUI in R, which is why I do not want to use {ggiraph}'s integrated onlick function. But if there is an easy implementation of a pure javascript solution involving {ggiraph}'s onlick function, then I would be interested.我想在 R 中动态构建这个覆盖页面作为renderUI ,这就是为什么我不想使用 {ggiraph} 的集成onlick function。但是如果有一个简单的实现涉及 {ggiraph} 的onlick的纯 javascript 解决方案function,那我有兴趣。 However, keep in mind that I want to display several columns of a data.frame in R in said overlay page, so in case of a pure javascript solution the page would need to be constructed as HTML in javascript, I guess.但是,请记住,我想在所述覆盖页面中显示 R 中的 data.frame 的几列,因此在纯 javascript 解决方案的情况下,我猜页面需要在 javascript 中构建为 HTML。

My current approach uses {ggiraph}'s input$plot_selected to subset the underlying data, which I use to build a dynamic overlay page.我当前的方法使用 {ggiraph} 的input$plot_selected对基础数据进行子集化,我用它来构建动态覆盖页面。 Since this is a reactive endpoint, I use a javascript mutationObserver that tracks changes of the DOM tree, and once its triggered it will toggle the overlay page.由于这是一个反应式端点,我使用 javascript mutationObserver来跟踪 DOM 树的变化,一旦它被触发,它就会切换覆盖页面。 Ideally I only want to listen to changes of the dynamic overlay page.理想情况下我想听动态覆盖页面的变化。 However, I am not able to target the corresponding node.但是,我无法定位相应的节点。 Any attempt yields the error:任何尝试都会产生错误:

TypeError: Argument 1 ('target') to MutationObserver.observe must be an instance of Node TypeError:MutationObserver.observe 的参数 1('target')必须是 Node 的实例

I looked at this SO question and also at the documentation of the mutationObserver as well as all the examples mentioned here , but I do not see a reason, why my javascript wouldn't work with我查看了这个SO 问题,还查看了mutationObserver的文档以及此处提到的所有示例,但我没有看到原因,为什么我的 javascript 无法使用

target = document.getElementById('targetthis')

Where targetthis is a hard coded div around the dynamic overlay page.其中targetthis是围绕动态覆盖页面的硬编码 div。

However, if I target document.body instead it works fine, but now any changes to the DOM tree will toggle the overlay page.但是,如果我改为以document.body为目标,它工作正常,但现在对 DOM 树的任何更改都会切换覆盖页面。 In my example there is a selectInput which does but should not toggle the overlay page.在我的示例中,有一个selectInput可以但不应该切换叠加页面。 I'm also confused that the configuration of the mutationObserver only works with childList where I thought it would work with characterData (because the text of the overlay page changes dynamically).我还感到困惑的是, mutationObserver的配置仅适用于childList ,而我认为它适用于characterData (因为覆盖页面的文本动态变化)。

Another possibility I see, is to start the mutationObserver onclick of the graph and disconnect it once the overlay page is displayed.我看到的另一种可能性是启动图形的mutationObserver onclick 并在显示覆盖页面后断开它。 In this case I could use document.body as target.在这种情况下,我可以使用document.body作为目标。 But it seems overly complex for the task at hand.但对于手头的任务来说,它似乎过于复杂。

I would appreciate any help.我将不胜感激任何帮助。

Here is a reproducible example:这是一个可重现的例子:

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")
        )
      )
    })

  }

)

@VictorPerrier commented on Twitter to just use a modal window with modalDialog , which I totally did not have in mind. @VictorPerrier 对 Twitter 的评论只是将模式 window 与modalDialog一起使用,我完全没有想到。 I played around with it and it pretty much does what I want it to do without one line of javascript (see code below).我玩了一下它,它几乎完成了我想要它做的事情,而没有一行 javascript(见下面的代码)。

I will leave this question open, just in case someone has a "real" overlay page solution (which I do not need for my use case, but someone else might be looking for it).我将保留这个问题,以防万一有人有一个“真正的”覆盖页面解决方案(我的用例不需要它,但其他人可能正在寻找它)。 And of course, after spending some time with the mutationObserver API I would really like to know, why my approach is not working.当然,在使用mutationObserver API 花了一些时间之后,我真的很想知道,为什么我的方法不起作用。

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")
        )
      )
    })

  }

)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM