我是一个 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")
)
)
})
}
)