javascript - Caret disappears in Firefox when saving its position with Rangy
问题描述
This happens only in Firefox.
Important: I am saving the caret's position with rangy.saveSelection():
- when click the content editable div
- on keyup
- when adding an external html element (as a node) to the content editable div
I need the position saved constantly through multiple means to be able to insert html elements on click (I have some tags).
When I click in the contentEditable div and the div is empty (first focus, let's say), I cannot see the caret unless I start typing. If the caret is at the end, I cannot see it either.
Another weird behaviour is that I cannot use the arrows to navigate between the text in the contentEditable div.
If I remove the functions which (constantly) saves the caret's position (on input, click etc.) the caret returns to normal (the caret is visible).
The problem appears when I start saving the position of the caret. Clearly I should be doing some sort of reset or a clear.. but from what I understand, those seem counterproductive (as from my understanding they destroy the saved caret location).
The content editable div
<div class="input__boolean input__boolean--no-focus">
<div
@keydown.enter.prevent
@blur="addPlaceholder"
@keyup="saveCursorLocation($event); fixDelete(); clearHtmlElem($event);"
@input="updateBooleanInput($event); clearHtmlElem($event);"
@paste="pasted"
v-on:click="clearPlaceholder(); saveCursorLocation($event);"
class="input__boolean-content"
ref="divInput"
contenteditable="true">Cuvinte cheie, cautare booleana..</div>
</div>
My methods/functions
inputLength($event){
this.input_length = $event.target.innerText.length;
if(this.input_length == 0)
this.typed = false;
},
addPlaceholder(){
if(this.input_length == 0 && this.typed == false){
this.$refs.divInput.innerHTML = 'Cuvinte cheie, cautare booleana..'
}
},
clearPlaceholder(){
if(this.input_length == 0 && this.typed == false){
this.$refs.divInput.innerHTML = '';
}
},
updateBooleanInput($event){
this.typed = true;
this.inputLength($event);
},
saveCursorLocation($event){
if($event.which != 8){
if(this.saved_sel)
rangy.removeMarkers(this.saved_sel)
this.saved_sel = rangy.saveSelection();
}
// if(this.input_length == 0 && this.typed == false){
// var div = this.$refs.divInput;
// var sel = rangy.getSelection();
// sel.collapse(div, 0);
// }
},
insertNode: function(node){
var selection = rangy.getSelection();
var range = selection.getRangeAt(0);
range.insertNode(node);
range.setStartAfter(node);
range.setEndAfter(node);
selection.removeAllRanges();
selection.addRange(range);
},
addBooleanTag($event){
// return this.$refs.ChatInput.insertEmoji($event.img);
this.$refs.divInput.focus();
console.log(this.input_length);
if(this.typed == false & this.input_length == 0){
this.$refs.divInput.innerHTML = ''
var space = '';
this.typed = true
this.saveCursorLocation($event);
}
rangy.restoreSelection(this.saved_sel);
var node = document.createElement('img');
node.src = $event.img;
node.className = "boolean-button--img boolean-button--no-margin";
node.addEventListener('click', (event) => {
// event.currentTarget.node.setAttribute('contenteditable','false');
this.$refs.divInput.removeChild(node);
})
this.insertNode(node);
this.saveCursorLocation($event);
},
clearHtmlElem($event){
var i = 0;
var temp = $event.target.querySelectorAll("span, br");
if(temp.length > 0){
for(i = 0; i < temp.length; i++){
if(!temp[i].classList.contains('rangySelectionBoundary')){
if (temp[i].tagName == "br"){
temp[i].parentNode.removeChild(temp[i]);
} else {
temp[i].outerHTML = temp[i].innerHTML;
}
}
}
}
},
pasted($event){
$event.preventDefault();
var text = $event.clipboardData.getData('text/plain');
this.insert(document.createTextNode(text));
this.inputLength($event);
this.typed == true;
},
insert(node){
this.$refs.divInput.focus();
this.insertNode(node);
this.saveCursorLocation($event);
},
As you can see in the saveCursorLocation(), I was trying to solve the scenario in which you click in the contentEditable div and there's no caret - which is confusing for the user.
// if(this.input_length == 0 && this.typed == false){
// var div = this.$refs.divInput;
// var sel = rangy.getSelection();
// sel.collapse(div, 0);
// }
It was a dead end - most likely because of my poor understanding of Rangy and how should I use those functions.
Expected behaviour vs actual results on Firefox
When I click on the contentEditable div I expect the caret to appear (while in the background to save my position). When typing, I expect the caret to appear after the last typed character while also on keyup to save my caret's position. Also I expect to be able to navigate the text via left/right arrows and see the caret when doing so.
All of these are generated by
v-on:click="..... saveCursorLocation($event);"
and
@keyup="saveCursorLocation($event);....."
If anybody believes that it would be helpful, I can record the content editable div and its behaviour in Firefox.
EDIT: I managed to isolate the problem and reproduce it into a JSFiddle - https://jsfiddle.net/Darkkz/6Landbj5/13.
What to look for?
- Open the fiddle link in Firefox, then press one of the blue buttons (SI, SAU, NU) and then look at the input, the caret is not displayed.
- Click the input, the caret is not displayed
- While typing in the input,the caret is not displayed. Although, if you click in a word/in between content, the caret will be displayed
解决方案
显然 rangy 的选择保存和恢复模块不能用于在用户与 a 交互时跟踪当前选择contenteditable
,就像你想要的那样。
我深入研究了一下,问题是 rangy 插入 hidden <span>
s 作为标记,并将选择更新为在标记之后,而不是将其保留#text
在用户编辑的节点内:
<div contenteditable>
#text [This is something I typed <!-- selection is moved from here -->]
<span class="rangySelectionBoundary"/>
<!-- to here -->
</div>
Firefox has trouble displaying the caret in this scenario (I haven't found a bug about this specific issue, but here's a similar one where the caret is not displayed when the selection is between two <span>
s ).
注释掉这段代码似乎可以解决插入符号消失的问题。我不清楚为什么需要该代码——它是在 1.0 之前在一个大型提交中添加的,其消息是:“修复了控制范围和多个范围选择的保存/恢复问题。添加了保存/恢复和 CSS 类应用程序的演示模块。” -- 所以我不太愿意建议在 rangy 中解决这个问题(而且由于它已经有几年没有维护了,我不太希望得到它的作者对此的意见)。
所以我试图弄清楚为什么你首先需要这个,以建议其他不涉及的解决方案rangy.saveSelection
(例如,rangy 的Text Range 模块提供getSelection().saveCharacterRanges(containerNode)
了无需修改 DOM 即可工作。
看来您有一个<div contenteditable>
和一些“按钮”(<span>
s),单击它们会在插入符号位置插入一些 HTML。您试图解决的问题是,当单击“按钮”时,选择从按钮移动contenteditable
到按钮中,并且您无法检测到插入位置。
您可以制作按钮,而不是存储和恢复选择,user-select: none
这将使插入符号保持在 contenteditable 中。
为了测试这一点,我注释掉了所有对 and 的引用,rangy.saveSelection
并将“按钮”处理程序中rangy.restoreSelection
的调用更改为仅在. 在这个更新的小提琴中查看它是如何工作的:https ://jsfiddle.net/fjxsgvm2/this.$refs.divInput.focus();
onclick
contenteditable
if (!this.$refs.divInput.contains(document.activeElement))
推荐阅读
- arrays - “随机”消除数组中的算法
- javascript - 在 shopify 网站上嵌入 Facebook RSS 提要,并发症
- python - 即使代码很好,机器人也停止工作
- reactjs - 如何将 Bootstrap 变量导入 SASS 模块?
- graph - Gatsby 使用带有语言环境的静态查询
- python - 如何将 python 包定向到用户安装库而不是根库?
- oauth-2.0 - 授权服务器如何验证 Oauth2.0 中的 client_secret
- javascript - 如果用户名已在使用但在 JAVA 中,请检查 Firebase 数据库
- r - 如果每个组在 R 中都有特定的块大小,如何将函数拆分或应用到散布的组?
- node.js - Ember 遗留项目中的 Node-sass 问题