2009-04-13 32 views
60

Tôi không mong đợi nó nhưng các thử nghiệm sau thất bại trên tờ séc giá trị nhân bản:Clone không nhân bản chọn giá trị

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

là quyền này?

Trả lời

2

Có. Đây là because thuộc tính 'đã chọn' của nút DOM 'chọn' khác với thuộc tính 'đã chọn' của các tùy chọn. jQuery không sửa đổi các thuộc tính của tùy chọn theo bất kỳ cách nào.

Hãy thử điều này thay vì:

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

Nếu bạn thực sự quan tâm đến cách chức năng val hoạt động, bạn có thể muốn xem xét

alert($.fn.val) 
+0

Nếu tôi sử dụng val() trên đối tượng lựa chọn thử nghiệm thất bại cũng như: \t thử nghiệm ("bản sao nên giữ lại giá trị của chọn", function() { \t \t var chọn = $ (" ").nối thêm ($ ("

59

Sau khi nghiên cứu sâu hơn tôi thấy vé này trong JQuery hệ thống theo dõi lỗi giải thích lỗi và cung cấp một công việc xung quanh. Rõ ràng, nó là quá đắt để sao chép các giá trị lựa chọn để họ sẽ không sửa chữa nó.

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

sử dụng của tôi về các phương pháp nhân bản là một phương pháp chung chung mà bất cứ điều gì có thể được nhân bản vì vậy tôi không chắc chắn khi nào hoặc nếu có sẽ là một lựa chọn để thiết lập giá trị trên. Vì vậy, tôi nói thêm như sau:

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

Họ không giải thích tại sao nó "quá đắt". Tôi ngạc nhiên không có giải pháp tốt hơn 8 năm sau đó. –

+1

Tại sao không chỉ xem các lựa chọn cho các thay đổi và thêm thuộc tính 'html'' selected' vào bất kỳ thay đổi nào nên nó sẽ được nhân bản một cách dễ dàng? – pie6k

+0

Khiếu nại về vé lỗi (URL cố định: https://bugs.jquery.com/ticket/1294) chống lại IE, nhưng nó không hoạt động ở đâu cả. Kiểm tra browesr của bạn bằng cách này fiddle: https://jsfiddle.net/ygmL0k8m/4/ – hejdav

8

Made một plugin ra câu trả lời chief7 của:

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

Chỉ thử nghiệm nó một thời gian ngắn, nhưng có vẻ như để làm việc.

2

Nhân bản một <select> không không copy value= tài sản trên <option> s. Vì vậy, plugin của Mark không hoạt động trong mọi trường hợp.

Để khắc phục, làm được điều này trước khi nhân bản <select> giá trị:

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

Một cách khác nhau để sao chép mà <select> tùy chọn được chọn, trong jQuery 1.6.1 + ...

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

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

Loại thứ hai cho phép bạn đặt các giá trị <option>sau khi đặt selectedIndex.

22

Dưới đây là một phiên bản cố định của phương pháp clone cho 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 Đó là một plugin tuyệt vời. Vui lòng tiếp tục duy trì dự án này. – Houman

+1

Công trình này tuyệt vời! Tại sao không thể jQuery chỉ làm điều đó, dev sử dụng Clone hiểu nó đắt tiền ... nhưng chúng tôi CẦN làm điều đó bất kỳ cách nào .. jeeez ... rất nhiều thời gian lãng phí cố gắng để gỡ lỗi này. Cảm ơn, sửa chữa này hoạt động tuyệt vời! + bia – ppumkin

2

Đơn giản hoá các câu trả lời chief7 của:

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

Một lần nữa, đây là vé jQuery: http://bugs.jquery.com/ticket/1294

0

Nếu bạn chỉ cần n EeD giá trị của lựa chọn, sắp đặt từng hình thức hoặc một cái gì đó giống như nó, điều này làm việc cho tôi:

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

Sau 1 giờ cố gắng giải pháp khác nhau mà không làm việc, tôi đã tạo ra giải pháp đơn giản này

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

Cách tiếp cận của tôi hơi khác một chút.

Thay vì sửa đổi Selects trong nhân bản, tôi chỉ theo dõi từng select trên trang cho change sự kiện, và sau đó, nếu giá trị được thay đổi tôi thêm cần thiết selected thuộc tính để chọn <option> để nó trở thành <option selected="selected">. Vì lựa chọn hiện được đánh dấu trong đánh dấu của <option>, nó sẽ được chuyển khi bạn sẽ .clone() nó.

Mã duy nhất bạn cần:

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

Và bây giờ, bạn có thể sao chép chọn bất kỳ cách nào bạn muốn và nó sẽ có giá trị nó sao chép quá.

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

Tôi nghĩ rằng giải pháp này là ít tùy chỉnh do đó bạn không cần phải lo lắng nếu mã của bạn sẽ phá vỡ nếu bạn sẽ sửa đổi một cái gì đó sau này.

Nếu bạn không muốn nó được áp dụng cho tất cả các lựa chọn trên trang, bạn có thể thay đổi selector trên dòng đầu tiên như:

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

Tôi nghĩ cách tiếp cận này khá thông minh nên tôi gần như đã dùng nó - tuy nhiên, cuối cùng tôi nhận ra rằng nó đi ngược lại tinh thần của thuộc tính "được chọn", không thực sự thay đổi vì nó đại diện cho ban đầu (mặc định) giá trị cho phần tử