动态创建select2的初始化

22

我有一个Select2下拉菜单,我为其提供了一个匹配函数。在初始页面加载时,它被初始化如下:

jQuery(document).ready(function() {
    jQuery(".my_select2").select2({
        matcher: function(term, text) {...}
    });
});

这在页面初始加载时运行良好。

现在,我有额外的下拉菜单(通过 AJAX 动态创建的 select 元素,即 jQuery(match).load(url))。这些额外的下拉菜单没有被初始化为 select2 小部件,这是可以理解的,即使它们与原始的 select2 选择器匹配。

那么,我该如何告诉 jQuery 将这些动态创建的 select 元素视为需要初始化的 select2 项目呢?我能否在匹配元素上设置某种“监视”,以便每次添加到页面时都会触发 select2 初始化?

我记得 jQuery 在一段时间前引入了 live(),支持在元素创建之前匹配元素,如果我理解正确。我从未使用过该功能,现在它似乎已过时。但它确实感觉像我正在寻找的东西。

这是一个 WordPress 插件,目前使用的是 jQuery v1.11.2。


3
你可以查看 https://github.com/uzairfarooq/arrive - SSA
这意味着jQuery不能在没有帮助程序的情况下监视正在创建的项目。或者arrive.js是为了使在根本不使用jQuery时事情变得更容易? - Jason
如果你在将选择项添加到DOM后无法触发select2,那么你必须像第一个评论所述使用DOM变异观察器。 - Irvin Dominin
谢谢,我现在采取的方法是在将新选择列表添加到DOM时显式地触发select2。这意味着有两个地方初始化了列表,但我已经将所有初始化代码移动到函数中以保持重复最小化。我认为我最初认为新元素的初始化可以自动完成是有点理想化的。它可以像juna-c的答案所演示的那样完成,但这确实依赖于一些更现代的浏览器功能,这在这个应用程序(商店 - 所有客户都很重要)中不合理地假定/强制在最终用户中使用。 - Jason
7个回答

32

您可以尝试使用 DOMNodeInserted 并查找 select 或分配给它们的 class。

演示

$('body').on('DOMNodeInserted', 'select', function () {
    $(this).select2();
});

更新

DOMNodeInserted

已弃用 此功能已从Web标准中删除。尽管某些浏览器可能仍然支持它,但正在逐步停用。如果可能,请避免使用该功能并更新现有代码;

建议的方法类似于以下内容,使用MutationObserver

