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

swipeR组件内select2无法正常工作,外部正常的技术问题求助

在Shiny中解决select2嵌入swipeR组件失效的问题

问题描述

在R语言Shiny框架开发时,将select2插件嵌入swipeR组件内部时无法正常工作,但放在组件外部可正常运行。复现代码如下:

library(shiny)

ui <- fluidPage(
  
  HTML(
    '<head>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.1.0-beta.1/css/select2.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.1.0-beta.1/js/select2.min.js"></script>
    </head>'
  ),
  
  swipeR::swipeR(
    id = "carouselSwotContainer1",
    initialSlide = 1,
    bulletsSize = NULL,
    loop = FALSE,
    width = "398px",
    height = "288px",
    effect = "cube",
    cubeEffect = list(shadowScale = .9),
    speed = 1000,
    rewind = TRUE,
    wrapper = swipeR::swipeRwrapper(
      
      div(
        
        class = "swiper-no-swiping",
        
        HTML(
          "<!-- select2 YOUR COMPANY ADVANTAGE-->
          <div style='grid-column: span 6;'>

    <div id='selectTwoDivA1' class='selectTwoDivClassA1' style='margin-top: 10px;'>

        <div class='inputLabelClass' style='position: relative; top: -5px;'>
            <i class='fas fa-disease' style='color: #4bf542;'></i> select2 Test (problem)
        </div>

        <select id='selectjquery1' multiple='multiple'></select>

    </div>

    <span id='placeholderTextInput1' style='display: none;'>TEXTO 1</span>
    <span id='selectResultText1' style='display: none;'>TEXTO 2</span>

    <style>
        #selectTwoDivA1 .select2-selection--multiple {
            border: 2px solid #4bf542 !important;
        }

        .selectTwoDivClassA1 .select2-selection--multiple {
            background-color: unset !important;
            color: #333333;
            width: 250px !important;
            height: 70px !important;
        }
    </style>

    <script>
        $(document).ready(function() {

            function updateSelectAdvantageA1(placeholderText1, textTooShort1) {
                $('#selectjquery1').select2({
                    placeholder: placeholderText1,
                    allowClear: true,
                    closeOnSelect: true,
                    minimumInputLength: 3,
                    maximumInputLength: 20,
                    maximumSelectionLength: 3,
                    tags: true,
                    language: {
                        maximumSelected: function (e) {
                            let text = 'Você pode atribuir apenas ' + e.maximum + ' vantagens.';
                            e.maximum != 1 && (text += 's.');
                            return text;
                        },
                        inputTooShort: function() {
                            return textTooShort1; // Corrigido aqui
                        }
                    }
                });
            }

            function updateSelectAdvantageB1() {
                let placeholderText1 = $('#placeholderTextInput1').text();
                let textTooShort1 = $('#selectResultText1').text();
                updateSelectAdvantageA1(placeholderText1, textTooShort1);
            }

            updateSelectAdvantageB1();

        });
    </script>
      </div>
      "
        )
        
      )
      
    )
    
  ),
  
  HTML(
    "<!-- select2 YOUR COMPANY ADVANTAGE-->
          <div style='grid-column: span 6;'>

    <div id='selectTwoDivA1' class='selectTwoDivClassA1' style='margin-top: 10px;'>

        <div class='inputLabelClass' style='position: relative; top: -5px;'>
            <i class='fas fa-disease' style='color: #4bf542;'></i> select2 Test (works)
        </div>

        <select id='selectjquery1' multiple='multiple'></select>

    </div>

    <span id='placeholderTextInput1' style='display: none;'>TEXTO 1</span>
    <span id='selectResultText1' style='display: none;'>TEXTO 2</span>

    <style>
        #selectTwoDivA1 .select2-selection--multiple {
            border: 2px solid #4bf542 !important;
        }

        .selectTwoDivClassA1 .select2-selection--multiple {
            background-color: unset !important;
            color: #333333;
            width: 250px !important;
            height: 70px !important;
        }
    </style>

    <script>
        $(document).ready(function() {

            function updateSelectAdvantageA1(placeholderText1, textTooShort1) {
                $('#selectjquery1').select2({
                    placeholder: placeholderText1,
                    allowClear: true,
                    closeOnSelect: true,
                    minimumInputLength: 3,
                    maximumInputLength: 20,
                    maximumSelectionLength: 3,
                    tags: true,
                    language: {
                        maximumSelected: function (e) {
                            let text = 'Você pode atribuir apenas ' + e.maximum + ' vantagens.';
                            e.maximum != 1 && (text += 's.');
                            return text;
                        },
                        inputTooShort: function() {
                            return textTooShort1; // Corrigido aqui
                        }
                    }
                });
            }

            function updateSelectAdvantageB1() {
                let placeholderText1 = $('#placeholderTextInput1').text();
                let textTooShort1 = $('#selectResultText1').text();
                updateSelectAdvantageA1(placeholderText1, textTooShort1);
            }

            updateSelectAdvantageB1();

        });
    </script>
      </div>"
  )
  
)

server <- function(input, output) {
  
}

shinyApp(ui, server)

问题根源

  1. DOM加载时机不匹配:swipeR基于Swiper.js,内部DOM元素是页面初始加载后动态渲染的,而$(document).ready()仅在初始DOM加载完成后执行一次,此时swipeR内部的select元素还未被渲染,导致select2无法绑定目标元素。
  2. 重复ID冲突:代码中内部、外部的select元素使用了相同的id="selectjquery1",jQuery选择器会优先匹配外部元素,无法正确定位swipeR内部的元素。

