52

I am trying to clone a row which contains select2 tool ,when i clone that row using jQuery the cloned select2 is not responding.In image given below first select2 which is original is working fine but 2nd and 3rd select2 which are cloned not responding

code snippet:

$(document).ready(function() {
  var clonedRow = $('.parentRow').clone().html();
  var appendRow = '<tr class = "parentRow">' + clonedRow + '</tr>';
  $('#addRow').click(function() {
    $('#test').after(appendRow);
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<tr class="parentRow" id="test">
  <td>
    <g:message code="educationDetails.educationLevel.label" default="Education Level" />
  </td>
  <td>
    <div style="float: left;">
      <g:select name="degree.id" from="${EducationalDegree.list()}" optionKey="id" optionValue="title" noSelection="['': '']" id="degree" value="${cvEducationDetailCO?.degree?.id}" onchange="changeGradeSelectData(this.value)" />
    </div>
    <div>
      <a href="javascript:void(0)" id="addRow">
        <img alt="" title="Add Additional Education Level" src="/static/images
                                                                /top_submit_1.gif">
      </a>
    </div>
  </td>
</tr>
j08691
  • 204,283
  • 31
  • 260
  • 272
ABC
  • 4,263
  • 10
  • 45
  • 72

14 Answers14

75

Before you clone the row, you need to disable Select2 on the select element by calling its destroy method:

destroy

Reverts changes to DOM done by Select2. Any selection done via Select2 will be preserved.

See http://ivaynberg.github.io/select2/index.html

After you clone the row and insert its clone in the DOM, you need to enable select2 on both select elements (the original one and the new cloned one).

Here's a JSFiddle that shows how it can be done: http://jsfiddle.net/ZzgTG/

Fiddle's code

HTML

<div id="contents">
    <select id="sel" data-placeholder="-Select education level-">
        <option></option>
        <option value="a">High School</option>
        <option value="b">Bachelor</option>
        <option value="c">Master's</option>
        <option value="c">Doctorate</option>
    </select>
</div>
<br>
<button id="add">add a dropdown</button>

CSS

#contents div.select2-container {
    margin: 10px;
    display: block;
    max-width: 60%;
}

jQuery

$(document).ready(function () {
    $("#contents").children("select").select2();
    $("#add").click(function () {
        $("#contents")
            .children("select")
            // call destroy to revert the changes made by Select2
            .select2("destroy")
            .end()
            .append(
                // clone the row and insert it in the DOM
                $("#contents")
                .children("select")
                .first()
                .clone()
        );
        // enable Select2 on the select elements
        $("#contents").children("select").select2();
    });
});
Community
  • 1
  • 1
Zabbala
  • 2,183
  • 1
  • 17
  • 17
  • About the fiddle: If I select the `Master's` option it changes to the option `Doctorate`. Could you take a look at that?! – Michel Ayres Mar 14 '14 at 17:06
  • 1
    Thanks for the detailed explanation! A note: the fiddle as-is was broken by select2 moving to a proper github org. You can fix by updating to `https://select2.github.io/select2/select2-3.3.2/select2.js` and `https://select2.github.io/select2/select2-3.3.2/select2.css` Note also the "https", which (for me) prevents breakage (firefox blocks insecure mixed content on https-pages like jsfiddle). Fixed fiddle: https://jsfiddle.net/nxabgfsv/ – Jules Kerssemakers Feb 24 '20 at 14:13
  • 4
    Note that this fiddle uses select2 3.x; In the current 4.x series, duplicated IDs (`#sel`) no longer work (it's also invalid HTML), and you need to set either `id=""`, or update the cloned ID to be unique. – Jules Kerssemakers Feb 24 '20 at 15:15
11

you have to destroy select2 first before cloning, but .select2('destroy') not works. Use this:

$myClone = $("section.origen").clone();

$myClone.find("span").remove();
$myClone.find("select").select2();

$("div").append($myClone);
amxa
  • 1,147
  • 9
  • 6
  • Super frustrating, but cloning a `select2` enabled ` – Tim Lewis Mar 17 '17 at 15:18
9

I faced the same problem, while trying to add a row to a table dynamically. (the row contains more than one select2 instance.)

I solved it in this way:

function addrow()
{
    var row = $("#table1 tr:last");

    row.find(".select2").each(function(index)
    {
        $(this).select2('destroy');
    }); 

    var newrow = row.clone();       

    $("#table1").append(newrow);

    $("select.select2").select2();
}

The trick was that you need to destroy all the instances of .select2 separately and before cloning the row. And then after cloning, re-initialize the .select2. I hope this will work for others as well.

eheydenr
  • 1,000
  • 11
  • 10
7

I've actually created account to answer this, since it took me a while to make it work.

This is not working when used before cloning: $('.selectpicker').select2('destroy')

But this works in my case:

$('.selectpicker').select2('destroy');
$('.selectpicker')
    .removeAttr('data-live-search')
    .removeAttr('data-select2-id')
    .removeAttr('aria-hidden')
    .removeAttr('tabindex');

Just remove all the additional attributes which select2 adds.

Edit #1

Ok so seems like You also got to remove ID from the element that is being cloned since select2 tries to add it's own unique id when none is found on the select, but when You do have select it's getting messy and selet2 attaches only on the last element with the same ID.

Volmarg Reiso
  • 437
  • 4
  • 11
  • 3
    I had the same problem and solved almost the same way, had also to clear the option items ids: $(ddls[i]).removeAttr('data-select2-id'); $(ddls[i]).find('option').removeAttr('data-select2-id'); – jeanadam May 29 '18 at 18:15
  • @jacvalle - you saved my day ! And as a +, if you have optgroups, remove the ids from there too ;) – qdev Jun 21 '19 at 16:52
3

I solved this problem with it:
Call destroy method before you add new row

 $(".className").select2("destroy");  //Destroy method , connect with class no ID (recomend)

After it call select2 jQuery function:

$(".className").select2({               
                placeholder: "Example",
                allowClear:true 
            });

hope it helps ;)

Yaakov Ellis
  • 40,752
  • 27
  • 129
  • 174
Husni Salax
  • 1,968
  • 1
  • 19
  • 29
3

you have to destroy all select2 first before cloning for example:

    var div = $("#filterForm div"); 

    //find all select2 and destroy them   
   div.find(".select2").each(function(index)
    {
        if ($(this).data('select2')) {
            $(this).select2('destroy');
          } 
    });

    //Now clone you select2 div 
    $('.filterDiv:last').clone( true).insertAfter(".filterDiv:last"); 

    //we must have to re-initialize  select2 
    $('.select2').select2(); 
Abhinav bhardwaj
  • 2,657
  • 25
  • 21
2

I solved this by creating a different clone function:

jQuery.fn.cloneSelect2 = function (withDataAndEvents, deepWithDataAndEvents) {
  var $oldSelects2 = this.is('select') ? this : this.find('select');
  $oldSelects2.select2('destroy');
  var $clonedEl = this.clone(withDataAndEvents, deepWithDataAndEvents);
  $oldSelects2.select2();
  $clonedEl.is('select') ? $clonedEl.select2() :
                            $clonedEl.find('select').select2();
  return $clonedEl;
};
jorar91
  • 130
  • 7
1

What worked for me, to clone select input managed by select2 I did the following:
1. Destroy the select that is cloned
2. Clone with a true param
3. Remove from the clone attributes 'id' and 'data-select2-id'
4. Remove attribute 'data-select2-id' from every option in the clone
5. Re-initialize element that was cloned
6. Initialize cloned element resetting the value

Here is an example:

const origin = $('select').last(); // last in case there are more than one select
origin.select2('destroy');
const cloned = origin.clone(true);
cloned.removeAttr('data-select2-id').removeAttr('id');
cloned.find('option').removeAttr('data-select2-id');
origin.select2();
cloned.select2().val(null).trigger('change');
husky
  • 11
  • 2
1
//Paste this code after your codes.
$('span.select2').remove();
$('select.select2').removeAttr('data-select2-id');
$('select.select2').select2();
  • 4
    Welcome to SO. It is common to add some more explanation in the answers than just a block of code. You could explain what your code does and why it solves the issue. – KillerX Mar 06 '20 at 12:47
  • this code removes the existing select2 after clone – Mohamed Raza May 16 '23 at 21:02
0

How to use jorar91 code.

var $clone = $("#multiple_objects_with_select2").cloneSelect2();

$($clone ).find('select').select2({
    width:'100%'
});

$("#some_place").append($clone);
Musznik
  • 94
  • 2
  • 5
0

In the parent div don'nt apply select2 on it. First clone it and save it in a variable then apply select2. Later apply select2 on the origional(as the origional without select2 is alreay saved in a variable) and then apply on the newly created select. I tried this way and it is working

AbdulBasit
  • 1,269
  • 11
  • 19
0

I offer to make this, it is my simple example:

function copy_row(id) {
    var new_row = $("#"+id+" tbody tr:first").clone();
    $("#"+id+" tbody").append('<tr>'+new_row.html()+'</tr>');
    $("#"+id+" tbody tr:last input").val('');
    $("#"+id+" tbody tr:last select").val('');
    $("#"+id+" tbody tr:last input[type='checkbox']").prop('checked', false);

    // Initialize
    $(".select-remote-address:last, .select-remote-address2:last").select2({
        language: {
            inputTooShort: function() {
            return 'Įveskite...';
        }},
        ajax: {
            url: base_url+"index.php/orders/data/getAddress",
            type: 'POST',
            dataType: 'json',
            delay: 250,
            data: function (params) {
                return {
                    q: params.term, // search term
                    page: params.page
                };
            },
            processResults: function (data, params) {

                // parse the results into the format expected by Select2
                // since we are using custom formatting functions we do not need to
                // alter the remote JSON data, except to indicate that infinite
                // scrolling can be used
                params.page = params.page || 1;

                return {
                    results: data,
                    pagination: {
                        more: (params.page * 30) < data.total_count
                    }
                };
            },
            cache: true
        },
        escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
        minimumInputLength: 1,
        templateResult: formatRepo, // omitted for brevity, see the source of this page
        templateSelection: formatRepoSelection // omitted for brevity, see the source of this page
    });

    $(".select-remote-address:last").last().next().next().remove();
    $(".select-remote-address2:last").last().next().next().remove();

    // Datetimepicker
    jQuery('.date_1:last, .date_2:last').datetimepicker({
      i18n:{
        lt:{
         months:[
          'Sausis','Vasaris','Kovas','Balandis',
          'Gegužė','Birželis','Liepa','Rugpjūtis',
          'Rugsėjis','Spalis','Lapkritis','Gruodis',
         ],
         dayOfWeek:[
          "Pir", "An", "Tre", "Ket",
          "Pen", "Šeš", "Sek",
         ]
        }
       },
      format:'Y-m-d H:i',
    });
}
Ramas Win
  • 17
  • 3
0

And one more solution:

function add_column(copy, paste) {
    $("." + copy + ":first").clone().appendTo("." + paste);
    $("." + paste + " tr:last input").val('');
    $("." + paste + " tr:last select").val('');
    // and etc...

  // Initialize
  $("." + paste + " tr:last select").select2({
      language: {
          inputTooShort: function() {
          return 'Prašome įvesti bent vieną raidę paieškai';
      }},
      ajax: {
          url: base_url+"fuel/Fuel/getWorkersSelect",
          type: 'POST',
          dataType: 'json',
          delay: 250,
          data: function (params) {
              return {
                  q: params.term, // search term
                  page: params.page
              };
          },
          processResults: function (data, params) {

              // parse the results into the format expected by Select2
              // since we are using custom formatting functions we do not need to
              // alter the remote JSON data, except to indicate that infinite
              // scrolling can be used
              params.page = params.page || 1;

              return {
                  results: data,
                  pagination: {
                      more: (params.page * 30) < data.total_count
                  }
              };
          },
          cache: true
      },
      escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
      minimumInputLength: 1,
      templateResult: formatRepo, // omitted for brevity, see the source of this page
      templateSelection: formatRepoSelection // omitted for brevity, see the source of this page
  });

    $("." + paste + " tr:last select").last().next().next().remove();

}

function remove_column(e, paste) {
    var how = $("." + paste + " tr").length;
    if (how >= 2) {
        $(e).parent().parent().remove();
    } else {
        $("." + paste + " input").val('');
        $("." + paste + " select").val('');
        // and etc...
    }
}
<table class="table table-striped table-bordered">
                 <thead>
                     <tr>
                         <th width="15%">Mašina</th>
                         <th width="15%">Išduota</th>
                         <th width="15%">Grąžinta</th>
                         <th width="20%">Vairuotojas</th>
                   <th width="10%">Neaktualus</th>
                   <th width="15%">Perdavimo aktas</th>
                         <th width="10%">Veiksmai</th>
                     </tr>
                 </thead>
                 <tbody class="paste_place">
                     <tr class="copy_item">
                         <td><input type="text" name="masina[]" class="form-control" placeholder="Įveskite..." /></td>
                         <td><input type="text" name="isduota[]" class="form-control datepicker" placeholder="Įveskite..." /></td>
                         <td><input type="text" name="grazinta[]" class="form-control datepicker" placeholder="Įveskite..." /></td>
                         <td>
                     <select class="form-control select-remote-vairuotojai" name="vairuotojas[]">
                      <option value="">Pasirinkite iš sąrašo</option>
                     </select>
                   </td>
                   <td><input type="text" name="neaktualus[]" class="form-control" placeholder="Įveskite..." /></td>
                   <td>haha</td>
                         <td><a onclick="add_column('copy_item', 'paste_place');"><i style="font-size: 20px;" class="icon-plus-circle2"></i></a> &nbsp;<a onclick="remove_column(this, 'paste_place');"><i style="font-size: 20px; color: red;" class="icon-minus-circle2"></i></a></td>
                     </tr>
                 </tbody>
             </table>
Ramas Win
  • 17
  • 3
0

Many answers are a mix of solutions, for me the problem was element id not being unique.

When you are cloning an element with an id, be sure to set it to an unique value to make the select2 instances independent, otherwise initializing the cloned will destroy the previous.

element.attr('id', element.attr('id')+Math.random());
Marco somefox
  • 370
  • 2
  • 10