为何JavaScript可修改Shiny预定义UI元素,却无法作用于insertUI新增元素?
问题:JavaScript控制Shiny动态新增元素属性无效的原因
我想用JavaScript控制一类UI元素的属性,但该操作仅对应用初始化时已存在的元素有效,对通过insertUI后续添加的元素无效。
示例中,每点击一次按钮就会插入一个新的彩色盒子。UI里定义的<script>标签会根据输入选择更改颜色,但仅对初始div生效。请问为何该脚本无法作用于新增的div?
代码示例
library(shiny) library(magrittr) # 创建带指定ID的彩色盒子div insertBox = function(Id) { boxDiv = tags$div(id = paste0("color_", Id), style = "height:30px; width: 30px; background-color: blue;" ) return(boxDiv) } # 生成修改指定前缀ID元素属性的JS脚本 jsChangeDivAttr = function(event, Id, attribute) { script = tags$script(HTML(paste0("$(document).on('shiny:inputchanged', function(event) { if(event.name == '", event, "') { document.querySelector('[id^=\"", Id, "\"]').style.setProperty('", attribute, "', event.value); } });"))) return(script) } # UI部分 ui = fluidPage( # 引入修改div属性的脚本 tags$head( jsChangeDivAttr(event = "boxColor", Id = "color", attribute = "background-color") ), # 盒子颜色选择器 selectizeInput("boxColor", choices = c("blue", "red"), label = "盒子颜色"), # 插入新盒子的按钮 actionButton("insertBoxButton", label = "插入盒子"), # 初始加载的第一个盒子 insertBox(0) ) # Server部分 server = function(input, output, session) { # 用于生成唯一ID的计数器 idCount = reactiveVal(0) # 点击按钮时在最后一个盒子后插入新盒子 observe({ # 当前要插入到哪个盒子之后 Id = idCount() # 更新计数器生成新ID idCount(idCount() + 1) # 执行插入操作 insertUI(selector = paste0("#color_", Id), where = "afterEnd", ui = insertBox(idCount())) }) %>% bindEvent(input$insertBoxButton) } shinyApp(ui, server)
2023-08-30 更新
根据@Stéphane Laurent的修改方案,脚本已可正常作用于所有动态新增元素:
script = tags$script(HTML(paste0("$(document).on('shiny:inputchanged', function(event) { if(event.name == '", event, "') { var divs = document.querySelectorAll('[id^=\"", Id, "\"]'); for (var i = 0; i < divs.length; i++) { divs[i].style.setProperty('", attribute, "', event.value); } } });")))
问题根源
原脚本使用document.querySelector()方法,该方法仅返回匹配选择器的第一个元素。初始化时页面只有一个符合条件的div,所以能正常生效;但新增元素后,该方法依然只选中第一个初始div,后续动态添加的元素完全不会被处理。
修改后的脚本改用document.querySelectorAll(),它会返回所有匹配选择器的元素集合,再通过循环遍历每个元素逐个修改属性,这样不管是初始元素还是后续新增的元素,都能被正确选中并更新样式。
内容的提问来源于stack exchange,提问作者tomato_soup
相关产品推荐
相关产品推荐

