需求背景
做一个富文本编辑器,只保留有序列表,无序列表,超链接,普通文本四种形式。
1.quill配置toolbar保留四种形式的工具栏
2.此外,还要防止粘贴时出现不允许的数据格式
解决办法
监听paste粘贴事件,阻断粘贴默认行为,拿到剪切板的值,清洗掉不需要的标签和属性后,自行插入到innerHTML
难点
在自行插入时我们需要知道插入的位置,但是我们的quill或者web api只给我们提供了纯文本的光标索引, 例如光标在input里面选择了某个位置,那么可以在input的onclick回调通过event.tartget.selectionStart获取光标在innerText的索引,如下
<input type="text" id="txt1" value="你好1 你好2 你好3" onclick="getTextCursorIndex()" />
function getTextCursorIndex (){
const txt1 = document.getElementById("txt1");
let cursurPosition = -1;
if (txt1.selectionStart) {//非IE浏览器
cursurPosition = txt1.selectionStart;
} else {//IE
const range = document.selection.createRange();
range.moveStart("character",-txt1.value.length);
cursurPosition=range.text.length;
}
console.log(`innerText:${txt1.value}`)
console.log(`光标所在innerText索引:${cursurPosition}`)
}
但是我们无法获取到光标所在总innerHTML的索引,因为计算innerHTML位置的成本较高,所以我们放弃这种方式。
我们的innerText可能是这样的
"你好1 你好2 你好3"
对应的innerHTML是这样的
<p>你好1</p>
<ul>
<li>你好2</li>
<li>你好3</li>
</ul>
我想到的做法是在光标具体所在富文本的dom中通过innerHTML的方式添加,啥意思呢?
就是说,咱们之前是在总innerHTML上修改粘贴值,但是这个位置算不出来
const html = quillDOM.innerHTML
quillDOM.innerHTML = `${html.slice(0, pointerIndex)}${e.clipboardData.getData("text/plain")}${html.slice(pointerIndex)}`
现在我们换一种思路,进到最深层具体的dom里面(这个元素底下没有子元素,子标签),那他的innerText的光标位置不就是innerHTML的光标位置吗,那我们不就可以直接用selectionStart插入了?
当你尝试过后,你会发现,只有input标签才能用event.target.selectionStart获取光标位置,普通的p标签或者其他都不能,哈哈,懵了吧,我自己研究了一个方法算具体的位置
那么如何获取最里层元素innerText的光标索引呢
1.通过在外层quill事件委托定位到被点击的dom元素, click事件
2.如下图,主要是通过计算差值和递归得出正解
quillDOM.root.addEventListener('click', (e) => {
let tmpIndex1 = quillDOM.selection.savedRange.index // 总字符串光标所处位置
let eText = e.target.innerText
let quillDOMText = quillDOM.root.innerText
let tmpIndex2 = quillDOMText.indexOf(eText) // 点击的元素的文本在总字符串的起始索引
// 如果这里不做判断,那么之前如果有相同的innerText,会导致位置错误
while (tmpIndex1 - tmpIndex2 > eText.length) {
tmpIndex2 = quillDOMText.indexOf(eText, tmpIndex2)
}
pointerIndex = tmpIndex1 - tmpIndex2
})