4

Say for example, I have a very basic shiny application where you selectize a letter:

library(shiny)

ui <- fluidPage(
  fluidRow(
    column(3, selectizeInput("Letters", "Choose a Letter",
      choices = LETTERS, multiple = TRUE)
    )
  )
)

server <- function(input, output) {}
shinyApp(ui = ui, server = server)

Basic UI

I need this shiny application to be 508 compliant/accessible. If I run it through a web accessibility test like WAVE, I get an error about a Missing form label for this selectizeInput section.

Missing Form Label

I believe this is because of a random div that is added that does not have the same ID that the "Choose a letter" label references. The label is for Letters-selectized and the div that is showing an accessibility error has the ID Letters. Is there any way to fix this error? I know you can add render to the options for selectizeInput but I havent found a way to correct this particular div.

Based on comment from ismirsehregal If I try to add an additional label with tagQuery for the ID "Letters", I get a duplicate form label error. I believe this is because even though I specify "for" = "Letters" the output is still for = "Letters-selectized"

library(shiny)
library(htmltools)

ui <- fluidPage(
  {queryfluidRow <- tagQuery(fluidRow(
    column(3, selectizeInput("Letters", "Choose a Letter",
      choices = LETTERS, multiple = TRUE))
    )
  )
  queryfluidRow$find("#Letters")$parent()$prepend(tag("label hidden",list("class" = "control-label","for" = "Letters", "Custom Tag")))$allTags()}
)

server <- function(input, output) {}
shinyApp(ui = ui, server = server)

The added tagQuery label html looks like the following and produces additional errors. I probably do not understand the underlying html relationship between labels and form control selection. However, I would have assumed setting "for" = "Letters" in tagQuery would have assigned the label to the correct ID:

<label hidden="" class="control-label" for="Letters-selectized">Custom Tag</label>
<select id="Letters" class="form-control selectized shiny-bound-input" multiple="multiple" tabindex="-1" style="display: none;"></select>
jasbner
  • 2,253
  • 12
  • 24
  • Maybe you can adjust things via htmltools::tagQuery. Please see [this related answer](https://stackoverflow.com/a/71348276/9841389). – ismirsehregal Apr 26 '23 at 13:34
  • @ismirsehregal added an attempt of adjusting using tagQuery but the control-label is still not linking with the missing form control – jasbner Apr 26 '23 at 15:44
  • The generated code at the end of your question isn't right. I'm not a R/Shiny developer, but from an HTML accessibility perspective, the ID referenced in the ` – slugolicious Apr 26 '23 at 19:52
  • @slugolicious yes I believe that's the problem i'm trying to solve. In the R code i define "for" = "Letters" with `tag("label hidden",list("class" = "control-label","for" = "Letters", "Custom Tag"))` but something in the backend transforms that into for= "Letters-selectized" and I don't understand why that's happening. – jasbner Apr 26 '23 at 20:13

1 Answers1

1

{shinyjs} allows you i. a. to execute javascript code client-side. You can thus query your label element via its id and set its for attribute like so:

library(shiny)
library(shinyjs)

ui <- fluidPage(
  useShinyjs(),  # include shinyjs
  fluidRow(
    column(3, selectizeInput("Letters", "Choose a Letter",
      choices = LETTERS, multiple = TRUE)
    )
  )
)

server <- function(input, output, session) {
  runjs('document.getElementById("Letters-label").setAttribute("for", "letters")')
}
shinyApp(ui = ui, server = server)

BTW, apart from above library, Shiny Awesome lists many more, well, awesome extensions for Shiny apps. Kudos to Nan Xiao.

I_O
  • 4,983
  • 2
  • 2
  • 15
  • 1
    Thanks for this, I think your proposed solution creates duplicate form label errors. However, i believe this is fixed if I instead add an aria-labelledby attribute to Letters `runjs('document.getElementById("Letters").setAttribute("aria-labelledby", "Letters-selectized")')` – jasbner May 01 '23 at 00:57