You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jQuery项目排名选择框自动移位脚本修复及优化方案咨询

排名选择框自动调整逻辑的Bug修复与优化方案

问题背景

我有一个HTML页面,通过select框让用户给项目排名,需求是当用户重复选择同一排名时自动调整其他项目的排名:比如把某项目设为第1名,原第1名自动变第2名;新选第1名时,原第1、2名依次后移。现有脚本多数场景正常,但存在bug:当项目1被选为第2名,项目2被选为第1名时,项目1会被自动设为第3名,此时第2名空着。

现有代码

var previousValue = 0;
$("select[class='myclass']").on('focusin', function(){
     previousValue = $(this).val();
});

$("select[class='myclass']").on('change', function (event) { 
    var prevValue = parseInt(previousValue);
    var selectedValue = parseInt($(this).val());
    var selectedId = $(this).attr('id');

    $('#' + selectedId + " option").removeAttr("selected");
    $('#' + selectedId + " option[value=\""+selectedValue+"\"]").attr('selected', true);

    $("select[class='myclass']").each(function (index, element) { 
           var eval = parseInt($('#' + element.id).val());
            if (prevValue !== 0 && selectedValue !== 0) {
                if (eval >= selectedValue && (eval < prevValue) && element.id !== selectedId) {
                       var b = eval + 1;
                        if (b <= 3)
                            $('#' + element.id).prop('selectedIndex', b);
                        $('#' + element.id + " option").removeAttr("selected");
                        $('#' + element.id + " option[value=\"" + b + "\"]").attr('selected', true);

                }
                else if (eval <= selectedValue && (eval > prevValue) && element.id !== selectedId) {
                       var b = eval - 1;
                       $('#' + element.id).prop('selectedIndex', b);
                       $('#' + element.id + " option").removeAttr("selected");
                       $('#' + element.id + " option[value=\"" + b + "\"]").attr('selected', true);
                            }
                }
                else if (prevValue == 0) {
                            if (selectedValue > 0) {
                                if (eval >= selectedValue && element.id !== selectedId) {
                                    var b = eval + 1;
                                    if (b <= 3) {
                                        $('#' + element.id).prop('selectedIndex', b);

                                        $('#' + element.id + " option").removeAttr("selected");
                                        $('#' + element.id + " option[value=\"" + b + "\"]").attr('selected', true);
                                    }
                                }
                            }
                }
        });
 });

Bug原因分析

现有逻辑的判断条件存在区间覆盖漏洞,且多个select共享单个previousValue变量,容易导致状态混乱。比如双向交换排名的场景:项目1从第1名改到第2名时,项目2会被挤到第3名;当项目2再改回第1名时,项目1的当前排名(2)处于selectedValue(1)和prevValue(3)之间,会被错误地加1变成3,最终导致第2名空缺。

Bug修复方案

核心思路是给每个select单独记录上一次选中值,同时明确区分排名移动方向(向前/向后),精准调整对应区间内的其他排名:

// 用对象存储每个select的上一次选中值,避免共享变量冲突
var previousValues = {}; 
$("select.myclass").on('focusin', function(){
    previousValues[this.id] = parseInt($(this).val());
});

$("select.myclass").on('change', function () { 
    var $select = $(this);
    var selectId = this.id;
    var prevValue = previousValues[selectId];
    var selectedValue = parseInt($select.val());
    
    // 值未变化则直接返回
    if (prevValue === selectedValue) return;

    // 处理排名调整逻辑
    $("select.myclass").not($select).each(function(){
        var $elem = $(this);
        var elemRank = parseInt($elem.val());
        
        if (prevValue < selectedValue) {
            // 当前元素向后移动:所有在[prevValue+1, selectedValue]区间的排名减1
            if (elemRank > prevValue && elemRank <= selectedValue) {
                $elem.val(elemRank - 1);
            }
        } else {
            // 当前元素向前移动:所有在[selectedValue, prevValue-1]区间的排名加1
            if (elemRank >= selectedValue && elemRank < prevValue) {
                $elem.val(elemRank + 1);
            }
        }
    });

    // 更新当前select的上一次值
    previousValues[selectId] = selectedValue;
});

修复说明:

  1. 用previousValues对象存储每个select的历史选中值,避免多元素状态冲突。
  2. 按移动方向划分调整区间,精准定位需要修改的其他排名,覆盖包括双向交换在内的所有场景。
  3. 简化DOM操作,直接用val()方法设置选中值,替代繁琐的attr/prop操作。

更优实现方案

采用数据驱动的方式,先维护一份排名数据,再同步到DOM,彻底分离数据逻辑与页面操作:

// 初始化排名数据:key为select的id,value为当前排名
var rankData = {};
$("select.myclass").each(function(){
    rankData[this.id] = parseInt($(this).val());
});

$("select.myclass").on('change', function () { 
    var selectId = this.id;
    var newRank = parseInt($(this).val());
    var oldRank = rankData[selectId];

    if (newRank === oldRank) return;

    // 调整冲突的排名数据
    Object.keys(rankData).forEach(function(id){
        if (id === selectId) return;
        var currentRank = rankData[id];
        
        if (oldRank < newRank) {
            // 元素向后移:缩小区间内的排名
            if (currentRank > oldRank && currentRank <= newRank) {
                rankData[id] = currentRank - 1;
            }
        } else {
            // 元素向前移:扩大区间内的排名
            if (currentRank >= newRank && currentRank < oldRank) {
                rankData[id] = currentRank + 1;
            }
        }
    });

    // 更新当前元素的排名数据
    rankData[selectId] = newRank;

    // 将数据同步到DOM
    Object.keys(rankData).forEach(function(id){
        $("#" + id).val(rankData[id]);
    });
});

该方案优势:

  • 数据与DOM分离,逻辑更清晰,便于维护和扩展(比如支持动态添加排名项)。
  • 减少频繁DOM查询,性能更优。
  • 核心逻辑集中在数据处理,页面仅做同步操作,降低耦合度。

内容的提问来源于stack exchange,提问作者Eray Balkanli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 15:19:32