实时同步需监听 input 事件,用 addEventListener 绑定并过滤 isComposing;预览区用 innerText 或转义后的 innerHTML;Layui 中须获取真实 textarea 节点并在 form 渲染后绑定。

textarea 实时同步到预览区,必须监听 input 事件而非 change
change 事件只在 textarea 失焦时触发,根本做不到“边打字边预览”。真正起效的是原生 input 事件——它响应每一次按键、粘贴、删除,包括 Ctrl+Z 撤销。Layui 不劫持这个事件,所以直接绑定 DOM 就行,别绕路用 form.on('submit') 或轮询 val()。
常见错误是把预览区写成 <div></div> 却往里塞 .value,结果没反应。记住:展示类元素(div、span、p)只认 .innerText 或 .textContent;表单控件才用 .value。
- 用
textarea.addEventListener('input', handler),不是oninput属性或 jQuery 的.on('input')(后者在 Layui 重绘后可能失效) - handler 里直接取
e.target.value,别调layui.form().val()——它返回的是 form 缓存值,不是实时 DOM 值 - 预览区若含 HTML 标签(如用户输入了
<b>加粗</b>),才考虑.innerHTML;否则一律用.innerText防 XSS
中文输入法下预览闪动?检查 event.isComposing
用户用拼音输“你好”,在敲“ni”阶段就触发 input,此时 e.target.value 是 “ni”,不是最终内容。不拦截会导致预览区反复显示中间态,UI 抖动、光标错乱。
只需加一行判断:if (e.isComposing) return;。它能准确识别输入法组合过程(Chrome/Firefox/Edge 均支持),比监听 compositionstart/compositionend 更简洁可靠。
- 不要写
if (e.type === 'compositionend')后再同步——这会延迟一拍,用户松手后才更新,失去“实时”意义 - 移动端微信内置浏览器也支持
isComposing,无需额外兼容 - 如果预览逻辑较重(如 markdown 渲染),可在此处加节流,但过滤
isComposing必须放在节流外层
预览区要渲染 HTML?先转义再解析,别直接 innerHTML
若需求是“用户输入 Markdown,预览区实时渲染为 HTML”,不能把原始 textarea 值直接塞进 .innerHTML ——这等于执行任意脚本,XSS 风险极高。安全做法是:先用正则或专用库(如 marked)将 Markdown 转为 HTML 字符串,再对结果做 HTML 转义(保留 <p>、<strong> 等白名单标签),最后赋给 .innerHTML。
- 简单场景可用
DOMPurify.sanitize(htmlString),轻量且适配 Layui 环境 - 纯文本预览(如日志摘要)坚决用
.innerText,连<都自动转义,零风险 - 避免在
input回调里调laytpl渲染——模板编译有开销,频繁触发会卡顿;预编译好模板函数再传值
layui-textarea 初始化后 DOM 才就绪,绑定时机不能错
Layui 渲染 <textarea class="layui-textarea"></textarea> 时,会包裹一层 <div class="layui-textarea">,真实 textarea 被藏在内部。直接 document.getElementById('myArea') 可能取不到——因为 Layui 还没完成 DOM 替换。
- 绑定逻辑必须放在
layui.use(['form'], callback)的callback内,或监听form.render()完成后的回调 - 更稳妥的方式:用
layui.$('#myArea').closest('.layui-textarea').find('textarea')[0]定位真实节点 - 如果表格内嵌 textarea(如
table.render()的cols中配置),需在done回调里绑定,否则初始行的 textarea 尚未生成
真正容易被忽略的点是:预览区若用 laydate、layedit 等组件联动,它们内部也依赖 input 事件,必须确保事件监听顺序和 isComposing 过滤一致,否则一个组件同步了,另一个还卡在中间态。


















