htmlcssshinyshinyjsshinyscreenshot

How to Exclude radioGroupButtons in shinyscreenshot


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.

The screenshot looks like this: enter image description here

It should look like this: enter image description here


Solution

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

    enter image description here