我如何使Shiny应用符合W3C标准?

4

我已经编写并优化了一个Shiny应用程序,现在我正在与我工作的组织的IT部门奋斗,以便将其发布在他们的服务器上。目前,根据W3C验证器的结果,他们声称该应用程序不符合W3C标准,这是正确的。

我一直在尝试解决以下问题,但没有成功:

  • <form class="well" role="complementary">“form”元素上“role”属性的值“complementary”无效。

  • <label class="control-label" id="foo-label" for="foo">“label”元素上“for”属性的值必须是非隐藏表单控件的ID。

即使是非常简单的Shiny应用程序也可能存在此类错误,例如:

# Reprex adapted from https://shiny.rstudio.com/gallery/tabsets.html
library(shiny)

# Define UI for random distribution app ----
ui <- fluidPage(
  
  # App title ----
  titlePanel("Tabsets"),
  
  # Sidebar layout with input and output definitions ----
  sidebarLayout(
    
    # Sidebar panel for inputs ----
    sidebarPanel(
      
      # Input: Select the random distribution type ----
      radioButtons("dist", "Distribution type:",
                   c("Normal" = "norm",
                     "Uniform" = "unif",
                     "Log-normal" = "lnorm",
                     "Exponential" = "exp")),
      
      # br() element to introduce extra vertical spacing ----
      br(),
      
      # Input: Slider for the number of observations to generate ----
      sliderInput("n",
                  "Number of observations:",
                  value = 500,
                  min = 1,
                  max = 1000)
      
    ),
    
    # Main panel for displaying outputs ----
    mainPanel(
      
      # Output: Tabset w/ plot, summary, and table ----
      tabsetPanel(type = "tabs",
                  tabPanel("Plot", plotOutput("plot")),
                  tabPanel("Summary", verbatimTextOutput("summary")),
                  tabPanel("Table", tableOutput("table"))
      )
      
    )
  )
)

# Define server logic for random distribution app ----
server <- function(input, output) {
  
  # Reactive expression to generate the requested distribution ----
  d <- reactive({
    dist <- switch(input$dist,
                   norm = rnorm,
                   unif = runif,
                   lnorm = rlnorm,
                   exp = rexp,
                   rnorm)
    
    dist(input$n)
  })
  
  # Generate a plot of the data ----
  output$plot <- renderPlot({
    dist <- input$dist
    n <- input$n
    
    hist(d(),
         main = paste("r", dist, "(", n, ")", sep = ""),
         col = "#75AADB", border = "white")
  })
}

# Create Shiny app ----
shinyApp(ui, server)

第二个错误似乎只涉及单选按钮,而第一个错误似乎影响到我从网站上找到和使用 W3C 验证器测试过的所有闪亮应用程序。 为了完整起见,我还报告了 reprex 中生成的 HTML 代码:
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <script type="application/shiny-singletons"></script>
  <script type="application/html-dependencies">jquery[3.6.0];shiny-css[1.7.1];shiny-javascript[1.7.1];ionrangeslider-javascript[2.3.1];strftime[0.9.2];ionrangeslider-css[2.3.1];bootstrap[3.4.1]</script>
<script src="shared/jquery.min.js"></script>
<link href="shared/shiny.min.css" rel="stylesheet" />
<script src="shared/shiny.min.js"></script>
<script src="shared/ionrangeslider/js/ion.rangeSlider.min.js"></script>
<script src="shared/strftime/strftime-min.js"></script>
<link href="shared/ionrangeslider/css/ion.rangeSlider.css" rel="stylesheet" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="shared/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<link href="shared/bootstrap/accessibility/css/bootstrap-accessibility.min.css" rel="stylesheet" />
<script src="shared/bootstrap/js/bootstrap.min.js"></script>
<script src="shared/bootstrap/accessibility/js/bootstrap-accessibility.min.js"></script>  <title>Tabsets</title>
</head>
<body>
  <div class="container-fluid">
    <h2>Tabsets</h2>
    <div class="row">
      <div class="col-sm-4">
        <form class="well" role="complementary">
          <div id="dist" class="form-group shiny-input-radiogroup shiny-input-container" role="radiogroup" aria-labelledby="dist-label">
            <label class="control-label" id="dist-label" for="dist">Distribution type:</label>
            <div class="shiny-options-group">
              <div class="radio">
                <label>
                  <input type="radio" name="dist" value="norm" checked="checked"/>
                  <span>Normal</span>
                </label>
              </div>
              <div class="radio">
                <label>
                  <input type="radio" name="dist" value="unif"/>
                  <span>Uniform</span>
                </label>
              </div>
              <div class="radio">
                <label>
                  <input type="radio" name="dist" value="lnorm"/>
                  <span>Log-normal</span>
                </label>
              </div>
              <div class="radio">
                <label>
                  <input type="radio" name="dist" value="exp"/>
                  <span>Exponential</span>
                </label>
              </div>
            </div>
          </div>
          <br/>
          <div class="form-group shiny-input-container">
            <label class="control-label" id="n-label" for="n">Number of observations:</label>
            <input class="js-range-slider" id="n" data-skin="shiny" data-min="1" data-max="1000" data-from="500" data-step="1" data-grid="true" data-grid-num="9.99" data-grid-snap="false" data-prettify-separator="," data-prettify-enabled="true" data-keyboard="true" data-data-type="number"/>
          </div>
        </form>
      </div>
      <div class="col-sm-8" role="main">
        <div class="tabbable">
          <ul class="nav nav-tabs" data-tabsetid="9747">
            <li class="active">
              <a href="#tab-9747-1" data-toggle="tab" data-bs-toggle="tab" data-value="Plot">Plot</a>
            </li>
            <li>
              <a href="#tab-9747-2" data-toggle="tab" data-bs-toggle="tab" data-value="Summary">Summary</a>
            </li>
            <li>
              <a href="#tab-9747-3" data-toggle="tab" data-bs-toggle="tab" data-value="Table">Table</a>
            </li>
          </ul>
          <div class="tab-content" data-tabsetid="9747">
            <div class="tab-pane active" data-value="Plot" id="tab-9747-1">
              <div id="plot" class="shiny-plot-output" style="width:100%;height:400px;"></div>
            </div>
            <div class="tab-pane" data-value="Summary" id="tab-9747-2">
              <pre class="shiny-text-output noplaceholder" id="summary"></pre>
            </div>
            <div class="tab-pane" data-value="Table" id="tab-9747-3">
              <div id="table" class="shiny-html-output"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>
