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

Select2分组选择组件鼠标悬停滚动自动回顶问题求助

修复Select2 Optgroup分组选择时滚动条自动回顶的问题

我之前在做Select2分组多选的时候也碰到过一模一样的滚动跳顶问题,折腾了好一会儿才找到根源,咱们来一步步拆解解决它:

问题根源分析

看你的代码,主要有两个核心问题导致滚动条自动回顶:

  1. 分组没有唯一ID:你的分组id都是空字符串或者重复的(比如两个Other分组),Select2在渲染下拉列表时,会把相同ID的元素视为同一个,每次执行query重新生成列表时,会销毁旧DOM再创建新的,这就导致滚动位置直接重置。
  2. 不必要的下拉列表重渲染:每次鼠标进入选择区域,Select2都会触发query函数重新过滤分组,而你返回的是新生成的数组对象,Select2会认为数据完全更新,从而重新渲染整个下拉框,滚动条自然回到顶部。

修复方案

针对这两个问题,咱们从三个方面调整代码:

1. 给每个分组设置唯一ID

确保每个分组的id属性是唯一的,Select2需要这个标识来稳定渲染DOM元素,避免重复创建。

2. 优化查询过滤逻辑

简化过滤逻辑,基于原始数据进行过滤,而不是每次生成新数组,减少不必要的DOM重绘。

3. 记忆滚动位置

通过Select2的opened和closed事件,保存和恢复下拉框的滚动位置,彻底解决跳顶问题。

修改后的完整代码

<input type="hidden" id="fruitSelect" value="" style="width:300px;" /><br /> <br /> 
<button type="button" id="showValue">Show Value</button><br /> <br /> 
<div id="output"> </div>
// 给每个分组添加唯一ID,同时修正子项重复ID的问题
var FRUIT_GROUPS = [ 
  { id: 'group-citrus', text: 'Citrus', children: [ 
      { id: 'c1', text: 'Grapefruit' }, 
      { id: 'c2', text: 'Orange' }, 
      { id: 'c3', text: 'Lemon' }, 
      { id: 'c4', text: 'Lime' } 
    ] 
  }, 
  { id: 'group-other', text: 'Other', children: [ 
      { id: 'o1', text: 'Apple' }, 
      { id: 'o2', text: 'Mango' }, 
      { id: 'o3', text: 'Banana' } 
    ] 
  }, 
  { id: 'group-other2', text: 'Other2', children: [ 
      { id: 'o1-2', text: 'Apple2' }, 
      { id: 'o2-2', text: 'Mango2' }, 
      { id: 'o3-2', text: 'Banana2' } 
    ] 
  }, 
  { id: 'group-other3', text: 'Other3', children: [ 
      { id: 'o1-3', text: 'Apple3' }, 
      { id: 'o2-3', text: 'Mango3' }, 
      { id: 'o3-3', text: 'Banana3' } 
    ] 
  }, 
  { id: 'group-other4', text: 'Other4', children: [ 
      { id: 'o1-4', text: 'Apple4' }, 
      { id: 'o2-4', text: 'Mango4' }, 
      { id: 'o3-4', text: 'Banana4' } 
    ] 
  } 
]; 

$('#fruitSelect').select2({
  multiple: true,
  placeholder: "Select fruits...",
  data: FRUIT_GROUPS,
  query: function(options) {
    var selectedIds = options.element.select2('val') || [];
    // 过滤出至少有一个未选中子项的分组
    var selectableGroups = FRUIT_GROUPS.filter(function(group) {
      return group.children.some(child => !selectedIds.includes(child.id));
    });
    options.callback({ results: selectableGroups });
  }
}).on('select2-selecting', function(e) {
  var $select = $(this);
  // 仅处理分组项(判断是否包含children属性)
  if (e.choice && e.choice.children) {
    e.preventDefault();
    var currentData = $select.select2('data') || [];
    // 避免重复添加已选中的子项
    e.choice.children.forEach(child => {
      if (!currentData.some(item => item.id === child.id)) {
        currentData.push(child);
      }
    });
    $select.select2('data', currentData);
    $select.select2('close');
  }
}).on('select2-opened', function() {
  // 打开下拉框时恢复之前的滚动位置
  var $dropdown = $(this).data('select2').dropdown.$dropdown;
  var scrollTop = $(this).data('select2ScrollTop') || 0;
  // 用setTimeout确保DOM渲染完成后再设置滚动位置
  setTimeout(() => {
    $dropdown.scrollTop(scrollTop);
  }, 0);
}).on('select2-closed', function() {
  // 关闭下拉框时保存当前滚动位置
  var $dropdown = $(this).data('select2').dropdown.$dropdown;
  $(this).data('select2ScrollTop', $dropdown.scrollTop());
});

$('#showValue').click(function() {
  $('#output').text($('#fruitSelect').val());
});

效果说明

修改后,你可以测试以下场景:

  • 选择Mango3后,滚动到下拉列表底部
  • 关闭下拉框后再次打开,或者鼠标在选择区域内移动
  • 滚动条会保持在之前的位置,不会自动回到顶部

另外我还顺便修正了子项ID重复的问题(比如多个o1),这也会避免Select2选中时的冲突问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:18:57