Select2重选选项抛出Cannot read properties of undefined (reading 'id')错误
Select2 重选值时报错
Cannot read properties of undefined (reading 'id') 修复方案 问题根因
从报错栈定位到的setClasses方法逻辑可见,报错直接原因是遍历下拉选项节点时,从DOM节点读取绑定的选项数据得到undefined,后续访问c.id时触发类型错误。该问题和组件是否放在Bootstrap Modal内无强绑定关系,Modal场景仅会提升问题出现概率,核心触发原因共三类:
- 选项数据格式不合法:Select2内部约定所有选项(静态option、动态加载项、默认选中项)必须携带非空唯一的
id字段与展示用的text字段,任意一个选项缺失id,在做选中态匹配时都会触发该报错。 - 组件状态不同步:直接操作原生select节点增删option、修改selected属性后,未调用Select2的同步方法更新内部缓存数据,导致组件持有的选项列表和实际DOM绑定的数据不一致,遍历到未绑定数据/已销毁的选项节点时读不到有效数据。
- 层级/版本冲突:旧版Select2(4.0.12及以下)默认把下拉面板append到body节点,在Bootstrap Modal等带z-index层级、全局事件拦截的容器内使用时,会出现事件绑定丢失、选项data未正确挂载的问题;搭配jQuery 2.1.x版本时,jQuery本身的data缓存泄漏问题也会导致节点绑定的数据丢失。
可行解决方案
按排查优先级从高到低处理:
- 校验全量选项格式
静态渲染的option标签必须携带value属性(对应Select2内部的id字段),禁止出现无value的空option:
动态传入的data数组、ajax接口返回的选项列表,每一项必须包含非空<!-- 错误写法 --> <!-- <option>测试选项</option> --> <!-- 正确写法 --> <option value="1">测试选项</option>id和text字段,禁止传null、undefined、空字符串作为id值,若需要保留空选项,统一将id设为0或约定的特殊字符串如'__empty__':// 错误写法 // [{text: '选项1'}] // 正确写法 const optionList = [ {id: '1', text: '选项1'}, {id: '2', text: '选项2'} ] - 修正状态同步逻辑
所有对Select2选项列表、选中值的修改,操作完原生select节点后必须主动触发change事件同步组件内部状态:
禁止直接操作Select2动态生成的下拉面板DOM,所有配置、数据修改都通过原生select节点或Select2官方API完成。// 修改选中值后同步 $('#target-select').val('2').trigger('change') // 追加新选项后同步 $('#target-select').append(new Option('新选项', '3', false, false)).trigger('change') - 解决层级/版本冲突
若在Bootstrap Modal内使用Select2,初始化时显式指定dropdownParent为当前Modal节点,避免下拉面板被append到body导致的事件、数据绑定丢失:
版本兼容问题优先升级Select2到4.0.13及以上稳定版,jQuery升级到3.x版本,不要使用第三方二次修改过的Select2压缩包。$('#target-select').select2({ dropdownParent: $('#current-bootstrap-modal'), // 其余配置项 }) - 兜底兼容方案
如果暂时无法定位具体丢数据的环节,可以在Select2加载完成后重写setClasses方法增加空值判断,从逻辑上跳过无效选项的处理:$.fn.select2.amd.require(['select2/results'], function (Results) { const originalSetClasses = Results.prototype.setClasses Results.prototype.setClasses = function () { const context = this this.data.current(function (selectedList) { const validSelectedIds = $.map(selectedList || [], function (item) { return item?.id ? item.id.toString() : '' }) const $optionNodes = context.$results.find('.select2-results__option[aria-selected]') $optionNodes.each(function () { const $currentOpt = $(this) const optData = $.data(this, 'data') // 无有效绑定数据的选项直接跳过 if (!optData || optData.id === undefined) return const currentId = optData.id.toString() const isSelected = (optData.element && optData.element.selected) || (!optData.element && $.inArray(currentId, validSelectedIds) > -1) $currentOpt.attr('aria-selected', isSelected ? 'true' : 'false') }) const $activeNode = $optionNodes.filter('[aria-selected=true]') const $targetNode = $activeNode.length > 0 ? $activeNode.first() : $optionNodes.first() $targetNode.trigger('mouseenter') }) } })
内容的提问来源于stack exchange,提问作者Tebby
相关产品推荐
相关产品推荐

