为什么我的可疑光标在Chrome中跳到最后?

时间:2015-01-05 19:00:39

标签: javascript html google-chrome contenteditable

方案

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

问题

光标位置是最重要的问题。如果删除其中一个<span>元素,则光标会跳转到<div>的末尾。更有趣的是,如果你键入一些文字,而光标是#34;最后,&#34;文本放置很好......然后,如果删除新键入的文本,光标会跳回来!

我准备了一个小提琴来证明这一点。 我需要此功能才能在Chrome 中使用,而其他浏览器现在不需要关注或者有适当的解决方法。 (另请注意,准备好的小提琴只是为了在Chrome中展示这一点)。

Fiddle


小提琴说明:Chrome版本39.0.2171.95 m(64位)以32位再现

  1. 点击<div>区域
  2. 键入&#34; 123&#34;
  3. Backspace&#34; 3&#34;退格&#34; 2&#34; Backspace&#34; 1&#34; enter image description here

  4. 相关细节

    对此进行了广泛的研究,我遇到了类似的各种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
    4. 我找到的最接近的SO解决方案可能是here。此解决方案中的想法是在&zwnj;元素之后放置<span>个字符,但如果我现在要删除<span>,我会删除&zwnj; ...强制我的光标跳到最后,通过不删除我的&#34;初始删除键击中的<span>来提供奇怪的UI体验。&#34;

      问题

      是否有人遇到此问题并找到了解决方法?我欢迎任何可能的解决方案,即使JS他们来的时候也很讨厌。我已经开始了解利用contenteditable带来了一份挣扎的清单,但这似乎是我目前的最后一个难点。

6 个答案:

答案 0 :(得分:16)

我不知道为什么会这样,但我觉得它与<div>的大小有关,所以我尝试使用display属性。在将其设置为inline-block并使用文本稍微播放之后,我发现在我对其进行一些编辑后,问题就消失了,特别是添加了新行。

我看到,出于某种原因,<br/>标记在我删除新行后保留在div.main中,但<div>的外观及其对箭头的响应方式键与其中没有新行相同。

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

总结如下:

  1. display: inline-block添加到div.main
  2. <br/>
  3. 末尾添加div.main

    <强> JSFiddle Link

答案 1 :(得分:4)

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

新HTML将在外部HTML中有一个新的div(以及最后的相应结束标记):

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

并将其添加到您的CSS:

div#editable {
    display: inline-block;
}

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

这是fiddle

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

答案 2 :(得分:1)

因此,我的方法稍微更清晰一些,它依赖于contenteditable字段更新时触发的input事件。 IE不支持此事件,但看起来contenteditable在IE中非常不稳定。

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

限制包括箭头键周围的一些奇怪的行为,就像你自己看到的那样。我所看到的主要是当你向后移动跨度时光标保持其正确的位置,但是当你向前移动时则不是。

JSFiddle Link

<强>的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

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

答案 3 :(得分:0)

我找到了一种解决<{1>}末尾带有一些JQuery的游标行为错误的初始问题的hacky方法。我基本上是为光标插入一个空<div>到&#34; latch&#34;在<i>内容的末尾。这个标签对我有用,因为由于覆盖<div>(请参阅小提琴)而从正常文本退回这些内容时,最终用户没有UI差异,而且我还需要一些不同于font-style: normal的内容来插入

我对这种解决方法并不特别激动,特别是选择<span>只是因为,但我没有遇到更好的选择,我仍然欢迎更好的解决方案 - 如果存在的话!我打算继续努力,希望能够弄清楚箭头键,但幸运的是,我只是被小提琴中的这个小故障所困扰,而不是我的代码。


Fiddle (仅限Chrome)

<i>

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

该问题在以下示例中显而易见,可以通过将contentetiable包装器更改为display:block来解决,但是会导致非常令人讨厌的Chrome错误-{{1 }}将在按下ENTER键后添加

<div><br></div>
[contenteditable="true"]{
  border: 1px dotted red;
  box-sizing: border-box;
  width: 100%;
  padding: 5px;
  outline: none;
  display: inline-block; /* prevents Chrome from adding <div><br></div> when pressing ENTER key. highly undesirable. */
}

[contenteditable="false"]{
  background: lightgreen;
  padding: 3px;
  border-radius: 3px;
}

让我们尝试对其进行修复:

我可以看到<div contenteditable="true"> <span contenteditable="false">item</span> <span contenteditable="false">item</span> <span contenteditable="false">item</span> </div>的组合比内容宽,而且width的组合也有问题。

如果有 CSS 修复的可能性,我也不想使用 javascript

我非常想保留display: inline-block来防止上述Chrome错误发生,因此将display: inline-block问题设置为{{1 }}当 contenteditable 成为焦点时,并使用允许假冒相同宽度的元素包装所有内容 和之前在 contenteditable 中使用伪元素一样(仅在聚焦时使用)

width
auto

上面的解决方案在某些情况下是好的,但是当行换行并且在最后一个像上时,最后一个元素是节点:

.wrapper{
  width: 100%;
  position: relative;
}

[contenteditable="true"]{
  border: 1px solid red;
  box-sizing: border-box;
  width: 100%;
  padding: 5px;
  outline: none;
  display: inline-block;
}

[contenteditable="true"]:focus{
  border-color: transparent;
  width: auto;
}

[contenteditable="true"]:focus::before{
  content: '';
  border: 1px solid red; /* same as "real" border */
  position: absolute;
  top:0; right:0; bottom:0; left:0;
}

[contenteditable="false"]{
  background: lightgreen;
  padding: 3px;
  border-radius: 3px;
}
<div class='wrapper'>
  <div contenteditable="true">
      <span contenteditable="false">item</span>
      <span contenteditable="false">item</span>
      <span contenteditable="false">item</span>
  </div>
<div>

让我们只使用javascript并在末尾放置一个.wrapper{ width: 100%; position: relative; } [contenteditable="true"]{ border: 1px solid red; box-sizing: border-box; width: 100%; padding: 5px; outline: none; display: inline-block; } [contenteditable="true"]:focus{ border-color: transparent; width: auto; } [contenteditable="true"]:focus::before{ content: ''; border: 1px solid red; /* same as "real" border */ position: absolute; top:0; right:0; bottom:0; left:0; } [contenteditable="false"]{ background: lightgreen; padding: 3px; border-radius: 3px; }元素(如果一个元素不存在的话)(不能通过全选并删除内容将其删除):

<div class='wrapper'>
  <div contenteditable="true">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
      <span contenteditable="false">item</span>
      <span contenteditable="false">item</span>
      <span contenteditable="false">item</span>
  </div>
<div>
br
var elem = document.body.firstElementChild;

if( !elem.lastChild || elem.lastChild.tagName != 'BR' )
    elem.insertAdjacentHTML('beforeend', '<br>')