直接用<input type="file">上传表情图片后插入富文本,必须通过getSelection()获取选区、range.insertNode()插入图片并range.collapse(false)定位光标;需手动派发input事件、聚焦并滚动可视、添加loading="lazy"和decoding="async"防卡顿,缺一不可。

直接用 <input type="file"> 上传表情图片,但不等于能“插入到富文本中”——绝大多数失败都卡在光标定位、DOM 同步和事件触发这三步上。
contenteditable 中插入图片后光标错乱
用 innerHTML += <img src="..."> 是最常见也最危险的做法:它会重置整个 div 的 DOM 结构,导致光标跳回开头或消失。浏览器不会自动维护选区状态。
- 必须用原生 DOM 操作:先调用
getSelection()获取当前选区,再用range.insertNode(imgEl)插入节点 - 插入后立刻执行
range.collapse(false),把光标移到图片后面,避免连续点击重复插入 - IE11 不支持
insertNode对TextNode,如需兼容,降级用document.execCommand('insertHTML', false, '<img>')并 try/catch 包裹
上传成功但 oninput 事件没触发
contenteditable 元素对图片插入默认不派发 input 事件,React/Vue 的双向绑定或表单校验会完全失效。
- 插入图片后,手动触发:
el.dispatchEvent(new Event('input', { bubbles: true })) - 别依赖
change或blur——它们只在失焦或文件选择时触发,和内容变更无关 - 如果用了防抖的
oninput处理逻辑,确保事件触发时机在 DOM 更新之后(可用setTimeout(..., 0)微任务兜底)
移动端 Safari 表情插入后键盘遮挡输入框
iOS 上 contenteditable 的 focus 行为不可靠,插入后不主动滚动,用户看不到刚输的内容。
立即学习“前端免费学习笔记(深入)”;
- 插入完成后立即执行:
el.focus()+setTimeout(() => el.scrollIntoView({ block: 'nearest' }), 0) - 不要用
behavior: 'smooth',Safari 旧版不支持,且动画期间可能被键盘打断 - 给容器加
scroll-behavior: auto防止全局滚动策略干扰
雪碧图表情上传后渲染卡顿
几百个 <img> 标签一次性渲染,主线程会被图片解码阻塞,尤其低端安卓机滑动掉帧明显。
- 所有表情
<img>必须加loading="lazy"和decoding="async" - 父容器加
will-change: transform提前启用合成层,避免滚动重绘 - 避免用
background-image+sprite.png实现雪碧图——CSS 背景无法被getSelection定位,也不能响应点击插入逻辑
真正难的不是“怎么传”,而是“传完怎么让编辑器承认它是一次有效输入”——事件、选区、焦点、渲染,四个环节只要漏一个,用户就会觉得“点了没反应”。



















