原生 html 没有 transfer 组件,因其需双向数据同步、拖拽转移、搜索过滤等状态驱动逻辑,而 select/option 无法直接响应;可用双 select + 按钮实现基础穿梭,关键在行为正确性而非样式相似。

为什么原生 HTML 没有 Transfer 组件
因为 Transfer 是典型的状态驱动交互组件,需要双向数据同步、拖拽/点击转移、搜索过滤、全选/反选逻辑,原生 HTML 的 select 和 option 无法直接响应这些操作。浏览器不提供 moveToRight 或 getSelectedKeys 这类方法——你得自己监听、收集、更新 DOM 并维护状态。
用两个 select + button 实现基础穿梭逻辑
这是最轻量、无依赖的实现方式,适合简单表单或遗留系统嵌入。关键不是“画得像”,而是“行为对”:选中项能正确移动、不重复、可回退。
- 左右两个
<select multiple></select>,分别加id="left-list"和id="right-list" - 移动按钮绑定
click事件,用selectedOptions获取当前选中项(不是options或value) - 逐个把
option元素从一个select移动到另一个:rightList.appendChild(option)—— 注意是appendChild,不是克隆;移动后原位置自动移除 - 清空选中状态要用
option.selected = false,不能只靠select.value = ""
const left = document.getElementById('left-list');
const right = document.getElementById('right-list');
document.getElementById('to-right').onclick = () => {
Array.from(left.selectedOptions).forEach(opt => right.appendChild(opt));
};
用 Array 管理数据比直接操作 option 更可靠
当选项带额外元数据(如 id、disabled、isGroup),或需支持搜索/分页时,硬操作 DOM 很快失控。此时应把数据和 UI 分离:
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 维护两个数组:
leftDataSource和rightDataSource,每个元素是形如{ key: 'user-1', label: '张三', disabled: false }的对象 - 渲染函数根据数组生成
option,用data-key属性标记对应关系 - 移动时只修改数组(
filter+concat),再重新渲染整个select—— 别试图 patch 单个option - 注意:重新渲染会丢失当前焦点和滚动位置,若体验敏感,可用
scrollIntoView({ preventScroll: true })保位置
别忽略键盘操作和可访问性(a11y)
纯鼠标点按钮的穿梭框在屏幕阅读器下几乎不可用,且无法用 Tab + Space 操作。必须补足:
- 给每个
select加aria-label(如"待选用户列表") - 移动按钮加
aria-describedby指向说明文字,例如<p id="transfer-desc">按住 Ctrl 可多选</p> - 监听
keydown:当焦点在左列表且按下→键时,触发向右移动;同理支持←、↑、↓ - 禁用状态的
option必须设disabled属性,不能只靠 CSS 灰掉
复杂点在于:键盘操作要和鼠标操作共享同一套数据源和移动逻辑,否则容易出现 UI 和状态不一致 —— 比如鼠标点了一次,键盘又点一次,结果重复添加。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










