You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R Shiny中自定义CSS样式单选按钮无法提交值的问题

修复Shiny自定义单选按钮无法提交值的问题

问题根源

你当前用纯HTML的<input type="radio">创建单选按钮,Shiny的input对象无法自动捕获这类纯HTML元素的输入状态,导致提交时返回NULL。Shiny仅能追踪自身原生输入组件(如radioButtons、textInput等)的数值变化。


解决方案一:改用Shiny原生radioButtons+自定义CSS(推荐)

这个方法既保留Shiny的输入绑定功能,又能实现你想要的答题卡样式,稳定性更高。

修改后的完整代码:

library(shiny)

# Define UI
ui <- fluidPage(
  # 自定义CSS适配Shiny原生radioButtons结构
  tags$head(
    tags$style(HTML("
      /* 隐藏原生radio按钮 */
      .scantron-radio .shiny-input-radio input[type='radio'] {
        opacity: 0;
        position: absolute;
      }
      /* 样式化label为圆形按钮 */
      .scantron-radio .shiny-input-radio label {
        display: inline-block;
        width: 20px;
        height: 20px;
        line-height: 20px;
        text-align: center;
        margin-right: 10px;
        border: 2px solid #ccc;
        border-radius: 30%;
        cursor: pointer;
        font-size: 12px;
        margin-bottom: 15px; /* 增加题与题之间的间距 */
      }
      /* 选中状态样式 */
      .scantron-radio .shiny-input-radio input[type='radio']:checked + label {
        background-color: #007bff;
        color: white;
        border-color: #007bff;
      }
      /* 调整radio选项的排列为横向 */
      .scantron-radio .shiny-input-radio .radio {
        display: inline-block;
        margin-right: 5px;
      }
    "))
  ),

  titlePanel("This is your test"),

  fluidRow(
    column(12,
           # 启用文本输入(可选)
           textInput("firstname", "Name:"),
           textInput("email", "Email:"),
           textInput("date", "Date:"),
           textInput("testID", "Test ID:"),

           # 问题1:用Shiny原生radioButtons,添加自定义类
           div(class = "scantron-radio",
               radioButtons(inputId = "question1", 
                           label = "Q1:", 
                           choices = c("A", "B", "C", "D", "E"),
                           inline = TRUE)
           ),

           # 问题2
           div(class = "scantron-radio",
               radioButtons(inputId = "question2", 
                           label = "Q2:", 
                           choices = c("F", "G", "H", "J", "K"),
                           inline = TRUE)
           ),

           # 问题3
           div(class = "scantron-radio",
               radioButtons(inputId = "question3", 
                           label = "Q3:", 
                           choices = c("A", "B", "C", "D", "E"),
                           inline = TRUE)
           ),

           actionButton("submit", "Submit")
    )
  ),
  fluidRow(
    column(12,
           div(class = "centered-content",
               textOutput("confirmation")
           )
    )
  )
)

# Server逻辑
server <- function(input, output) {
  observeEvent(input$submit, {
    # 打印测试值
    print(input$question1)
    print(input$question2)
    print(input$question3)

    # 收集响应数据
    response <- data.frame(
      Name = ifelse(nchar(input$firstname) > 0, input$firstname, NA),
      Email = ifelse(nchar(input$email) > 0, input$email, NA),
      Date = ifelse(nchar(input$date) > 0, input$date, NA),
      TestID = ifelse(nchar(input$testID) > 0, input$testID, NA),
      question1 = input$question1,
      question2 = input$question2,
      question3 = input$question3,
      stringsAsFactors = FALSE
    )

    # 处理CSV写入
    file_exists <- file.exists("responses.csv")
    write.table(response, "responses.csv", sep = ",", append = file_exists,
                col.names = !file_exists, row.names = FALSE, quote = TRUE)
    
    # 显示确认信息
    output$confirmation <- renderText("Thank you for your submission!")
  })
}

shinyApp(ui, server)

改动说明:

  • 替换纯HTML单选按钮为Shiny原生radioButtons,设置inline = TRUE让选项横向排列。
  • 调整CSS选择器,适配Shiny原生radio组件的DOM结构(.shiny-input-radio类)。
  • 合并了两个observeEvent(input$submit),避免重复执行逻辑。
  • 简化了响应数据的收集逻辑,Shiny原生组件会直接返回选中值或NULL,无需额外判断空字符串。

解决方案二:纯HTML+JavaScript同步值(适合特殊场景)

如果一定要保留纯HTML的radio按钮结构,需要添加JavaScript代码将选中值同步到Shiny的input对象中:

在tags$head里添加以下JS代码:

tags$script(HTML("
  // 监听所有radio按钮的变化
  document.addEventListener('change', function(e) {
    if (e.target.type === 'radio') {
      var inputName = e.target.name;
      var inputValue = e.target.value;
      // 将值同步到Shiny input
      Shiny.setInputValue(inputName, inputValue);
    }
  });
"))

此方法需要维护JS与HTML的对应关系,稳定性不如原生组件,仅推荐在特殊需求下使用。

内容的提问来源于stack exchange,提问作者Gavin Green

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 21:23:14