解决方法

1. 修正重复ID问题

给swipeR内部的select元素及相关辅助元素分配唯一ID,避免与外部元素冲突。

2. 延迟初始化select2

监听Shiny的shiny:connected事件,结合定时器确保swipeR组件完全渲染后,再初始化内部的select2。

修正后的完整代码

library(shiny)

ui <- fluidPage(
  
  tags$head(
    tags$link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"),
    tags$link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.1.0-beta.1/css/select2.min.css"),
    tags$script(src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"),
    tags$script(src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.1.0-beta.1/js/select2.min.js")
  ),
  
  swipeR::swipeR(
    id = "carouselSwotContainer1",
    initialSlide = 1,
    bulletsSize = NULL,
    loop = FALSE,
    width = "398px",
    height = "288px",
    effect = "cube",
    cubeEffect = list(shadowScale = .9),
    speed = 1000,
    rewind = TRUE,
    wrapper = swipeR::swipeRwrapper(
      
      div(
        class = "swiper-no-swiping",
        div(style='grid-column: span 6;',
            div(id='selectTwoDivA1-inner', class='selectTwoDivClassA1', style='margin-top: 10px;',
                div(class='inputLabelClass', style='position: relative; top: -5px;',
                    tags$i(class='fas fa-disease', style='color: #4bf542;'),
                    " select2 Test (fixed)"
                ),
                tags$select(id='selectjquery-inner', multiple='multiple')
            ),
            tags$span(id='placeholderTextInput-inner', style='display: none;', 'TEXTO 1'),
            tags$span(id='selectResultText-inner', style='display: none;', 'TEXTO 2'),
            tags$style(HTML("
                #selectTwoDivA1-inner .select2-selection--multiple {
                    border: 2px solid #4bf542 !important;
                }

                .selectTwoDivClassA1 .select2-selection--multiple {
                    background-color: unset !important;
                    color: #333333;
                    width: 250px !important;
                    height: 70px !important;
                }
            "))
        )
      )
      
    )
    
  ),
  
  div(style='grid-column: span 6;',
      div(id='selectTwoDivA1', class='selectTwoDivClassA1', style='margin-top: 10px;',
          div(class='inputLabelClass', style='position: relative; top: -5px;',
              tags$i(class='fas fa-disease', style='color: #4bf542;'),
              " select2 Test (works)"
          ),
          tags$select(id='selectjquery1', multiple='multiple')
      ),
      tags$span(id='placeholderTextInput1', style='display: none;', 'TEXTO 1'),
      tags$span(id='selectResultText1', style='display: none;', 'TEXTO 2'),
      tags$style(HTML("
          #selectTwoDivA1 .select2-selection--multiple {
              border: 2px solid #4bf542 !important;
          }

          .selectTwoDivClassA1 .select2-selection--multiple {
              background-color: unset !important;
              color: #333333;
              width: 250px !important;
              height: 70px !important;
          }
      "))
  ),
  
  tags$script(HTML("
      // 初始化外部select2
      $(document).ready(function() {
          function updateSelectAdvantageA1(placeholderText1, textTooShort1) {
              $('#selectjquery1').select2({
                  placeholder: placeholderText1,
                  allowClear: true,
                  closeOnSelect: true,
                  minimumInputLength: 3,
                  maximumInputLength: 20,
                  maximumSelectionLength: 3,
                  tags: true,
                  language: {
                      maximumSelected: function (e) {
                          let text = 'Você pode atribuir apenas ' + e.maximum + ' vantagens.';
                          e.maximum != 1 && (text += 's.');
                          return text;
                      },
                      inputTooShort: function() {
                          return textTooShort1;
                      }
                  }
              });
          }

          function updateSelectAdvantageB1() {
              let placeholderText1 = $('#placeholderTextInput1').text();
              let textTooShort1 = $('#selectResultText1').text();
              updateSelectAdvantageA1(placeholderText1, textTooShort1);
          }

          updateSelectAdvantageB1();
      });

      // 监听Shiny连接事件,延迟初始化内部select2
      document.addEventListener('shiny:connected', function() {
          setTimeout(function() {
              function updateSelectAdvantageInner(placeholderText, textTooShort) {
                  $('#selectjquery-inner').select2({
                      placeholder: placeholderText,
                      allowClear: true,
                      closeOnSelect: true,
                      minimumInputLength: 3,
                      maximumInputLength: 20,
                      maximumSelectionLength: 3,
                      tags: true,
                      language: {
                          maximumSelected: function (e) {
                              let text = 'Você pode atribuir apenas ' + e.maximum + ' vantagens.';
                              e.maximum != 1 && (text += 's.');
                              return text;
                          },
                          inputTooShort: function() {
                              return textTooShort;
                          }
                      }
                  });
              }

              let placeholderText = $('#placeholderTextInput-inner').text();
              let textTooShort = $('#selectResultText-inner').text();
              updateSelectAdvantageInner(placeholderText, textTooShort);
          }, 500);
      });
  "))
  
)

server <- function(input, output) {
  
}

shinyApp(ui, server)

关键修正说明

  • 唯一ID设置:将swipeR内部的select元素及辅助元素ID改为selectjquery-inner等唯一值,避免ID冲突。
  • 延迟初始化逻辑:通过shiny:connected事件结合setTimeout,确保swipeR组件完全渲染后再初始化select2,解决DOM加载时机问题。
  • 规范标签使用:用Shiny原生tags函数替代原生HTML字符串,避免转义问题,代码更易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 10:43:12