javascript 为什么我的可编辑光标在 Chrome 中跳到末尾?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27786048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 07:59:41  来源:igfitidea点击:

Why Is My Contenteditable Cursor Jumping to the End in Chrome?

javascripthtmlgoogle-chromecontenteditable

提问by scniro

Scenario

设想

I have a contenteditable <div>area, and within this area I may have some <span contenteditable="false"></span>containing some text. The idea is, these span elements will represent styled text that can not be edited, but may be deleted from the <div contenteditable="true"></div>area by pressing the backspace key.

我有一个 contenteditable<div>区域,在这个区域内我可能有一些<span contenteditable="false"></span>包含一些文本。这个想法是,这些跨度元素将代表无法编辑的样式文本,但可以<div contenteditable="true"></div>通过按退格键从该区域中删除。

Issue

问题

The cursor placementis the big issue here. if you delete one of these <span>elements, the cursor jumps to the end of the <div>. More interesting, if you type some text while the cursor is "at the end," the text placement is just fine... Then, if you delete the newly typed text, the cursor jumps back!

光标放置在这里的大问题。如果删除这些<span>元素之一,光标将跳转到<div>. 更有趣的是,如果您在光标位于“末尾”时键入一些文本,则文本放置就可以了……然后,如果您删除新键入的文本,则光标会跳回!

I have prepared a fiddle which will demonstrate this. I need this to work only in Chrome, and other browsers are either of non-concern for now or have workarounds in place. (Also note the prepared Fiddle is crafted to demonstrate this in Chrome only).

Fiddle

我准备了一个小提琴来证明这一点。我只需要它在 Chrome 中工作,其他浏览器要么暂时不关心,要么有解决方法。(另请注意,准备好的 Fiddle 仅用于在 Chrome 中演示这一点)。

小提琴



Fiddle Instruction:Chrome Version 39.0.2171.95 m (64-bit) reproduced in 32-bit as well

小提琴说明:Chrome 版本 39.0.2171.95 m(64 位)也以 32 位复制

  1. Click into <div>area
  2. Type "123"
  3. Backspace "3" Backspace "2" Backspace "1" enter image description here
  1. 点击进入<div>区域
  2. 输入“123”
  3. 退格“3”退格“2”退格“1” 在此处输入图片说明


Related Details

相关详情

Researching this extensively, I have come across various SO question that are similar, but borrowing the associated solutions has not proved to be the silver bullet I am after. I have also found issues for the Chrome project which seem to target (perhaps not in the exact manner) the issue described above, and can be viewed below.

对此进行了广泛的研究,我遇到了各种类似的 SO 问题,但事实证明借用相关的解决方案并不是我所追求的灵丹妙药。我还发现了 Chrome 项目的问题,这些问题似乎针对(可能不是以确切的方式)上述问题,可以在下面查看。

  1. Issue 384357: Caret position inside contenteditable region with uneditable nodes
  2. Issue 385003: Insert caret style is wrong when reaching the end of a line in a contenteditable element
  3. Issue 71598: Caret in wrong position after non-editable element at the end of contentEditable
  1. 问题 384357:具有不可编辑节点的可编辑区域内的插入符号位置
  2. 问题 385003:到达 contenteditable 元素中的行尾时插入插入符号样式错误
  3. 问题 71598:在 contentEditable 末尾的不可编辑元素之后的插入符号位置错误

The closest SO solution I have found can be here. The idea in this solution is to place &zwnj;characters after the <span>elements, but if I want to now delete my <span>, I instead delete the &zwnj;... forcing my cursor to jump to the end, offering a weird UI experience by not deleting my <span>on my "initial delete key stroke."

我找到的最接近的 SO 解决方案可以在这里。这个解决方案的想法是&zwnj;<span>元素之后放置字符,但是如果我现在想删除 my <span>,我会删除&zwnj;... 强制我的光标跳到最后,通过不删除 my<span>来提供奇怪的 UI 体验。初始删除键击。”

Question

问题

