在Rangy中保存位置时,插入符号在Firefox中消失 [英] Caret disappears in Firefox when saving its position with Rangy

查看:93
本文介绍了在Rangy中保存位置时,插入符号在Firefox中消失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这仅在Firefox中发生.

重要提示:我使用保存插入符号的位置rangy.saveSelection():

  • 点击内容可编辑的div
  • 关于按键
  • 在将外部html元素(作为节点)添加到内容可编辑div时

我需要通过多种方式不断保存的位置,以便能够在点击时插入html元素(我有一些标签).

当我单击contentEditable div且div为空时(假设是第一个焦点),除非开始输入,否则看不到插入符号.如果插入符号在末尾,我也看不到它.

另一个奇怪的行为是我不能使用箭头在contentEditable div中的文本之间导航.

如果我删除了(恒定地)保存插入符号位置的功能(在输入时单击等),插入符号将恢复正常(插入符号可见).

当我开始保存插入符号的位置时出现问题.显然,我应该进行某种重置或清除操作.但是,据我了解,这些操作似乎适得其反(因为据我了解,它们会破坏保存的插入符号位置).

内容可编辑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>


我的方法/功能

            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);
            },

正如您在saveCursorLocation()中看到的那样,我正在尝试解决以下情况:您单击contentEditable div并且没有插入符号-这会使用户感到困惑.

                // if(this.input_length == 0 && this.typed == false){
                //  var div = this.$refs.divInput;
                //  var sel = rangy.getSelection();
                //  sel.collapse(div, 0);
                // }

这是一个死胡同-很有可能是由于我对Rangy的了解不深,以及我应该如何使用这些功能.

Firefox上的预期行为与实际结果

当我单击contentEditable div时,我希望插入符号出现(在后台保存我的位置).键入时,我希望插入符号出现在最后一个键入的字符之后,同时也要进行键盘键入以保存插入符号的位置.另外,我希望能够通过左右箭头导航文本,并在执行此操作时看到插入符号.

所有这些都是由

生成的

v-on:click="..... saveCursorLocation($event);"

@keyup="saveCursorLocation($event);....."

如果有人认为这会有所帮助,那么我可以在Firefox中记录可编辑div的内容及其行为.

我设法隔离了问题并将其重现为JSFiddle- https://jsfiddle. net/Darkkz/6Landbj5/13 .

寻找什么?

  • 在Firefox中打开小提琴链接,然后按蓝色按钮(SI,SAU,NU)之一,然后查看输入内容,插入符号不显示.
  • 单击输入,插入符号不显示
  • 在输入中输入时,插入符号不显示.虽然,如果您在内容之间单击单词/插入,将显示插入符号

解决方案

显然是rangy的

在这种情况下,Firefox无法显示插入符号(我尚未找到有关此特定问题的错误,但这是一个类似的错误,其中不是当选择为二取值)显示插入记号

注释掉此代码似乎可以解决插入符号消失的问题.我不清楚为什么需要该代码-在1.0之前添加了 a>在大型提交中带有以下消息:解决了控制范围和多个范围选择的保存/恢复问题.添加了保存/恢复和CSS类应用程序模块的演示." -所以我不建议将其修正为多种格式(并且由于已经多年未维护,因此我对此寄希望于作者的希望不大.)

所以我试图弄清楚为什么您首先需要这样做,以建议不涉及rangy.saveSelection的其他解决方案(例如,rangy的getSelection().saveCharacterRanges(containerNode) ,它无需修改DOM就可以正常工作.

似乎您有一个<div contenteditable>和一些按钮"(<span> s),单击它们会在插入符号位置插入一些HTML.您要解决的问题是,当单击按钮"时,选择内容从contenteditable移到了按钮中,并且您无法检测到插入位置.

您可以使按钮 user-select: none -这将使插入符号的内容可编辑.

为了对此进行测试,我注释掉了对rangy.saveSelectionrangy.restoreSelection的所有引用,并将按钮"的onclick处理程序中的this.$refs.divInput.focus();调用更改为仅在contenteditable尚未运行时运行通过将其包装在if (!this.$refs.divInput.contains(document.activeElement))中进行聚焦.在更新的小提琴中查看其工作原理:
https://jsfiddle.net/fjxsgvm2/

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

解决方案

Apparently rangy's Selection Save and Restore module can't be used to keep track of the current selection while the user interacts with a contenteditable, like you want.

I digged into it a bit, and the problem is that rangy inserts hidden <span>s as markers, and updates the selection to be after the marker, instead of keeping it inside the #text node the user's editing:

 <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).

Commenting this code out seems to fix the issue with the disappearing caret. It's unclear to me why that code is needed -- it was added before 1.0 in a large commit with its message saying: "Fixes for save/restore problems with control ranges and multiple range selections. Added demos for save/restore and CSS class applier modules." -- so I'm not comfortable to suggest fixing this in rangy (and since it's unmaintained for a few years, I don't have much hope in getting its author's input on this).

So I tried to figure out why you needed this in the first place, to suggest other solutions not involving rangy.saveSelection (for example, rangy's Text Range module provides getSelection().saveCharacterRanges(containerNode) that works without modifying the DOM.

It appears that you have a <div contenteditable> and some "buttons" (<span>s), clicking on which would insert some HTML at the caret position. The problem you were trying to solve was that when the "buttons" were clicked, the selection moved from the contenteditable into the button, and you were unable to detect the insert position.

Instead of storing and restoring the selection, you can instead make the buttons user-select: none - this will keep the caret in the contenteditable.

To test this, I commented out all references to rangy.saveSelection and rangy.restoreSelection and changed the this.$refs.divInput.focus(); call in the "button"'s onclick handler to run only when the contenteditable wasn't already focused by wrapping it in an if (!this.$refs.divInput.contains(document.activeElement)). See how this works in this updated fiddle:
https://jsfiddle.net/fjxsgvm2/

这篇关于在Rangy中保存位置时,插入符号在Firefox中消失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