JavaScript使用Range插入节点后如何将光标设置到节点后方
问题现象
- 向设置了
contenteditable属性的div中,通过JavaScript Range API插入emoji节点时,新节点插入完成后光标会出现在节点前方,不符合预期的节点后方定位要求。 - 参考同类问题的公开修复方案调整后仍未解决问题,原有实现代码如下:
var sel, rng; if (window.getSelection) { rng = document.createRange(); sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { console.log(sel); rng = sel.getRangeAt(0); rng.deleteContents(); rng.insertNode( curemji ); rng.setStartAfter(curemji); rng.collapse(false); sel.removeAllRanges(); sel.addRange(rng); } } else if (document.selection && document.selection.createRange) { document.selection.createRange().text = emoji; }
问题根因
代码中Range边界设置逻辑存在错误:
- 调用
rng.insertNode(curemji)后,Range的起止边界默认都停留在新插入节点的前方位置 - 仅调用
rng.setStartAfter(curemji)只会把Range的起始边界移动到节点后方,结束边界依然保留在节点前方,此时Range是反向选中状态(起点在终点之后) - 后续调用
rng.collapse(false)时,参数false代表折叠到Range的结束边界,也就是还停留在节点前方的旧边界位置,最终光标就会落到插入节点的前面。
修复代码
只需要在设置起始位置后,同步把Range的结束位置也设置到节点后方,再执行折叠即可,兼容绝大多数现代浏览器:
var sel, rng; if (window.getSelection) { sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { rng = sel.getRangeAt(0); rng.deleteContents(); rng.insertNode(curemji); // 同时设置起止位置到插入节点后方 rng.setStartAfter(curemji); rng.setEndAfter(curemji); rng.collapse(false); sel.removeAllRanges(); sel.addRange(rng); } } else if (document.selection && document.selection.createRange) { document.selection.createRange().text = emoji; }
如果插入的emoji是
<img>等替换元素,在部分低版本Safari中如果仍有定位异常,可以在插入节点后额外追加一个零宽空格字符\u200B,再把光标定位到零宽空格后方即可规避兼容问题。
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

