Клон не клонує вибрані значення


77

Я не очікував цього, але наступний тест не вдається перевірити клоноване значення:

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

Чи це правильно?

Відповіді:


76

Після подальших досліджень я знайшов цей квиток у системі відстеження помилок JQuery, яка пояснює цю помилку та забезпечує роботу. Мабуть, клонувати вибрані значення занадто дорого, щоб вони не виправили це.

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

Моє використання методу clone було у загальному методі, де що-небудь могло бути клоновано, тому я не впевнений, коли і чи буде вибрано значення для встановлення значення. Тому я додав наступне:

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

13
Вони не пояснюють, чому це "занадто дорого". Я здивований, що через 8 років немає кращого рішення.
Тайлер Коллер

1
Чому б просто не спостерігати за вибором змін і не додавати html selectedатрибут при будь-якій зміні, щоб вона була легко клонована?
Адам П'єтрасіяк,

Скарги на помилки (фіксована URL-адреса: bugs.jquery.com/ticket/1294 ) щодо IE, але це ніде не працює. Перевірте свій браузер за допомогою цієї скрипки: jsfiddle.net/ygmL0k8m/4
hejdav,

Це виправлення не спрацювало для мене. Відповідь нижче (Novalis ') спрацювала.
rigdonmr

36

Ось виправлена ​​версія методу клонування для 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);

1
+1 Це чудовий плагін. Продовжуйте підтримувати цей проект.
Гуман

2
Це чудово працює! Чому я не можу jQuery просто це зробити, розробник використовує Clone розуміє його дороге ... але ми ПОТРІБНІ робити це будь-яким чином .. боже ... стільки часу було витрачено на спроби налагодити це. Дякуємо, це виправлення чудово працює! + пиво
Piotr Kula

хтось може пояснити, як це застосувати? наприклад у випадку, $('.inputPrefixListIx:first').clone().insertAfter('.inputPrefixListIx:last');як це повинно виглядати як розгляд jQuery.fn.clone?
nskalis

не працює з декількома вибраними. Він вибирає лише перший варіант (((
Сергей Сергеев

8

Зробив плагін з відповіді 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);

Тільки коротко перевірив, але, здається, це працює.


6

Мій підхід трохи інший.

Замість того, щоб змінювати виділення під час клонування, я просто спостерігаю за кожною 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(){

Я вважав цей підхід досить розумним, тому майже використав його - однак врешті-решт зрозумів, що він суперечить духу "вибраного" атрибута, який насправді не повинен змінюватися, оскільки він представляє початкове (за замовчуванням) значення для елемент <select>. Джерело: api.jquery.com/prop
etipton

У чому мінус його зміни? тобто змінюється initial selection?
Адам П'єтрасіяк

Практично , я не можу придумати жодного реального негативного TBH. Тільки вказуючи, що технічно це суперечить специфікаціям W3C. Але не головним чином (тому все одно приємне рішення).
etipton 07.03.17


2

Так. Це пов’язано з тим, що властивість 'selected' вузла DOM 'select' відрізняється від атрибута 'selected' параметрів. jQuery жодним чином не змінює атрибути параметрів.

Спробуйте замість цього:

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

Якщо вас дійсно цікавить, як працює функція val, можливо, ви захочете вивчити

alert($.fn.val)

Якщо я використовую val () на виділеному об'єкті, тест також не вдається: test ("клон повинен зберігати значення select", function () {var select = $ ("<select>") .append ($ ("< option> "). val (" 1 ")). append ($ (" <option> ") .val (" 2 ")); $ (select) .val (" 2 "); дорівнює ($ (select) .val (), "2", "очікуй 2"); var cl
начальник7

Це досить дивно, бо це працює для мене в IE 6/7, Firefox 3 та Opera 9. Можливо, щось не так із вашою функцією "дорівнює"? alert (eval ('select = $ ("<select>") .append ($ ("<option>") .val ("1")). append ($ ("<option>") .val ("2 ")); $ (виберіть) .val (" 2 "); $ (виберіть) .val () '));
користувач123444555621

2

Клонування <select>зовсім НЕ копіювати value=власність на<option> с. Тож плагін Марка працює не у всіх випадках.

Щоб виправити, зробіть це перед клонуванням <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.


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

});

0

Якщо вам просто потрібно значення select, щоб серіалізувати форму або щось подібне, це працює для мене:

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

0

Після 1 години випробувань різних рішень, які не спрацювали, я створив це просте рішення

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

0

@ pie6k показати гарну ідею.

Це вирішило мою проблему. Я змінюю його трохи маленьким:

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

0

просто звітуючи. З якоїсь невідомої причини, і хоча це було перше, що я перевірив, і я не змінив свого коду, зараз

$("#selectTipoIntervencion1").val($("#selectTipoIntervencion0").val());

підхід працює. Я не уявляю, чому і якщо це знову перестане працювати, як тільки я щось зміню, але поки я піду з цим. Дякуємо усім за допомогу!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.