必须用 compositionstart/compositionend + paste + input 三重监听才能完整拦截 emoji,因为单靠 oninput 会漏粘贴和输入法上屏前场景,且需处理四字节 emoji 和中文输入法中间态。

直接在 textarea 上用正则替换表情字符,但只靠 oninput 或 onkeyup 会漏掉粘贴、中文输入法上屏前等场景,必须配合 compositionstart/compositionend 和 paste 延迟处理。
为什么 replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, '') 不够用
这个正则能匹配大部分 emoji,但实际使用中会出问题:
- 粘贴整段含 emoji 的文本时,
oninput触发太晚,内容已写入; - 中文输入法「正在打字未上屏」阶段(比如输入「我爱」还没按空格),
compositionstart后的中间态会被误删,导致输入中断; - 部分 emoji 是四字节(如 ??),需用
/[\ud800-\udbff][\udc00-\udfff]/g才能完整匹配; - 某些平台(如微信)会把颜文字(如 (。•́︿•̀。))转成图片,但网页里仍是普通字符,正则无法识别。
正确拦截方案:composition + paste + input 三重监听
核心是分阶段控制:输入法中暂不处理,粘贴后延迟读取,每次输入后立即清洗。示例代码:
function cleanEmoji(el) {
const val = el.value;
// 清除所有 Unicode emoji 区间(含四字节)
const cleaned = val.replace(/[\ud800-\udbff][\udc00-\udfff]|\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, '');
if (cleaned !== val) {
el.value = cleaned;
// 手动触发 input 事件,确保 Layui 表单校验或字数统计同步
el.dispatchEvent(new Event('input', { bubbles: true }));
}
}
// 绑定到 textarea 元素(确保已在 layui.render() 之后)
const $ta = $('.layui-textarea');
$ta.on('input', function() {
cleanEmoji(this);
});
$ta.on('compositionstart', function() {
this.dataset.composing = 'true';
});
$ta.on('compositionend', function() {
delete this.dataset.composing;
cleanEmoji(this);
});
$ta.on('paste', function(e) {
setTimeout(() => {
if (this.dataset.composing !== 'true') {
cleanEmoji(this);
}
}, 0);
});
表格内编辑(edit: "textarea")要放弃内置配置
Layui 表格的 edit: "textarea" 不透传 DOM 事件,也无法绑定 composition 等事件。必须改用 templet 自定义模板:
- 在
cols中去掉edit: "textarea"; - 用
templet: '<textarea lay-key="{{d.LAY_TABLE_INDEX}}" class="layui-textarea">{{d.fieldName}}</textarea>'; - 表格渲染完成后,对所有该类
textarea执行上面的三重监听绑定; - 注意动态插入行(如 addRow)后要重新绑定,否则新行失效。
真正难的不是正则本身,而是输入法状态和事件时机的错位——compositionend 之后才真正上屏,paste 后必须 setTimeout(, 0) 才能读到粘贴内容,这两个点漏掉一个,表情就进去了。


















