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

Shiny应用更新selectizeInput后rintrojs无法定位元素问题

问题原因与解决方案

问题描述

我开发了一个Shiny应用,可根据另一个选择器的值为selectizeInput()生成选项——具体是获取所选变量的唯一值作为选项。UI中用selectizeInput()初始化选择器,通过observe({})监听输入变化,调用updateSelectizeInput()更新选项,这部分功能正常。

但使用rintrojs时出现问题:首次运行应用时能定位到选项选择器,但更新选项后,再次尝试定位该元素却无法找到。具体表现为:教程第一步可正常指向元素,选择两个对比组选项后,切换Group 1为"y",此时对比组选项已更新为D、E、F,但教程再次指向对比组选择器时,tooltip会跑到左上角,无法正确定位元素。始终使用同一ID选择对比组选择器,首次可用但第二次失效。

复现代码:

library(shiny)
library(rintrojs)
# Define UI for application that draws a histogram
ui <- fluidPage(
  
  # Application title
  titlePanel("IntroJS Problem"),
  
  # Sidebar with a slider input for number of bins 
  sidebarLayout(
    sidebarPanel(
      actionButton("browser", "browser"),
      introjsUI(),
      actionButton("btn","Interactive Tutorial"),
      selectizeInput("grp1", 
                     "Grouping Variable", 
                     choices = c("x", "y"), 
                     selected="x"),
      selectizeInput("compgroup", "Compare Groups (Choose 2)", choices="", multiple=TRUE, options=list(maxItems=2))
    ),
    
    
    # Show a plot of the generated distribution
    mainPanel(
    )
  )
)

# Define server logic required to draw a histogram
server <- function(input, output, session) {
  library(rintrojs)
  observeEvent(input$browser,{
    browser()
  })

  observe({
    g1 <- input$grp1
    if(g1 == "x"){
      chc <- c("A", "B", "C")
    }
    if(g1 == "y"){
      chc <- c("D", "E", "F")
    }
    updateSelectizeInput(session, "compgroup", choices = chc)
  })
  
  
  steps <- reactive({ 
    data.frame( 
      element = c("#grp1 + .selectize-control", 
                  "#compgroup + .selectize-control", 
                  "#grp1 + .selectize-control", 
                  "#compgroup + .selectize-control"),
      intro = c("Choose x for Group 1",
                "Choose two values for Comparing Groups", 
                "Choose y for Group 1", 
                "Choose two values for Comparing Groups"),
      position = "right")
  })
  observeEvent(input$btn, introjs(session, options = list(steps=steps())))
  
}
# Run the application 
shinyApp(ui = ui, server = server)

原因分析

  • DOM元素被替换:updateSelectizeInput()会完全重新渲染selectize组件的DOM结构,原来的.selectize-control元素会被移除并生成新的元素。introjs在教程初始化时会缓存目标元素的引用,当元素被替换后,旧的引用失效,导致无法定位新元素。
  • 选择器不稳定:使用相邻兄弟选择器#compgroup + .selectize-control依赖固定的DOM结构关系,当selectize组件重新渲染时,这种关系可能被破坏,导致选择器无法匹配到新生成的元素。

解决方案

改用基于data-input属性的稳定选择器,selectize生成的控制容器会自带data-input属性,值为原始selectizeInput的ID,无论DOM如何重新渲染,这个属性都会保持不变。

修改步骤中的element选择器:

steps <- reactive({ 
  data.frame( 
    element = c('div.selectize-control[data-input="grp1"]', 
                'div.selectize-control[data-input="compgroup"]', 
                'div.selectize-control[data-input="grp1"]', 
                'div.selectize-control[data-input="compgroup"]'),
    intro = c("Choose x for Group 1",
              "Choose two values for Comparing Groups", 
              "Choose y for Group 1", 
              "Choose two values for Comparing Groups"),
    position = "right")
})

修改后的完整代码:

library(shiny)
library(rintrojs)
# Define UI for application that draws a histogram
ui <- fluidPage(
  
  # Application title
  titlePanel("IntroJS Problem"),
  
  # Sidebar with a slider input for number of bins 
  sidebarLayout(
    sidebarPanel(
      actionButton("browser", "browser"),
      introjsUI(),
      actionButton("btn","Interactive Tutorial"),
      selectizeInput("grp1", 
                     "Grouping Variable", 
                     choices = c("x", "y"), 
                     selected="x"),
      selectizeInput("compgroup", "Compare Groups (Choose 2)", choices="", multiple=TRUE, options=list(maxItems=2))
    ),
    
    
    # Show a plot of the generated distribution
    mainPanel(
    )
  )
)

# Define server logic required to draw a histogram
server <- function(input, output, session) {
  library(rintrojs)
  observeEvent(input$browser,{
    browser()
  })

  observe({
    g1 <- input$grp1
    if(g1 == "x"){
      chc <- c("A", "B", "C")
    }
    if(g1 == "y"){
      chc <- c("D", "E", "F")
    }
    updateSelectizeInput(session, "compgroup", choices = chc)
  })
  
  
  steps <- reactive({ 
    data.frame( 
      element = c('div.selectize-control[data-input="grp1"]', 
                  'div.selectize-control[data-input="compgroup"]', 
                  'div.selectize-control[data-input="grp1"]', 
                  'div.selectize-control[data-input="compgroup"]'),
      intro = c("Choose x for Group 1",
                "Choose two values for Comparing Groups", 
                "Choose y for Group 1", 
                "Choose two values for Comparing Groups"),
      position = "right")
  })
  observeEvent(input$btn, introjs(session, options = list(steps=steps())))
  
}
# Run the application 
shinyApp(ui = ui, server = server)

这样修改后,无论selectizeInput被更新多少次,introjs都能通过data-input属性精准定位到目标元素,解决tooltip错位的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 02:45:20