Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I didn't expect it but the following test fails on 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 this right?

share|improve this question

5 Answers

After further research I found this ticket in the JQuery bug tracker system which explains the bug and provides a work around. Apparently, it is too expensive to clone the select values so they won't fix it.

http://dev.jquery.com/ticket/1294

My use of the clone method was in a generic method where anything might be cloned so I'm not sure when or if there will be a select to set the value on. 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());
	});
share|improve this answer

Made a plugin out of 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);

Only tested it briefly, but it seems to work.

share|improve this answer

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's clone() method works in most cases, but it fails to copy the value of textareas and select elements. This patch replaces jQuery's 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's 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);
share|improve this answer
+1 Thats an awesome plugin. Please continue maintaining this project. – Kave Dec 20 '12 at 12:26

Yes. This is because the 'selected' property of a 'select' DOM node differs from the 'selected' attribute of the options. jQuery does not modify the options' attributes in any way.

Try this instead:

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

If you're really interested in how the val function works, you may want to examine

alert($.fn.val)
share|improve this answer
If I use the val() on the select object the test fails as well: test("clone should retain values of select", function() { var select = $("<select>").append($("<option>").val("1")).append($("<option>").val("2")); $(select).val("2"); equals($(select).val(), "2", "expect 2"); var cl – chief7 Apr 13 '09 at 12:46
That's pretty weird, cause this works for me in IE 6/7, Firefox 3 and Opera 9. Maybe something wrong with your 'equals' function? alert(eval(' select = $("<select>").append($("<option>").val("1")).append($("<option>").val("2"));$(se‌​lect).val("2"); $(select).val() ')); – Pumbaa80 Apr 13 '09 at 14:27

Cloning a <select> does not copy the value= property on <option>s. So Mark's plugin does not work in all cases.

To fix, 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());
});

A different way to clone which <select> option is selected, 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 the selectedIndex.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.