Clone does not clone selected values

I did not expect this, but the following test did not pass the cloned value check:

test("clone should retain values of select", function() { var select = $("<select>").append($("<option>") .val("1")) .append($("<option>") .val("2")); $(select).val("2"); equals($(select).find("option:selected").val(), "2", "expect 2"); var clone = $(select).clone(); equals($(clone).find("option:selected").val(), "2", "expect 2"); }); 

Is it correct?

+70
jquery
Apr 13 '09 at 1:35
source share
11 answers

After further research, I found this ticket in the jQuery bug tracking system, which explains the error and provides a workaround. Obviously, cloning selected values ​​is too expensive for them to fix it.

https://bugs.jquery.com/ticket/1294

I used the cloning method in a general method where you can clone something, so I'm not sure when or whether there will be a choice to set the value. So I added the following:

 var selects = $(cloneSourceId).find("select"); $(selects).each(function(i) { var select = this; $(clone).find("select").eq(i).val($(select).val()); }); 
+70
Apr 13 '09 at 13:20
source share

Here's a fixed version of the clone method for jQuery:

https://github.com/spencertipping/jquery.fix.clone

 // Textarea and select clone() bug workaround | Spencer Tipping // Licensed under the terms of the MIT source code license // Motivation. // jQuery clone() method works in most cases, but it fails to copy the value of textareas and select elements. This patch replaces jQuery clone() method with a wrapper that fills in the // values after the fact. // An interesting error case submitted by Piotr Przybył: If two <select> options had the same value, the clone() method would select the wrong one in the cloned box. The fix, suggested by Piotr // and implemented here, is to use the selectedIndex property on the <select> box itself rather than relying on jQuery value-based val(). (function (original) { jQuery.fn.clone = function () { var result = original.apply(this, arguments), my_textareas = this.find('textarea').add(this.filter('textarea')), result_textareas = result.find('textarea').add(result.filter('textarea')), my_selects = this.find('select').add(this.filter('select')), result_selects = result.find('select').add(result.filter('select')); for (var i = 0, l = my_textareas.length; i < l; ++i) $(result_textareas[i]).val($(my_textareas[i]).val()); for (var i = 0, l = my_selects.length; i < l; ++i) result_selects[i].selectedIndex = my_selects[i].selectedIndex; return result; }; }) (jQuery.fn.clone); 
+29
Aug 03 '12 at 22:32
source share

Made a plugin from Chief7's answer:

 (function($,undefined) { $.fn.cloneSelects = function(withDataAndEvents, deepWithDataAndEvents) { var $clone = this.clone(withDataAndEvents, deepWithDataAndEvents); var $origSelects = $('select', this); var $clonedSelects = $('select', $clone); $origSelects.each(function(i) { $clonedSelects.eq(i).val($(this).val()); }); return $clone; } })(jQuery); 

Tested only for a short time, but it seems to work.

+8
Jul 15 2018-11-17T00:
source share

My approach is a little different.

Instead of changing the selection during cloning, I just look at each select on the page for the change event, and then, if the value is changed, I add the required selected attribute to the selected <option> , so it becomes <option selected="selected"> . Since the selection is now marked in the <option> markup, it will be passed when you .clone() it.

The only code needed:

 //when ANY select on page changes its value $(document).on("change", "select", function(){ var val = $(this).val(); //get new value //find selected option $("option", this).removeAttr("selected").filter(function(){ return $(this).attr("value") == val; }).first().attr("selected", "selected"); //add selected attribute to selected option }); 

And now you can copy the selected option in any way, and it will also copy the value.

 $("#my-select").clone(); //will have selected value copied 

I think this solution is less customary, so you don’t have to worry if your code breaks if you change something later.

If you do not want it to be applied to each selection on the page, you can change the selector in the first line, for example:

 $(document).on("change", "select.select-to-watch", function(){ 
+3
Mar 25 '16 at 11:16
source share

Yes. This is because the 'selected' property for the 'select' DOM node is different from the 'selected' attribute of the parameters. jQuery does not change the attributes of parameters in any way.

Try this instead:

 $('option', select).get(1).setAttribute('selected', 'selected'); // starting from 0 ^ 

If you are really interested in how the val function works, you can explore

 alert($.fn.val) 
+2
Apr 13 '09 at 2:52
source share

Cloning a <select> does not copy the value= property to <option> s. Thus, the Mark plugin does not work in all cases.

To fix this, do this before cloning the <select> values:

 var $origOpts = $('option', this); var $clonedOpts = $('option', $clone); $origOpts.each(function(i) { $clonedOpts.eq(i).val($(this).val()); }); 

Another cloning method that <select> selects in jQuery 1.6.1 + ...

 // instead of: $clonedSelects.eq(i).val($(this).val()); // use this: $clonedSelects.eq(i).prop('selectedIndex', $(this).prop('selectedIndex')); 

The latter allows you to set the <option> values ​​after setting selectedIndex .

+2
Aug 17 '11 at 17:23
source share

Simplification of the answer of the main7:

 var cloned_form = original_form.clone() original_form.find('select').each(function(i) { cloned_form.find('select').eq(i).val($(this).val()) }) 

Again, here is the jQuery ticket: http://bugs.jquery.com/ticket/1294

+2
Aug 04 '14 at
source share

If you just need a select value to serialize a form or something like that, this works for me:

 $clonedForm.find('theselect').val($origForm.find('theselect').val()); 
0
Jan 28 '15 at 9:35
source share

After 1 hour of trying different solutions that did not work, I created this simple solution

 $clonedItem.find('select option').removeAttr('selected'); $clonedItem.find('select option[value="' + $originaItem.find('select').val() + '"]').attr('selected', 'true'); 
0
Nov 27 '15 at 16:22
source share

@ pie6k show a good idea.

He solved my problem. I am changing it a bit:

 $(document).on("change", "select", function(){ var val = $(this).val(); $(this).find("option[value=" + val + "]").attr("selected",true); }); 
0
Jul 29 '17 at 3:46 on
source share
 $(document).on("change", "select", function(){ original = $("#original"); clone = $(original.clone()); clone.find("select").val(original.find("select").val()); }); 
0
Jul 21 '19 at 23:21
source share



All Articles