$(function() {
  $("button").on("click", function() {
    $("#dynamic-container").append($("<select><option>test</option><select/>"));
  });

  // select the target node
  var target = document.getElementById('dynamic-container');

  if (target) {
    // create an observer instance
    var observer = new MutationObserver(function(mutations) {
      //loop through the detected mutations(added controls)
      mutations.forEach(function(mutation) {
      //addedNodes contains all detected new controls
        if (mutation && mutation.addedNodes) {
          mutation.addedNodes.forEach(function(elm) {
          //only apply select2 to select elements
            if (elm && elm.nodeName === "SELECT") {
              $(elm).select2();
            }
          });
        }
      });
    }); 
    
    // pass in the target node, as well as the observer options
    observer.observe(target, {
      childList: true
    });

    // later, you can stop observing
    //observer.disconnect();
  }
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>


<button>Add new select</button>
  <div id="dynamic-container">

  </div>


谢谢,那个效果很好。不过我想尽量避免一些重复 - select2下拉菜单在两个不同的位置进行初始化。我有一个匹配器和一个“.on('change', ...)”触发器作用于下拉菜单上,所以这些也需要在两个地方进行初始化。我会考虑如何将初始化定义在一个地方。 - Jason
我会接受这个答案,因为它确实解决了最初提出的问题,尽管我已经改变了整个方法来响应答案。建议使用https://github.com/uzairfarooq/arrive包进行封装以使其更易于使用(即更抽象)。感谢大家。 - Jason
@Jason,为了明确事情。arrive.js没有包装DOMNodeInserted API。它在内部使用新的MutationObserver API。DOMNodeInserted API已经弃用,应该避免使用。 - Uzair Farooq
4
不工作,在 Google Chrome 中测试过了。即使演示也不工作。 - Shakeel Ahmed
3
在谷歌浏览器中对我有效。 - Ben Clayton

19

最近我遇到了类似的情况,但是我用了一种非常通常的方法解决了它:

$(document).ready(function() {

 //function to initialize select2
  function initializeSelect2(selectElementObj) {
    selectElementObj.select2({
      width: "80%",
      tags: true
    });
  }


 //onload: call the above function 
  $(".select-to-select2").each(function() {
    initializeSelect2($(this));
  });

 //dynamically added selects

  $(".add-new-select").on("click", function() {
    var newSelect = $("<select class='select-to-select2'  multiple><option>option 1</option><option>option 2</option></select>");
    $(".select-container").append(newSelect);
    initializeSelect2(newSelect);
  });


});
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.1/css/select2.css" rel="stylesheet" />

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.1/js/select2.min.js"></script>
<div class="select-container">
  <select class='select-to-select2' multiple>
    <option value='1'>option1</option>
    <option value='2'>option2</option>
  </select>

  <select class='select-to-select2' multiple>
    <option value='1'>option1</option>
    <option value='2'>option2</option>
  </select>

</div>
<div>
  <button class="add-new-select">Add New Select</button>
</div>

在.load函数中,查找所有需要在load函数回调中初始化的选择元素,并在每个选择元素上调用initializeSelect2函数。

希望这能帮助正在寻找简单解决方案的人。


1
谢谢@Roshan,我使用了你的代码来获取select2。但是我遇到了问题,只有一个select2可以工作,其他的select没有初始化initializeSelect2 - always-a-learner
你是否已经将 select-to-select2 类添加到 select 元素中? - roshan
1
@ankitsuthar 我之前使用jQuery的clone()方法来复制select元素,但是像你说的那样,Select2只在第一个元素上起作用。后来我发现,你不需要在要克隆的元素上应用Select2,而只需要在克隆出来的元素上应用即可。如果已经应用了Select2,再对其进行克隆会导致不可靠的结果。 - a.barbieri
我将其与远程select2.js和dropzone.js一起使用,只需要将函数从文档准备好中取出即可。谢谢我的朋友。 - absolutkarlos

11

对我来说有效

<div id="somediv">
    <select class="component">
    ...
    </select>
</div>



<script>
    $(document).on('click', '#addComponent', function () {

        $('#somediv').append(data); //data is my new select
        $('.component:last').select2();
    });
</script>

这真的很不错。我本来想使用'MutationObserver',但你的解决方案非常好用。 - A.G.

1
我曾经遇到过同样的问题,但是在苦思冥想了几天后,我找到了一种解决方案,但如果你期望有良好的性能,那么它并不好。
因此,当DOM第一次加载时,所有已加载的选择项都将具有select2功能。对于动态生成的字段,select2功能将不可用,因为select2脚本已经加载。
因此,为使select2起作用,请在单击时动态重新加载脚本。
function reload(id) {
    $(id).on("click", function () {
        $("head").append($("<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.1/css/select2.css' type='text/css' media='screen' />"));
        $.getScript("https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js", function () {
            $.getScript("https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.1/js/select2.min.js", function () { 
                $('select').select2();
            })
        })
    })
}

这个功能是将 select2 功能添加到 $ 中。

0

补充其他答案,如果应用于

  1. 隐藏的选择器

或者

  1. 具有相同ID的选择器

不要应用于隐藏元素,请使用:visible选择器。如果您正在动态生成元素,请确保iddata-select2-id是唯一的(您也可以删除它)。


0

类似于上面的答案:

function initSelect2(element){
  element.select2({   });
}

// Dynamic add 
$('button[data-id="add-item"]').on('click', function(){
   // Add element
   obj.addItem();
   // Add Select2 to element
   initSelect2($('element'));
});

-1
我按照roshan的解决方案解决了这个问题,但是没有在函数中传递选择对象。这是针对从ajax调用输出的表格。
$(document).ready(function() {

        function initSelect2() {
            $("[id^='DDAlertFreq']").select2({
                minimumResultsForSearch: Infinity,
                allowClear: false,
                theme: "bootstrap"
            });
        };

//define the dropdown and set to variable    
var DDAlertFrequency = '<select id="DDAlertFreq" ><option value="Fifteen_Minutes">15 Minutes</option><option value="Thirty_Minutes">30 Minutes</option><option value="Hour">Hour</option><option value="Two_Hours">2 Hours</option><option value="Three_Hours">3 Hours</option><option value="Four_Hours">4 Hours</option><option value="Six_Hours">6 Hours</option><option value="Eight_Hours">8 Hours</option><option value="Twelve_Hours">12 Hours</option><option value="Day">Day</option></select>'

function RetrieveUserAlerts(uid) {
                $.ajax({
                    url: 'SavedSearches.asmx/LoadUserAlerts',
                    dataType: 'json',
                    method: 'post',
                    data: { userid: uid },
                    success: function (data) {
                        var tbl = $("#tblAlerts > tbody");
                        tbl.empty();
                        $.each(data, function () {
                            userAlert.alert_idx = this['alert_idx'];
                            userAlert.Name = this['Name'];
                            userAlert.price_alert_low = this['price_alert_low'];
                            userAlert.alert_frequency = this['alert_frequency'];
                            userAlert.alert_max_per_day = this['alert_max_per_day'];
                            userAlert.alert_to_email = this['alert_to_email'];
                            userAlert.alert_to_sms = this['alert_to_sms'];
                            userAlert.active = this['active'];
                            userAlert.alert_start_date = moment(this['alert_start_date']).format("MM/DD/YY");
                            userAlert.alert_end_date = moment(this['alert_end_date']).format("MM/DD/YY");
                            userAlert.OpenSectionID = this['OpenSectionID'];
// modify the dropdown to assign unique id and match selection
                            var selectAlert = DDAlertFrequency.replace("DDAlertFreq", "DDAlertFreq_" + userAlert.alert_idx).replace('"' + userAlert.alert_frequency + '"', '"' + userAlert.alert_frequency + '" selected');
                            var tblRow = '<tr><td>'
                                + userAlert.Name
                             + '</td><td>'
                            + '<input id="txtPriceAlertLow_' + userAlert.alert_idx + '" type="text" class="form-control" value="' + userAlert.price_alert_low + '"/>'
                             + '</td><td>'
                            + '<input id="chkAlertToEmail_' + userAlert.alert_idx + '" type="checkbox" ' + ((userAlert.alert_to_email == true) ? "checked" : "") + ' />'
                             + '</td><td>'
                            + '<input id="chkAlertToEmail_' + userAlert.alert_idx + '" type="checkbox" ' + ((userAlert.alert_to_sms == true) ? "checked" : "") + ' />'
                             + '</td><td>'
                            + selectAlert //modified Select2 dropdown
                             + '</td><td>'
                             + '<input id="txtMaxPerDay_' + userAlert.alert_idx + '" type="text" class="form-control" value="' + userAlert.alert_max_per_day + '"/>'
                             + '</td><td>'
                            + userAlert.alert_start_date
                             + '</td><td>'
                            + userAlert.alert_end_date
                             + '</td><td>'
                           + '<input id="chkActive_' + userAlert.alert_idx + '" type="checkbox" ' + ((userAlert.active == true) ? "checked" : "") + ' />'
                             + '</td><tr>'
                            tbl.append(tblRow);
                            initSelect2(); //call the function to initialize all Select2 dropdowns created
                        });
                    },
                    error: function (err) {
                        console.log('Error (RetrieveUserAlerts): ' + JSON.stringify(err, null, 2));
                    }
                });
            };

抱歉留下了一些无关的东西 - 我在评论感兴趣的领域。 希望这能帮助其他人!


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