</html>

您有什么建议吗? 谢谢!


此消息也发布在RStudio社区:抱歉跨帖。


也许你可以查看这个关于修改HTML元素的教程:https://youtu.be/jVh05izjHMI - MrFlick
2
我可能有点天真,但除了在这种情况下需要完成它之外,一般来说符合W3C标准有什么优势? - r2evans
我对规则的详细理由一无所知,但是可能有人仔细考虑过它们吧?https://www.w3.org/TR/html-aria/说`form`的唯一允许角色是`Roles: search, none or presentation`。 - Ben Bolker
1
这篇文章提出了一个论点,即追踪每个w3c违规行为的努力可能不值得。只要违规行为列表不是特别长,也许你可以自己测试跨浏览器的兼容性?但不确定这是否能说服你的IT同事。 - Till
1个回答

3
以下仅涉及您提到的第一个错误(由于@BenBolkers评论很清楚),但希望它能指引您使用正确的工具。
我会使用htmltools::tagQuery来进行必要的修改 - 请检查以下内容:
# Reprex adapted from https://shiny.rstudio.com/gallery/tabsets.html
library(shiny)
library(htmltools)

# Define UI for random distribution app ----
ui <- fluidPage(
  
  # App title ----
  titlePanel("Tabsets"),
  
  # Sidebar layout with input and output definitions ----
  sidebarLayout(
    
    # Sidebar panel for inputs ----
    {querySidebarPanel <- tagQuery(sidebarPanel(
      # Input: Select the random distribution type ----
      radioButtons("dist", "Distribution type:",
                   c("Normal" = "norm",
                     "Uniform" = "unif",
                     "Log-normal" = "lnorm",
                     "Exponential" = "exp")),
      
      # br() element to introduce extra vertical spacing ----
      br(),
      
      # Input: Slider for the number of observations to generate ----
      sliderInput("n",
                  "Number of observations:",
                  value = 500,
                  min = 1,
                  max = 1000)
    ))
    querySidebarPanel$find(".well")$removeAttrs("role")$addAttrs("role" = "none")$allTags()},
    
    # Main panel for displaying outputs ----
    mainPanel(
      
      # Output: Tabset w/ plot, summary, and table ----
      tabsetPanel(type = "tabs",
                  tabPanel("Plot", plotOutput("plot")),
                  tabPanel("Summary", verbatimTextOutput("summary")),
                  tabPanel("Table", tableOutput("table"))
      )
      
    )
  )
)

# Define server logic for random distribution app ----
server <- function(input, output) {
  
  # Reactive expression to generate the requested distribution ----
  d <- reactive({
    dist <- switch(input$dist,
                   norm = rnorm,
                   unif = runif,
                   lnorm = rlnorm,
                   exp = rexp,
                   rnorm)
    
    dist(input$n)
  })
  
  # Generate a plot of the data ----
  output$plot <- renderPlot({
    dist <- input$dist
    n <- input$n
    
    hist(d(),
         main = paste("r", dist, "(", n, ")", sep = ""),
         col = "#75AADB", border = "white")
  })
}

# Create Shiny app ----
shinyApp(ui, server)

请查看"Outstanding User Interfaces with Shiny"相关章节


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接