Has anyone experienced this issue and found a work around? I welcome any possible solution, even as JS hacky as they come. I've come to learn that leveraging contenteditablecomes with a laundry list of struggle, but this seems to be the last remaining difficulty I currentlyhave.

有没有人遇到过这个问题并找到了解决方法?我欢迎任何可能的解决方案,即使他们来了 JS hacky。我开始了解到利用杠杆contenteditable会带来一系列的斗争,但这似乎是我目前遇到的最后一个困难。

采纳答案by dutzi

I don't know why this happens, but I had the feeling it has something to do with the sizing of the <div>, so I tried playing with the displayproperty. After setting it to inline-blockand playing a little with the text I found that the issue is gone after I make some edits to it, specifically adding a new line.

我不知道为什么会发生这种情况,但我感觉这与 的大小有关<div>,因此我尝试使用该display属性。将其设置为inline-block并稍微处理文本后,我发现在对其进行一些编辑后问题消失了,特别是添加了一个新行。

I saw that, for some reason, the <br/>tag is kept in div.mainafter I delete my new line, but the appearance of the <div>and the way it responds to arrow keys is the same as if there is no new line in it.

我看到,由于某种原因,在我删除新行后,<br/>标签仍然保留div.main,但它的外观<div>和对箭头键的响应方式与其中没有新行一样。

So I restarted the fiddle with the CSS change and a <br/>tag in div.mainand viola!

所以我用 CSS 更改和中提琴中的<br/>标签重新启动了小提琴div.main

So to conclude:

所以总结一下:

  1. Add display: inline-blockto div.main
  2. add a <br/>at the end of div.main
  1. 添加display: inline-blockdiv.main
  2. <br/>在末尾添加一个div.main

JSFiddle Link

JSFiddle 链接

回答by Louis

The problem is that your contenteditableelement is a div, which by default is display: block. This is what causes your caret position problem. This can be fixed by making the outermost divnon-editable and adding a new editable divthat will be treated as inline-block.

问题是您的contenteditable元素是 a div,默认情况下是display: block. 这就是导致您的插入符号位置问题的原因。这可以通过使最外面的div不可编辑并添加div将被视为inline-block.

The new HTML would have a new divjust inside the outer one (and the corresponding closing tag at the end):

新的 HTML 将div在外层(以及末尾的相应结束标记)内有一个新的:

<div id="main" class="main"><div id="editable" contenteditable="true">...

And add this to your CSS:

并将其添加到您的 CSS 中:

div#editable {
    display: inline-block;
}

For the sake of seeing the caret better when it is between spanelements, I've also added margin: 2pxto the rule for div.main spanin the CSS but this is not necessary to prevent the caret jumping issue reported in the question.

为了在span元素之间更好地看到插入符号,我还在 CSS 中添加margin: 2px了规则,div.main span但这不是防止问题中报告的插入符号跳转问题所必需的。

Here is a fiddle.

这是一个小提琴

As you've started discovering, contenteditableis handled inconsistently across browsers. A few years back, I started working on a project (in-browser XML editor) where I thought contenteditablewould make everything easier. Then as I developed the application, I soon found myself taking over the functions that I thought contenteditablewould give me for free. Today, the only thing contenteditablegive me is that it turns on keyboard events on elements I want to edit. Everything else, including caret movement and caret display, is managed by custom code.

正如您开始发现的那样,contenteditable在浏览器之间的处理不一致。几年前,我开始从事一个项目(浏览器内的 XML 编辑器),我认为它contenteditable会使一切变得更容易。然后当我开发应用程序时,我很快发现自己接管了我认为contenteditable可以免费提供的功能。今天,唯一contenteditable给我的是它在我想要编辑的元素上打开键盘事件。其他一切,包括插入符号移动和插入符号显示,都由自定义代码管理。

回答by cmptrgeekken

So, I've got a slightly cleaner implementation of your approach, which relies on the inputevent that is triggered when a contenteditablefield is updated. This event is not supported by IE, but it looks like contenteditableis pretty wonky in IE anyways.

