Klonen klont keine ausgewählten Werte

75

Ich habe es nicht erwartet, aber der folgende Test schlägt bei der Überprüfung des geklonten Werts fehl:

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

Ist das richtig?

Chef7
quelle

Antworten:

76

Nach weiteren Recherchen fand ich dieses Ticket im JQuery-Bug-Tracker-System, das den Bug erklärt und eine Problemumgehung bietet. Anscheinend ist es zu teuer, die ausgewählten Werte zu klonen, damit sie nicht behoben werden.

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

Ich habe die Klonmethode in einer generischen Methode verwendet, bei der möglicherweise alles geklont wird. Daher bin ich mir nicht sicher, wann oder ob eine Auswahl zum Festlegen des Werts angezeigt wird. Also habe ich folgendes hinzugefügt:

var selects = $(cloneSourceId).find("select");
$(selects).each(function(i) {
    var select = this;
    $(clone).find("select").eq(i).val($(select).val());
});
Chef7
quelle
13
Sie erklären nicht, warum es "zu teuer" ist. Ich bin überrascht, dass es 8 Jahre später keine bessere Lösung gibt.
Tyler Collier
1
Warum nicht einfach die Auswahl auf Änderungen beobachten und html selectedbei jeder Änderung ein Attribut hinzufügen , damit es einfach geklont werden kann?
Pie6k
Fehler Ticket Beschwerden (feste URL: bugs.jquery.com/ticket/1294 ) gegen IE, aber es funktioniert nirgendwo. Testen Sie Ihren Browesr mit dieser Geige: jsfiddle.net/ygmL0k8m/4
hejdav
Dieser Fix hat bei mir nicht funktioniert. Die folgende Antwort (Novalis ') hat funktioniert.
Rigdonmr
34

Hier ist eine feste Version der Klonmethode für 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);
Novalis
quelle
1
+1 Das ist ein großartiges Plugin. Bitte pflegen Sie dieses Projekt weiter.
Houman
2
Das funktioniert super! Warum kann jQuery es nicht einfach tun, Entwickler, die Clone verwenden, verstehen, dass es teuer ist ... aber wir MÜSSEN es auf irgendeine Weise tun ... jeeez ... so viel Zeit verschwendet, um dies zu debuggen. Danke, dieses Update funktioniert super! + Bier
Piotr Kula
kann jemand erklären, wie man das anwendet? Zum Beispiel für den Fall, $('.inputPrefixListIx:first').clone().insertAfter('.inputPrefixListIx:last');wie es aussehen sollte, wenn man darüber nachdenkt jQuery.fn.clone?
Nskalis
8

Aus der Antwort von Chief7 wurde ein Plugin erstellt:

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

Nur kurz getestet, aber es scheint zu funktionieren.

mpen
quelle
5

Mein Ansatz ist etwas anders.

Anstatt die Auswahl während des Klonens zu ändern, beobachte ich nur jede selectSeite auf ein changeEreignis. Wenn der Wert geändert wird, füge ich selecteddem ausgewählten das erforderliche Attribut hinzu, <option>damit es wird <option selected="selected">. Da die Auswahl jetzt im <option>Markup markiert ist , wird sie übergeben, wenn Sie es tun .clone().

Der einzige Code, den Sie benötigen:

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

Und jetzt können Sie die Auswahl nach Belieben kopieren, und der Wert wird ebenfalls kopiert.

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

Ich denke, diese Lösung ist weniger benutzerdefiniert, sodass Sie sich keine Sorgen machen müssen, wenn Ihr Code beschädigt wird, wenn Sie später etwas ändern.

Wenn Sie nicht möchten, dass es auf jede Auswahl auf der Seite angewendet wird, können Sie die Auswahl in der ersten Zeile wie folgt ändern:

$(document).on("change", "select.select-to-watch", function(){
pie6k
quelle
Ich fand diesen Ansatz ziemlich clever, also habe ich ihn fast verwendet - aber irgendwann wurde mir klar, dass er gegen den Geist des "ausgewählten" Attributs verstößt, das sich eigentlich nicht ändern soll, da es den anfänglichen (Standard-) Wert für das darstellt <select> -Element. Quelle: api.jquery.com/prop
etipton
Was ist der Nachteil einer Änderung? dh. ändern initial selection?
Pie6k
Praktisch kann ich mir keinen wirklichen Nachteil TBH vorstellen. Ich möchte nur darauf hinweisen, dass es technisch gegen die W3C-Spezifikation verstößt. Aber nicht im großen und ganzen (also immer noch eine schöne Lösung).
Etipton
4

Vereinfachung der Antwort von Chief7:

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

Hier ist wieder das jQuery-Ticket: http://bugs.jquery.com/ticket/1294

Collin Anderson
quelle
2

Ja. Dies liegt daran, dass sich die Eigenschaft 'selected' eines DOM-Knotens 'select' vom Attribut 'selected' der Optionen unterscheidet. jQuery ändert die Attribute der Optionen in keiner Weise.

Versuchen Sie stattdessen Folgendes:

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

Wenn Sie wirklich daran interessiert sind, wie die val-Funktion funktioniert, sollten Sie dies untersuchen

alert($.fn.val)
user123444555621
quelle
Wenn ich das val () für das Auswahlobjekt verwende, schlägt der Test ebenfalls fehl: test ("Klon sollte die Werte von select beibehalten", function () {var select = $ ("<select>") .append ($ ("<) Option> "). val (" 1 ")). append ($ (" <option> ") .val (" 2 ")); $ (select) .val (" 2 "); equals ($ (select) .val (), "2", "erwarten 2"); var cl
chef7
Das ist ziemlich seltsam, weil dies für mich in IE 6/7, Firefox 3 und Opera 9 funktioniert. Vielleicht stimmt etwas mit Ihrer "Gleich" -Funktion nicht? alert (eval ('select = $ ("<select>") .append ($ ("<option>") .val ("1")). append ($ ("<option>") .val ("2 ")); $ (select) .val (" 2 "); $ (select) .val () '));
user123444555621
2

Durch das Klonen von a <select>wird die Eigenschaft auf s nicht kopiert . Das Plugin von Mark funktioniert also nicht in allen Fällen.value=<option>

Führen Sie zum Beheben folgende Schritte aus, bevor Sie die <select>Werte klonen :

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

Eine andere Möglichkeit zum Klonen der <select>ausgewählten Option in jQuery 1.6.1 + ...

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

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

Mit letzterem können Sie die <option>Werte nach dem Einstellen von einstellen selectedIndex.

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

});
Hilary Okoro
quelle
0

Wenn Sie nur den Wert der Auswahl benötigen, um das Formular oder ähnliches zu serialisieren, funktioniert dies für mich:

$clonedForm.find('theselect').val($origForm.find('theselect').val());
SkarXa
quelle
0

Nachdem ich 1 Stunde lang verschiedene Lösungen ausprobiert hatte, die nicht funktionierten, habe ich diese einfache Lösung erstellt

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

@ pie6k zeigen eine gute Idee.

Es hat mein Problem gelöst. Ich ändere es ein wenig klein:

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

Ich melde mich nur zurück. Aus irgendeinem gottesfürchtigen unbekannten Grund, und obwohl dies das erste war, was ich getestet habe und ich meinen Code überhaupt nicht geändert habe, jetzt das

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

Ansatz funktioniert. Ich habe keine Ahnung warum oder ob es nicht mehr funktioniert, sobald ich etwas ändere, aber ich werde jetzt damit weitermachen. Vielen Dank an alle für die Hilfe!

David Antelo
quelle