jQuery不需要的排序数组输出在Chrome和IE (FF工作良好)

jQuery unwanted sorting of array output in Chrome and IE (FF working fine)

本文关键字:FF IE 工作 Chrome 不需要 排序 数组 输出 jQuery      更新时间:2023-09-26

我有一个jquery脚本,它将下拉选择框的选项转换为使用数组的ul列表项。下拉菜单中的每个选项都有一个数字选项值,例如

<option value="12">Size S</option>
<option value="34">Size M</option>
<option value="7">Size L</option>

被转换成像

这样的列表
<ul>
<li class="opt_12">Size S</li>
<li class="opt_34">Size M</li>
<li class="opt_7">Size L</li>
</ul>

在Firefox中,一切都像预期的那样工作,列表项以与下拉选项相同的顺序出现。然而,IE和Chrome似乎按降序自动按选项值排序数组的选项值。不像下拉菜单里的大小顺序S M L XL;例如,我得到一个类似XL M S l的列表

我注意到一件事:当我使用var options = Array;构建数组时,Firefox以正确的顺序显示列表元素,而Chrome和IE则以错误的顺序显示。当我使用var options = [];时,所有三个测试的浏览器都以错误的顺序显示列表。

下面是我用来将下拉菜单转换为列表项的脚本的相关代码:

(function($) {
    $.fn.visualAttribute = function(options) {
        var defaults = {
            useTitle: false,
            attrClicked : function(data) {
                return true;
            },
            attrUpdated : function(data) {
            }
        };
        var settings = $.extend(defaults, options);
        //loop all attributes
        var selectbox_counter = 0;
        return this.each(function() {
            //use counter for a unique class for each wrapper
            selectbox_counter++;
            //store reference to attribute selectbox
            var selectbox = $(this);
            //hide the default dropdown (but keep it in dom for posting the required values)
            $(this).css('position', 'absolute').css('left', '-100000px').show();
            //insert wrapper for options
            var wrapper = $('<ul />')
                    .attr("class", "la_wrapper")
                    .attr("id", "la_wrapper_" + selectbox_counter)
                    .appendTo($(this).parent());
            $(this).parent().append('<div style="clear:both"></div>');
            if (selectbox.attr("id") != "") {
                wrapper.attr("rel", selectbox.attr("id"));
            }
            //store all values of the dropdown in an array
            var options = [];
            var option_counter = 0;
            var description = '';
            selectbox.children('option').each(function() {
                option_counter++;
                if (option_counter == 1) {
                    //first option contains the description, e.g. 'please select size'
                    description = $(this).text();
                }
                //only use option if has a value
                var value = $(this).val();
                if (value != '') {
                    options[value] = ({value : value, text : $(this).text()});
                }
            });
            //loop all stored options and create custom html
            if (options.length) {
                for (var index in options) {
                    if (!isNaN(index)) {
                        var value = index;
                        var text = options[index].text;
                        if (!settings.useTitle) {
                            description = '';
                        }
                        wrapper.append('<li title="' + description + '" class="opt_' + value + '"><a href="#' + value + '">' + text + '</a></li>');
                    }
                }
            }
            //set custom options to same value as current selectbox value (only needed in rare cases)
            var current_value = selectbox.val();
            if (current_value > 0) {
                $("#la_wrapper_" + selectbox_counter + ' li.opt_' + current_value + ' a').addClass('selected');
            }
            //event handler for catching a clicked attribute
            $("#la_wrapper_" + selectbox_counter + ' li a').click(function() {
                var value = $(this).attr("href").split('#')[1];
                //use callback
                if (!settings.attrClicked(options[value])) {
                    return false;
                }
                //set value and class
                selectbox.val(value);
                $("#la_wrapper_" + selectbox_counter + ' .selected').removeClass('selected');
                $(this).addClass('selected');
                //use callback
                settings.attrUpdated(options[value]);
                return false;
            });
        });
    };
    })(jQuery);

我怎么能防止IE和Chrome从"自动排序"的数组和保持/转移在结果列表中的下拉选项的原始顺序?

如果顺序很重要,不要使用选项的value作为数组键,只需使用append(注:使用[],而不是Array -这不是做你认为它正在做的事情)

var options = [];
var option_counter = 0;
var description = '';
selectbox.children('option').each(function() {
    option_counter++;
    //only use option if has a value
    var value = $(this).val();
    if ( value ) {
        options.push({value : value, text : $(this).text()});
    }
});

然后,在循环时,完全去掉if (options.length),并使用常规的for循环:

for (var i=0; i<options.length; i++) {
    var value = options[i].value;
    var text = options[i].text;
    if (!settings.useTitle) {
        description = '';
    }
    wrapper.append('<li title="' + description +
                   '" class="opt_' + value +
                   '"><a href="#' + value + '">' + text +
                   '</a></li>');
}

当你这样迭代数组时:

for (var index in options) 

你没有得到一个保证的顺序,因为你只是迭代一个对象的属性(不是数组索引),根据规范没有保证的顺序。你应该这样写:

for (var i = 0; i < options.length; i++)

按数组顺序迭代数组。第一种形式迭代对象的属性,没有特定的顺序。后一种形式按数组索引顺序迭代数组元素(它给出了实际数组元素的数组顺序)。

另外,用this声明一个数组:

var options = [];

var options = new Array();

你不应该使用:

var options = Array;

您的问题是您使用for...in来枚举属性。

到目前为止,ECMAScript中还没有定义对象(包括数组)属性的枚举顺序。ECMAScript 6建议按以下顺序定义顺序,或多或少:"首先是所有名称看起来像整数的属性,按数字顺序,然后是所有其他属性按它们添加的顺序"。这是Chrome和IE实现的行为。Firefox的行为有点复杂,这取决于你的对象是否是一个数组,如果它是一个数组,它的属性名称使用了什么顺序,数组的长度是多少,以及其他一些事情。

在任何情况下,如果您想按顺序枚举值,只需使用array.push()将它们存储在数组中,然后枚举数组的索引。因此,将options[value] = ...替换为options.push(...),将for (var index in options)替换为for (var index = 0; index < options.length; ++index),并从options[index].value获取值,就像您已经获得文本一样。