原生HTML+JS可实现轻量emoji选择器:通过Unicode字符直接渲染,分离处理textarea(selectionStart/End)与contenteditable(getSelection/createRange)的光标插入逻辑,内联精简JSON数据源,兼容现代浏览器且零依赖。

用原生 HTML + JS 实现轻量 emoji 选择器,不依赖第三方库
直接上手就能用,不用引入 emoji-picker-element 或 gemoji 这类重型组件。核心是利用 Unicode 表情字符本身可渲染、可复制的特性,配合 DOM 事件控制显示/隐藏和插入逻辑。
关键判断:如果你只需要在 textarea 或 contenteditable 元素里点一下插入 emoji,且支持主流现代浏览器(Chrome 90+、Firefox 95+、Safari 15.4+),纯原生方案更可控、无兼容包袱、体积几乎为零。
监听点击并插入 emoji 到光标位置(textarea / contenteditable)
很多人卡在「点完 emoji 后插不到光标处」,本质是没正确读取/恢复光标位置。textarea 和 contenteditable 的 API 不同,必须分开处理。
-
textarea用selectionStart/selectionEnd获取位置,用字符串拼接 +setSelectionRange恢复 -
contenteditable需用window.getSelection()和document.createRange(),否则会丢失格式或插入到页面顶部 - 插入后要主动触发
input事件(尤其 React/Vue 中监听input的场景),否则视图可能不同步
示例(textarea 场景):
function insertAtCursor(textarea, emoji) {
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const value = textarea.value;
const newValue = value.slice(0, start) + emoji + value.slice(end);
textarea.value = newValue;
textarea.setSelectionRange(start + emoji.length, start + emoji.length);
textarea.dispatchEvent(new Event('input', { bubbles: true }));
}
emoji 数据源怎么选:本地 JSON 还是动态加载?
别直接硬编码几百个 emoji —— 维护成本高、体积大、无法搜索。但也不建议实时 fetch https://cdn.jsdelivr.net/npm/emoji-datasource@latest/,首屏延迟明显且受 CDN 稳定性影响。
推荐做法:按需导出精简 JSON(比如只含常用 200 个 + 分组信息),内联为 JS 对象或作为静态 JSON 文件加载。用 emoji-datasource 工具生成时加 --categories 参数保留分组字段,方便渲染 tab 切换。
- 避免用
native字段(含变体,长度不一),优先用unified+toEmoji()转换(可用unicode-emoji库或简单正则) - 注意某些 emoji 在 Windows Chrome 下渲染为图片(如 ??),但实际仍是文本,不影响插入逻辑
- 搜索功能只需对
name和short_names做includes(),无需全文索引
移动端点击穿透、键盘遮挡、focus 失败问题
iOS Safari 是重灾区:点击 emoji 后输入框失焦、软键盘收起、emoji 插入到页面 body 末尾。这不是 bug,是 Safari 对 contenteditable + 动态 DOM 的策略限制。
- 确保 emoji 面板是
position: absolute且父容器有transform: translateZ(0)或will-change: transform,避免被 iOS 层级机制误判 - 不在
touchend里直接调用insertAtCursor,改用setTimeout(..., 0)延迟一帧,让 focus 状态稳定 - 对
contenteditable元素,插入前手动element.focus(),再getSelection().collapse(element, 0)强制锚点 - Android WebView 下若遇到光标跳到开头,检查是否用了
innerHTML = ...替换内容 —— 改用textContent或insertAdjacentText
复杂点在于:iOS 上 emoji 面板如果用 fixed 定位,滚动时可能悬浮错位;用 absolute 又得监听 scroll 手动更新 top/left。实际项目中,多数选择放弃 fixed,接受面板随输入框一起滚动。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











