在 Shiny 应用程序中使用 `renderUI` 时附加 javascript 侦听器
2022-06-17
289
我最终试图捕获用户在显示一系列直方图后单击它们所花费的时间。但是,在下面的示例应用中,在加载应用时会出现 javascript 错误:
Uncaught TypeError: Cannot set properties of null (setting 'onclick') at HTMLDocument. ((index):31:30) at e (jquery.min.js:2:30038) at t (jquery.min.js:2:30340)
这可能是因为
document.getElementById("img")
在加载应用时找不到
img
,但我不知道如何解决。
当直方图显示在
renderUI
之外时,我可以让它工作,但我需要从服务器动态更改直方图,因此我需要它与渲染的 UI 一起工作。
shinyApp(
ui = fluidPage(
tags$script('
// ------ javascript code ------
$(document).ready(function(){
// function to set Shiny input value to current time:
const clockEvent = function(inputName){Shiny.setInputValue(inputName, new Date().getTime())}
// trigger when the value of output id "img" changes:
$(document).on("shiny:value",
function(event){
if (event.target.id === "img") {clockEvent("displayed_at")}
}
)
// trigger when the image, after being sent or refreshed, is clicked:
document.getElementById("img")
.onclick = function(){clockEvent("reacted_at")}
})
// ------------------------------
'),
sidebarLayout(
sidebarPanel(
actionButton(inputId="render_dynamic", label= "Create Dynamic UI")
),
mainPanel(
uiOutput("dynamic")
)
)
),
server = function(input, output) {
output$img <- renderImage({
outfile <- tempfile(fileext='.png')
png(outfile, width=400, height=400)
hist(rnorm(100))
dev.off()
list(src = outfile,
contentType = "image/jpeg")
},
deleteFile = FALSE)
output$reaction_time <- renderPrint(paste('reaction time (ms)', input$reacted_at - input$displayed_at))
output$dynamic <- renderUI({
req(input$render_dynamic > 0)
div(id = 'image_container',
imageOutput("img", click = "photo_click"),
textOutput("reaction_time"))
})
}
)
2个回答
这里有一种避免使用
renderUI
并使用
bindEvent
的方法:
library(shiny)
ui = fluidPage(
tags$script('
// ------ javascript code ------
$(document).ready(function(){
// function to set Shiny input value to current time:
const clockEvent = function(inputName){Shiny.setInputValue(inputName, new Date().getTime())}
// trigger when the value of output id "img" changes:
$(document).on("shiny:value",
function(event){
if (event.target.id === "img") {clockEvent("displayed_at")}
}
)
// trigger when the image, after being sent or refreshed, is clicked:
document.getElementById("img")
.onclick = function(){clockEvent("reacted_at")}
})
// ------------------------------
'),
sidebarLayout(
sidebarPanel(
actionButton(inputId="render_dynamic", label= "Create Dynamic UI")
),
mainPanel(
imageOutput("img"),
textOutput("reaction_time")
)
)
)
server = function(input, output, session) {
output$img <- renderImage({
outfile <- tempfile(fileext='.png')
png(outfile, width=400, height=400)
hist(rnorm(100))
dev.off()
list(src = outfile,
contentType = "image/jpeg")
}, deleteFile = FALSE) |> bindEvent(input$render_dynamic)
output$reaction_time <- renderPrint({
paste('reaction time (ms)', input$reacted_at - input$displayed_at)
}) |> bindEvent(input$reacted_at)
}
shinyApp(ui, server)
我不知道是否有充分的理由让您将图输出为图像并通过
renderImage
显示它,而不是直接使用
renderPlot
- 但这里是
renderPlot
版本:
library(shiny)
ui = fluidPage(
tags$script('
// ------ javascript code ------
$(document).ready(function(){
// function to set Shiny input value to current time:
const clockEvent = function(inputName){Shiny.setInputValue(inputName, new Date().getTime())}
// trigger when the value of output id "img" changes:
$(document).on("shiny:value",
function(event){
if (event.target.id === "img") {clockEvent("displayed_at")}
}
)
// trigger when the image, after being sent or refreshed, is clicked:
document.getElementById("img")
.onclick = function(){clockEvent("reacted_at")}
})
// ------------------------------
'),
sidebarLayout(
sidebarPanel(
actionButton(inputId="render_dynamic", label= "Create Dynamic UI")
),
mainPanel(
plotOutput("img"),
textOutput("reaction_time")
)
)
)
server = function(input, output, session) {
output$img <- renderPlot({
hist(rnorm(100))
}) |> bindEvent(input$render_dynamic)
output$reaction_time <- renderPrint({
paste('reaction time (ms)', input$reacted_at - input$displayed_at)
}) |> bindEvent(input$reacted_at)
}
shinyApp(ui, server)
PS:如果您仍然对如何解决
renderUI
问题感兴趣,请查看以下
GitHub 上的帖子
。
ismirsehregal
2022-06-17
从客户端的角度来看,在
renderUI
另一个元素之前,文档似乎已完全加载。因此 JQuery
$(document).ready(...)
表示可以继续尝试将事件附加到尚不存在的元素。
避免
renderUI
的选项已给出。如果您不想要“占位符”空白,您可以在渲染时将图像高度设置为零:
ui <- fluidPage(
## ...
imageOutput("img", click = "photo_click",
height = 0
)
## ...
2022-06-17