done回调是唯一可靠监听点,它在每次渲染完成或数据变更后执行,传入右侧当前选中项的value字符串数组,可据此准确获知移动结果并做差异比对。
transfer.render 的 done 回调是唯一可靠监听点
layui 官方没有提供独立的 onchange 或 onmove 事件钩子,所有左右移动操作(点击箭头、双击、全选转移)最终都会触发 done 回调。它在每次渲染完成或数据变更后执行,且传入当前右侧选中项的 value 数组 —— 这是你能拿到的最准、最及时的“移动结果”。
常见错误是试图监听中间按钮的 click 事件或复选框 change 事件:这些操作本身不直接反映最终状态,且容易漏掉双击转移、全选转移等路径。
-
done是唯一被文档明确支持的响应式回调,位于transfer.render()配置中 - 回调参数为
(value, elem),其中value是右侧当前所有选中项的value字符串数组 - 不要在
done中重复调用transfer.getData(id)—— 此时value就是最新结果,再查是冗余且可能滞后 - 若需区分“左→右”还是“右→左”,只能靠前后两次
done的value差异比对(见下一条)
如何判断是左移还是右移:缓存上一次 value 做 diff
因为 done 只告诉你“现在右边有哪些”,不告诉你是怎么来的,所以必须自己记一笔上一次的状态。典型做法是在闭包或模块作用域里维护一个变量,每次 done 触发时对比变化。
示例逻辑:
var lastValue = [];
transfer.render({
elem: '#test-transfer',
data: [...],
done: function(value, elem) {
var added = value.filter(v => !lastValue.includes(v));
var removed = lastValue.filter(v => !value.includes(v));
if (added.length > 0) {
console.log('新增到右侧:', added); // 即左→右
}
if (removed.length > 0) {
console.log('从右侧移出:', removed); // 即右→左
}
lastValue = value; // 更新快照
}
});
- 注意:
value是字符串数组,不是对象,diff 必须基于value字段做,不能用引用比较 - 首次
done触发时lastValue为空数组,此时所有value都算“新增”,对应初始化带value参数的场景 - 该 diff 方式在高频率操作下仍稳定,但不要在
done里做复杂 DOM 操作或异步请求,避免阻塞 UI
别碰原生 click / dblclick 监听中间按钮
有人会尝试用 $('body').on('click', '.layui-transfer-btn .layui-icon-right', ...) 这类选择器去监听箭头按钮,这非常危险:
- 按钮 DOM 是动态生成的,且在
transfer.render()多次调用后可能重复绑定 - 双击转移(double-click item)完全绕过按钮,监听不到
- 全选转移(.layui-transfer-header input[type=checkbox])也无对应按钮事件
- Layui 内部未承诺按钮结构稳定性,升级后可能失效
真正需要拦截移动行为(比如加权限校验、防误操作),应该在 done 里做业务判断,并配合 transfer.set() 回滚 —— 而不是抢在底层动作前干预。
transfer.getData() 不是事件监听,是状态快照工具
transfer.getData(id) 是一个同步取值方法,返回当前右侧所有数据项的完整对象数组(含 value、title 等)。它常被误当成事件使用,但它本身不监听、不响应、不触发任何动作。
- 只应在你需要“此刻完整数据结构”时调用,比如提交表单前拼装 payload
- 若在
done回调里又调一次transfer.getData(id),属于重复劳动 ——done的value参数已足够用于大多数判断 - 它的性能开销略高于读
value数组,因需遍历内部映射查找原始数据对象
真正复杂的顺序管理(如拖拽重排)必须另起一套逻辑,done 仅负责捕获“哪些项最终在右边”,这是所有后续处理的起点。别指望靠监听按钮或 checkbox 来覆盖这个事实。