因此,我对您的方法有一个稍微清晰的实现,它依赖于更新字段input时触发的事件contenteditable。IE 不支持此事件,但contenteditable无论如何在 IE 中看起来都非常不稳定。

It basically injects elements around every element marked as contenteditable="false". It could probably be done on initial load rather than just on the inputevent, but I figured you might have ways of injecting more span's to the region, so inputshould account for this.

它基本上在每个标记为的元素周围注入元素contenteditable="false"。它可能在初始加载时完成,而不仅仅是在input事件上完成,但我认为您可能有方法span向该区域注入更多's,因此input应该考虑到这一点。

Limitations include some weird behavior surrounding the arrow keys, as you've seen yourself. Mostly what I've seen is that the cursor maintains its proper position when you move backward through the spans, but not when you move forward.

如您所见,限制包括围绕箭头键的一些奇怪行为。大多数情况下,我看到的是,当您向后移动跨度时,光标会保持其正确位置,但当您向前移动时则不会。

JSFiddle Link

JSFiddle 链接

Javascript

Javascript

$('#main').on('input', function() {
    var first = true;
    $(this).contents().each(function(){
        if ($(this).is('[contenteditable=false]')) {
            $("<i class='wrapper'/>").insertAfter(this);
            if (first) {
                $("<i class='wrapper'/>").insertBefore(this);
                first = false   
            }
        }
    });
}).trigger('input');

CSS

CSS

div.main {
    width: 400px;
    height: 250px;
    border: solid 1px black;
}

div.main span {
    width:40px;
    background-color: red;
    border-radius: 5px;
    color: white;
    cursor: pointer;
}

i.wrapper {
    font-style: normal;
}

回答by scniro

I have found a hacky way to solve the initialproblem of the cursor misbehaving at the end of the <div>with some JQuery. I am basically inserting an empty <i>for the cursor to "latch" on to at the end of the <div>contents. This tag works for me because there is no UI difference to the end user when backspacing these from normal text due to overriding font-style: normal(see fiddle), and I also needed something different than <span>to insert

我找到了一种解决在某些 JQuery结束时游标行为异常的初始问题的hacky 方法<div>。我基本上<i>是在<div>内容的末尾插入一个空的光标以“锁定” 。这个标签对我有用,因为当由于覆盖font-style: normal(参见小提琴)而将这些与普通文本退格时,最终用户没有 UI 差异,而且我还需要一些不同于<span>插入的东西

I am not particularly thrilled with this workaround, especially choosing an <i>just because, but I have not come across a better alternative, and I still welcome better solutions- if any exist!. I plan to keep working at this to hopefully figure out the arrow keys as well, but luckily I am only bothered by this glitch in the Fiddle and not my code.

我对这种解决方法并不是特别兴奋,尤其是选择一个<i>只是因为,但我还没有遇到更好的替代方法,我仍然欢迎更好的解决方案 - 如果存在的话!我打算继续努力,希望也能找出箭头键,但幸运的是,我只被 Fiddle 中的这个故障困扰,而不是我的代码。


Fiddle(Chrome only)


小提琴(仅限 Chrome)

<div id="main" contenteditable="true">
    <span contenteditable="false">item</span>
    <span contenteditable="false">item</span>
    <span contenteditable="false">item</span>
</div>

function hackCursor() {    
    return $('#main :last-child').get(0).tagName === 'SPAN' ? 
        $('#main span:last-child').after('<i style="font-style: normal"></i>') : 
        false;
}

$(function(){

    hackCursor();

    $('#main').bind({
        'keyup': function(e) {
            hackCursor();
        }
    })
});

回答by Mati Horovitz

Just add a new line char (\n) before closing the contenteditabletag. For example in JavaScript: var html = '<div id="main" contenteditable="true">' + content + "\n" + '</div>'

只需\n在关闭contenteditable标签之前添加一个新行字符 ( ) 。例如在 JavaScript 中: var html = '<div id="main" contenteditable="true">' + content + "\n" + '</div>'