简单富文本编辑器实现

在线demo预览,实现原理如下:

1)获取选区内容,如果没有选区则给整段文本设置样式。 2)新增span标签包裹选区内容,为span标签增加style样式。 3)优化DOM结构。比如demo中删除了空白节点、合并父子层级样式减少层级嵌套。

第一步:通过 www.getSelection() 获取选区,sel.isCollapsed判断选区的起始点和终止点是否位于一个位置
//返回一个 Selection 对象,表示用户选择的文本范围或光标的当前位置 let sel = window.getSelection() let range = sel.getRangeAt(0) //返回一个包含当前选区内容的区域对象。 if (sel.isCollapsed) { //没有框选设置整段文本样式 container.style[key] = val return }

第二步:新增span标签包裹选区内容
const f = range.extractContents() //获取选取内容,并删除 let span = document.createElement('span') span.style.setProperty(key, val) span.appendChild(f) //span标签包裹选取内容

【简单富文本编辑器实现】第三步:优化dom结构,替换选取节点内容
const keys = ['font-size', 'letter-spacing', 'font-weight', 'color']function getNodeStyle(n) { const style = n.style let ret = {} keys.filter(k => style[k] != null && style[k] != '').forEach(k => { ret[k] = style[k] }) return ret }function appendStyle(n, style) { for (let key in style) { n.style.setProperty(key, style[key]) } }function isSame(s1, s2) { return JSON.stringify(s1) == JSON.stringify(s2) } //找到文本,向上获取父节点的样式附加到自身,减少span嵌套层级 function flatNode(node) { let list = [] let pStyle = getNodeStyle(node) let last = null; function _each(n, s) { for (let i = 0; i < n.childNodes.length; i++) { let child = n.childNodes[i] if (child.nodeType == 3) { let newStyle = Object.assign({}, s, pStyle) if (last && isSame(newStyle, getNodeStyle(last))) { last.appendChild(child.cloneNode()) last.normalize() //相邻文本节点合并 } else { let span = document.createElement('span') span.appendChild(child.cloneNode()) appendStyle(span, newStyle) list.push(span) last = span; } } else { _each(child, Object.assign({}, s, getNodeStyle(child), pStyle)) } } } _each(node, {}) return list; }const nodeList = flatNode(span) let newF = document.createDocumentFragment() newF.replaceChildren(...nodeList) newF.normalize() range.insertNode(newF) //选区位置插入新节点

    推荐阅读