Клон не клонирует выбранные значения

Я этого не ожидал, но следующий тест не прошел проверку клонированной стоимости:

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");
});

Правильно ли это?

Ответ 1

После дальнейших исследований я нашел этот билет в системе отслеживания ошибок JQuery, которая объясняет ошибку и предоставляет обходной путь. Очевидно, что клонировать выбранные значения слишком дорого, чтобы они не исправили это.

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

Я использовал метод клонирования в общем методе, где можно клонировать что-либо, поэтому я не уверен, когда или будет ли выбор для установки значения. Поэтому я добавил следующее:

var selects = $(cloneSourceId).find("select");
$(selects).each(function(i) {
    var select = this;
    $(clone).find("select").eq(i).val($(select).val());
});

Ответ 2

Здесь фиксированная версия метода clone для 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);

Ответ 3

Сделал плагин из ответа Chief7:

(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);

Проверено только на короткое время, но, похоже, оно работает.

Ответ 4

Мой подход немного отличается.

Вместо того, чтобы изменять выбор во время клонирования, я просто просматриваю каждую select на странице для события change, а затем, если значение изменено, я добавляю необходимый атрибут selected к выбранному <option>, поэтому он становится <option selected="selected">. Поскольку выделение теперь отмечено в разметке <option>, оно будет передано, когда вы .clone() его.

Единственный необходимый код:

//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
});

И теперь вы можете скопировать выбранный вариант любым способом, и он также скопирует значение.

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

Я думаю, что это решение менее обычай, поэтому вам не нужно беспокоиться, если ваш код сломается, если вы что-то измените позже.

Если вы не хотите, чтобы его применяли к каждому выбору на странице, вы можете изменить селектор в первой строке, например:

$(document).on("change", "select.select-to-watch", function(){

Ответ 5

Да. Это потому что свойство 'selected' для 'select' DOM node отличается от атрибута 'selected' параметров. jQuery никак не изменяет атрибуты параметров.

Попробуйте это вместо:

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

Если вам действительно интересно, как работает функция val, вы можете изучить

alert($.fn.val)

Ответ 6

Клонирование a <select> делает не копировать свойство value= на <option> s. Таким образом, плагин Mark не работает во всех случаях.

Чтобы исправить это, сделайте это, прежде чем клонировать значения <select>:

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

Другой способ клонирования, который выбирается <select>, в jQuery 1.6.1 +...

// instead of:
$clonedSelects.eq(i).val($(this).val());

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

Последний позволяет вам установить значения <option> после установки selectedIndex.

Ответ 7

Упрощение ответа главного7:

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

Снова, здесь билет jQuery: http://bugs.jquery.com/ticket/1294

Ответ 8

Если вам просто нужно значение select, чтобы сериализовать форму или что-то в этом роде, это работает для меня:

$clonedForm.find('theselect').val($origForm.find('theselect').val());

Ответ 9

После 1 часа попыток разных решений, которые не сработали, я создал это простое решение

$clonedItem.find('select option').removeAttr('selected');
$clonedItem.find('select option[value="' + $originaItem.find('select').val() + '"]').attr('selected', 'true');

Ответ 10

@pie6k показать хорошую идею.

Он решил мою проблему. Я немного изменяю его:

$(document).on("change", "select", function(){
    var val = $(this).val();
    $(this).find("option[value=" + val + "]").attr("selected",true);
});

Ответ 11

$(document).on("change", "select", function(){
    original = $("#original");
    clone = $(original.clone());
    clone.find("select").val(original.find("select").val());

});