I'm building a Shiny app using shinyscreenshot
.
My app includes a "Score" card with a list of radioGroupButtons that users can select from.
The radioGroupButtons are not visible in the app, however, when I take a screenshot using shinyscreenshot
, the radioGroupButtons occur and overlap with the numeric values, making the screenshot messy.
I want to hide the radioGroupButtons when taking the screenshot. I tried using shinyjs to toggle the visibility of the buttons, but it’s not working as expected.
Here’s a minimal reproducible example that demonstrates my setup and approach:
library(shiny)
library(bs4Dash)
library(shinyWidgets)
library(shinyjs)
library(shinyscreenshot)
my_ids <- c("region_1", "region_2")
my_labels <- c("A", "B")
ui <- bs4DashPage(
title = "My Example",
header = bs4DashNavbar(),
sidebar = bs4DashSidebar(disable = TRUE),
body = bs4DashBody(
useShinyjs(),
# CSS for radio button styles and hiding elements
tags$style(HTML("
.btn-zero {background-color: white; color: black;}
.btn-zero.active {background-color: #a2ff45; color: black;}
.btn-one {background-color: white; color: black;}
.btn-one.active {background-color: #a2ff45; color: black;}
.btn-two {background-color: white; color: black;}
.btn-two.active {background-color: #a2ff45; color: black;}
.btn-three {background-color: white; color: black;}
.btn-three.active {background-color: #a2ff45; color: black;}
.region-label { line-height: 2.5; }
/* Hide elements during screenshot */
.hide-for-screenshot { display: none !important; }
")),
bs4Card(
title = "My Score",
width = 6,
style = "height: 200px; overflow: auto;",
tags$style(HTML("
.region-label { margin-top: -10px; }
")),
# Render radio buttons for each region
lapply(seq_along(my_ids), function(i) {
fluidRow(
column(6, h4(class = "region-label", paste(i-1, my_labels[i]))),
column(6, div(
style = "display: inline-block;",
shinyWidgets::radioGroupButtons(
inputId = my_ids[i],
label = NULL,
choices = 0:3,
selected = 0,
checkIcon = list(yes = icon("check")),
status = c("zero", "one", "two", "three"),
size = 'xs'
)
))
)
})
),
# Screenshot Button
actionButton("take_screenshot", "Take Screenshot", style = "margin-top: 10px;")
)
)
server <- function(input, output, session) {
observeEvent(input$take_screenshot, {
# Temporarily hide radio buttons
shinyjs::addClass(selector = ".radioGroupButtons", class = "hide-for-screenshot")
# Take screenshot and remove the hide class afterward
shinyscreenshot::screenshot()
# Show radio buttons again
shinyjs::removeClass(selector = ".radioGroupButtons", class = "hide-for-screenshot")
})
}
shinyApp(ui, server)
What I've Tried
Custom CSS: I added a .hide-for-screenshot
CSS class with display: none !important
; to hide elements. The class is applied right before taking the screenshot and removed afterward.
Using shinyjs
: I'm using shinyjs::addClass
and shinyjs::removeClass
to toggle the .hide-for-screenshot
class on the radioGroupButtons
.
Problem
Despite adding the hide-for-screenshot
class and hiding the radioGroupButtons
before taking the screenshot, they still appear in the screenshot. I suspect that shinyscreenshot
might not be respecting the temporary CSS changes or that the delay isn’t enough to apply the styling.
Question
How can I ensure that the radioGroupButtons
are completely hidden when shinyscreenshot
captures the screenshot?
Any suggestions or alternative approaches would be greatly appreciated! Thank you.
There are a couple of points to make about your example - first you're trying to hide the entire radioGroupButton
object instead of just the <input>
element that is causing the problem and you were also using an invalid class - they are in radio-group-buttons
not radioGroupButtons
. Further, the events inside an observeEvent
are only ever triggered at the end, so your addClass
and removeClass
cancel each other out. You therefore need three observeEvents
, one to hide them, one to take the screenshot and then another to unhide them. Note that this is currently hiding all the <input>
elements which is unlikely to be what you want in your real use case, but I haven't been able to figure out what selector to use.
server <- function(input, output, session) {
observeEvent(input$take_screenshot, {
shinyjs::addClass(selector = "input", class = "hide-for-screenshot")
if (is.null(input$take_screenshot2)){
new_value <- 1
} else {
new_value <- input$take_screenshot2 + 1
}
shinyjs::runjs(paste0("Shiny.setInputValue('take_screenshot2',", new_value, ");"))
})
observeEvent(input$take_screenshot2, {
shinyscreenshot::screenshot()
if (is.null(input$take_screenshot3)){
new_value <- 1
} else {
new_value <- input$take_screenshot3 + 1
}
shinyjs::runjs(paste0("Shiny.setInputValue('take_screenshot3',", new_value, ");"))
})
observeEvent(input$take_screenshot3, {
shinyjs::removeClass(selector = "input", class = "hide-for-screenshot")
})
}