先来看看效果
6月15日
自定义编辑器原理:
HTML的contenteditable属性:
当一个元素的contenteditable状态为true(contenteditable属性为空字符串,或为true,或为inherit且其父元素状态为true)时,意味着该元素是可编辑的。
否则,该元素不可编辑。
contenteditable可直接使用而不需赋值;较新的浏览器中,像<p contenteditable> 也可以被正常呈现,表示contenteditable为true。虽然它是不合法的。一个简单的例子:在浏览器地址栏输入 data:text/html, <html contenteditable> ,即可打开一个简单的在线编辑器
<div
class="task-edit"
ref={editor}
contenteditable
onInput={handleInput}>
</div>
我们想要实现视屏里的插入自定义标签的效果,很自然的能想到直接往这个div里面插入元素,
常见的方法如:innerHTML和appendchild
但这两个方法都会出现无法在行末直接添加的问题,原因是当我们换行时,会自动在父div里插入一个新的div,如图:
当我们直接用如下方法插入时,会出现:
editor.value.innerHTML +=
`<span style="background-color: whitesmoke;padding:4px;display: inline;" contenteditable="false">${tagSelectValue.value}</span>`
我们添加的span会直接添加在第二行的div下面,变成了第三行,而且也不能实现光标在哪里就插入哪里的功能
解决方法:
通过Selection对象获取当前的光标
1. 获取当前 Selection 对象
通过 window.getSelection()
获取用户当前光标选区:
const selection = window.getSelection(); if (!selection || selection.rangeCount === 0) return false; // 无光标
2. 验证焦点节点在目标输入框内
检查选区起始节点(anchorNode
)是否在目标编辑器内部
const editor = document.getElementById('editableDiv'); // 目标输入框
const anchorNode = selection.anchorNode; // 向上遍历父节点,判断是否属于编辑器内部
const isCursorInEditor = editor.contains(anchorNode);
完整代码:
if (!isCursorInEditor()) {
console.log('未指定插入标签的位置')
return
}
const selection = window.getSelection()
const range = selection?.getRangeAt(0)
console.log(selection, range)
const span = document.createElement('span')
span.textContent = tagSelectValue.value
span.style.backgroundColor = 'whitesmoke'
span.style.padding = '4px'
span.contentEditable = 'false'
range.insertNode(span)
// 将光标移到 span 后方
range.setStartAfter(span)
range.collapse(true)
selection.removeAllRanges()
selection.addRange(range)
// 聚焦编辑器
editor.value.focus()
handleInput()
如果是复杂一点的组件,可以考虑用webcomponent等技术