【问题标题】:Clone isn't cloning select values克隆不是克隆选择值
【发布时间】:2010-10-19 01:45:39
【问题描述】:

我没想到,但克隆值检查时以下测试失败:

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

是这样吗?

【问题讨论】:

    标签: jquery


    【解决方案1】:

    经过进一步研究,我在 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());
    });
    

    【讨论】:

    • 他们没有解释为什么它“太贵了”。我很惊讶 8 年后没有更好的解决方案。
    • 为什么不只是观察选择的变化并在任何变化上添加html selected 属性以便轻松克隆?
    • 针对 IE 的错误票投诉(固定 URL:bugs.jquery.com/ticket/1294),但它在任何地方都不起作用。用这个小提琴测试你的浏览器:jsfiddle.net/ygmL0k8m/4
    • 这个修复对我不起作用。以下答案(Novalis')有效。
    【解决方案2】:

    这是 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 这是一个很棒的插件。请继续维护这个项目。
    • 这很好用!为什么 jQuery 不能做到这一点,开发人员使用 Clone 了解它的昂贵......但我们需要以任何方式做到这一点......天哪......浪费了这么多时间试图调试这个。谢谢,这个修复效果很好! +啤酒
    • 有人能解释一下如何应用吗?例如,在$('.inputPrefixListIx:first').clone().insertAfter('.inputPrefixListIx:last'); 的情况下,考虑到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);
    

    只是简单地测试了一下,但它似乎可以工作。

    【讨论】:

    • 非常感谢,这看起来是一个通用的解决方案。但是如何与 JQuery 一起实现呢?
    【解决方案4】:

    我的方法有点不同。

    我不是在克隆过程中修改选择,而是在页面上查看每个selectchange 事件,然后,如果值发生更改,我将所需的selected 属性添加到选定的&lt;option&gt;,因此它变为@ 987654326@。由于选择现在标记在&lt;option&gt; 的标记中,当您.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(){
    

    【讨论】:

    • 我认为这种方法非常聪明,所以我几乎使用了它——然而,最终我意识到它违背了“selected”属性的精神,它不应该真正改变它所代表的
    • 改变它的缺点是什么? IE。改变initial selection?
    • 实际上,我想不出任何真正的缺点 TBH。只是指出技术上它违反了 W3C 规范。但不是主要方式(所以仍然是一个不错的解决方案)。
    【解决方案5】:

    Chief7 的回答简化:

    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

    【讨论】:

      【解决方案6】:

      是的。这是because'select' DOM 节点的'selected' 属性与选项的'selected' 属性不同。 jQuery 不会以任何方式修改选项的属性。

      试试这个:

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

      如果您真的对 val 函数的工作原理感兴趣,您可能需要研究一下

      alert($.fn.val)
      

      【讨论】:

      • 如果我在选择对象上使用 val(),测试也会失败:test("clone should retain values of select", function() { var select = $("
      • 这很奇怪,因为这适用于我在 IE 6/7、Firefox 3 和 Opera 9 中。也许你的“等于”函数有问题? alert(eval(' select = $("
      【解决方案7】:

      克隆&lt;select&gt; 不会复制&lt;option&gt;s 上的value= 属性。所以 Mark 的插件并不是在所有情况下都能正常工作。

      要修复,请在克隆&lt;select&gt; 值之前执行此操作:

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

      在 jQuery 1.6.1+ 中克隆 &lt;select&gt; 选项的另一种方法...

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

      后者允许您在设置selectedIndex 之后设置&lt;option&gt;

      【讨论】:

        【解决方案8】:
        $(document).on("change", "select", function(){
            original = $("#original");
            clone = $(original.clone());
            clone.find("select").val(original.find("select").val());
        
        });
        

        【讨论】:

          【解决方案9】:

          如果您只需要选择的值,序列化表单或类似的东西,这对我有用:

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

          【讨论】:

            【解决方案10】:

            在尝试了 1 小时但不起作用的不同解决方案后,我确实创建了这个简单的解决方案

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

            【讨论】:

              【解决方案11】:

              @pie6k 展示了一个好主意。

              它解决了我的问题。我把它改小一点:

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

              【讨论】:

                【解决方案12】:

                只是汇报。出于某种未知的原因,尽管这是我测试的第一件事,而且我没有更改任何代码,但现在

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

                方法有效。我不知道为什么或者一旦我改变某些东西它会再次停止工作,但我现在要继续这样做。谢谢大家的帮助!

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-09-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多