[英]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.