实现所见即所得编辑器的核心技术与实战经验分享
所见所得效果踩坑记:从问题到解决方案
最近在开发一个富文本编辑器的项目,需要实现所见所得(WYSIWYG)的效果。听起来不复杂对吧?但实际做的时候真是踩了不少坑。折腾了两天才搞定,这里分享下我的经历和解决方案。
先说说问题:样式总是乱窜
最开始我用的是 contenteditable 属性,直接给 div 加上这个属性确实能实现基本的编辑功能。但很快我就发现问题了:用户输入的内容样式总是乱窜。比如设置了字体加粗,结果换行后整个段落都变成了加粗;或者调整字号时,前面已经输入的文字也被影响了。
这里我踩了个坑:我以为 contenteditable 会自动处理好这些样式隔离的问题。实际上它压根不管这些,完全是“裸奔”状态。
尝试了几个方案,都不太理想
第一个想到的是用 iframe 嵌套一个独立的 document 来做编辑器容器。这样确实能解决部分样式污染的问题,但带来了新的麻烦:iframe 和主页面通信变得很麻烦,尤其是要实时同步内容变化的时候。
后来试了下用 execCommand 方法操作文档,比如:
document.execCommand('bold', false, null);
document.execCommand('fontSize', false, '7');
这种方法虽然能触发一些基础的样式命令,但现代浏览器已经逐渐废弃了 execCommand,兼容性是个大问题。而且它的功能非常有限,很多复杂的样式需求根本实现不了。
最终的解决方案:用 DOM 操作 + 样式隔离
折腾了半天发现,最好的办法还是老老实实操作 DOM,同时做好样式隔离。这里是我的核心代码:
<div id="editor" contenteditable="true" style="outline:none;"></div>
<script>
const editor = document.getElementById('editor');
// 监听输入事件
editor.addEventListener('input', () => {
const selection = window.getSelection();
const range = selection.getRangeAt(0);
// 创建一个新的 span 包裹当前选中的文本
const span = document.createElement('span');
span.style.fontWeight = 'bold'; // 示例:加粗样式
range.surroundContents(span);
console.log(editor.innerHTML); // 实时获取 HTML 内容
});
// 初始化时加载样式隔离
function initEditor() {
editor.innerHTML = <p><br></p>; // 默认占位符
editor.style.cssText =
white-space: pre-wrap;
word-wrap: break-word;
min-height: 100px;
border: 1px solid #ccc;
padding: 10px;
;
}
initEditor();
</script>
为什么这个方案靠谱?
上面的代码有几个关键点:
- 通过 range.surroundContents 方法,我们可以精准地控制样式的应用范围,避免影响其他内容。
- 样式隔离是通过动态创建 span 或其他标签来实现的,确保每个样式修改都是局部的。
- 监听 input 事件可以实时捕获用户的操作,方便后续扩展功能,比如保存草稿或实时预览。
当然,这个方案也不是完美的。比如在某些极端情况下(快速连续输入),可能会出现光标错位的问题。不过无大碍,用户刷新一下就能恢复。
踩坑提醒:这三点一定注意
最后总结几个我踩过的坑,给大家提个醒:
- 不要直接依赖 contenteditable 的默认行为,它的表现因浏览器而异,尤其是在移动端。
- execCommand 看似简单,但已经被现代浏览器逐步废弃,尽量少用。
- 样式隔离一定要做好,否则用户随便改个样式就会影响到整篇文档。
以上是我踩坑后的总结
所见所得效果看似简单,实际开发中还是会遇到不少细节问题。以上就是我的完整解决方案和踩坑经验,亲测有效。如果你有更好的实现方式,欢迎评论区交流!后续我还会分享更多类似的实战经验,咱们下次见。

暂无评论