2009-04-13 3 views
60

Je ne m'y attendais pas, mais le test suivant échoue sur la vérification de la valeur clonée:Clone est pas le clonage des valeurs de sélection

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

Est-ce exact?

Répondre

2

Oui. C'est because la propriété 'selected' d'un noeud DOM 'select' diffère de l'attribut 'selected' des options. jQuery ne modifie en aucun cas les attributs des options.

Essayez ceci:

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

Si vous êtes vraiment intéressé par la façon dont la fonction val fonctionne, vous voudrez peut-être examiner

alert($.fn.val) 
+0

Si j'utilise le val() sur l'objet, sélectionnez le test échoue ainsi: \t test ("clone doit conserver les valeurs de sélection", function() { \t \t var select = $ (» ").append ($ ("

59

Après d'autres recherches, j'ai trouvé ce billet dans le JQuery bug tracker système qui explique le bug et fournit un travail autour. Apparemment, il est trop coûteux de cloner les valeurs de sélection pour qu'elles ne le réparent pas.

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

Mon utilisation de la méthode clone était dans une méthode générique où tout peut être cloné, donc je ne sais pas quand ou s'il y aura une sélection pour définir la valeur sur. Alors j'ajouté ce qui suit:

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

Ils n'expliquent pas pourquoi c'est "trop ​​cher". Je suis surpris qu'il n'y ait pas de meilleure solution 8 ans plus tard. –

+1

Pourquoi ne pas simplement regarder les sélections pour les changements et ajouter l'attribut 'html'' selected' à n'importe quel changement afin qu'il soit cloné facilement? – pie6k

+0

Les plaintes de ticket de bogue (URL fixe: https://bugs.jquery.com/ticket/1294) contre IE, mais cela ne fonctionne nulle part. Testez votre browesr par ce violon: https://jsfiddle.net/ygmL0k8m/4/ – hejdav

8

Fait un plugin de réponse de 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); 

seulement testé brièvement, mais il semble fonctionner.

2

Le clonage d'un <select> ne pas copie la propriété value= sur <option> s. Donc, le plugin de Mark ne fonctionne pas dans tous les cas.

Pour corriger, faire ce avant clonage des <select> valeurs:

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

Une autre façon de cloner l'option <select> est sélectionnée, dans jQuery 1.6.1 + ...

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

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

Ce dernier vous permet de définir les <option> valeurs après en réglant le selectedIndex.

22

est ici une version fixe de la méthode clone pour 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 C'est un super plugin. S'il vous plaît continuer à maintenir ce projet. – Houman

+1

Cela fonctionne très bien! Pourquoi jQuery ne peut pas le faire, dev utilise Clone comprendre son coûteuse ... mais nous avons besoin de le faire de toute façon .. jeeez ... tant de temps perdu en essayant de déboguer cela. Merci, cette correction fonctionne très bien! + bière – ppumkin

2

Simplification de la réponse chief7:

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

Encore une fois, voici le billet jQuery: http://bugs.jquery.com/ticket/1294

0

Si vous venez de n EED la valeur de la sélection, pour sérialiser la forme ou quelque chose comme ça, cela fonctionne pour moi:

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

Après 1 heure d'essayer différentes solutions qui ne fonctionne pas, je ne crée cette solution simple

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

Mon approche est un peu différente.

Au lieu de modifier au cours du clonage choisit, je suis en train de regarder tous les select à la page pour l'événement change, puis, si la valeur est modifiée ajouter nécessaire selected attribut sélectionné <option> il devient <option selected="selected">. Comme la sélection est maintenant marquée dans le balisage <option>, elle sera transmise lorsque vous l'aurez .clone().

Le seul code dont vous avez besoin:

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

Et maintenant, vous pouvez copier sélectionner comme vous le souhaitez et il va avoir sa valeur copiée aussi.

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

Je pense que cette solution est moins personnalisée de sorte que vous n'avez pas besoin de vous inquiéter si votre code cassera si vous allez modifier quelque chose plus tard.

Si vous ne voulez pas à appliquer à tous les sélectionner à la page, vous pouvez changer de sélection sur la première ligne comme:

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

Je pensais que cette approche était assez intelligente donc je l'ai presque utilisée - cependant, finalement je me suis rendu compte que cela va à l'encontre de l'attribut "sélectionné", qui n'est pas vraiment supposé changer valeur pour l'élément