【问题标题】:Cursor in contenteditable not behaving properly in Chromecontenteditable 中的光标在 Chrome 中无法正常运行
【发布时间】:2019-02-18 00:41:02
【问题描述】:

当用户在 contenteditable div 内部但在实际文本之外单击时,我遇到了一个问题,即光标跳转到错误的位置。这似乎只在较新版本的 Chrome(以及 Opera)中存在问题:巧合的是,我在较旧的浏览器(Chrome 版本 55)中测试了我的示例,并且根本不存在该问题。 Edge/IE11/FireFox 也没有问题。

仅当用户单击文本行后面或位于两个黄色 divs 和类 pagebreak 之间的空行时,才会出现此问题。光标最终位于第一个 pagebreak div 上方。并且是否直接相关,我不知道,但是当删除带有类flowboxdiv 时,问题就消失了。不幸的是,我无法从应用程序中删除这个带有 flowbox 类的 div

我在这个小提琴中整理了一个例子来展示我的问题:https://jsfiddle.net/dymcn1ao/

<div class="textframe a">
    <div class="flowbox"></div>
    <article contenteditable="true">
        <p>
            <span>
                <span>Foo bar baz</span>
                <br>
                <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
                <span>Foo bar baz</span>
                <br>
                <span>Lorem ipsum dolor sit amet, consectetur adi piscing elit.</span>
                <br>
                <br>
                <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
                <br>
                <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
            </span>
        </p>
    </article>
</div>

左侧的文本字段有问题,右侧的文本字段按预期工作,因为 .flowbox div 已被删除。

编辑 1:

我创建了一个可能更容易理解的新示例。 .textframe 中的其他元素(如分页符和流框元素)确实有特定的用途,因此它们不能被忽视。这是改进后演示的链接:https://jsfiddle.net/q4pu37dn/15/

【问题讨论】:

    标签: html css google-chrome contenteditable blink


    【解决方案1】:

    更新 3(演示 3)



    变化

    我注意到most current OP code 中不再使用任何position: relative,这很好,但我相信这被遗忘了:

    &lt;span class='pagebreak spacer'contenteditable="false"&gt;&lt;/span&gt;

    我相信您最初使用 contenteditable="false" 是为了给您的 .pagebreaks 提供额外的功能并防止它们被删除,所以我重新添加了它们。


    比较

    Demo 3 将我的解决方案与OP code 并排比较行为。 Demo 3 还具有 2 个按钮(每个内容编辑器 1 个)突出显示每个 &lt;span&gt; 文本。以下是 OP 代码中的类列表(右侧的内容编辑器)和我的代码中等效的每个类的列表(左侧的内容编辑器)。

    1. div.textframe.................................section.editor
    2. p.textOutline.................article.content
    3. span.flowbox.spacer......mark.vertRule
    4. span.pagebreak.spacer ..mark.breaker

    OP 关注两个要求:

    1. 点击&lt;span&gt;s周围的空白区域时,光标会跳到内容区域的一角。

    2. 每行字符数必须与OP码当前容量一致。

    这个问题已经存在多年,但原因是星云,所以如果你将这种异常视为一种行为,你可以通过灌输不同的行为来解决它。

    Demo2Demo3 只需应用以下样式规则集即可满足这些标准:

    演示 2

    article p {display: table;...
    

    演示 3

    .content {display:table-cell;...
    

    表格单元格的行为是严格且完善的,AFAIK 是唯一默认符合其内容并符合周围表格元素的非replaced element。作为奖励,带有display: table-cell(不是&lt;td&gt;)的元素不需要嵌套在&lt;tr&gt; 内,而&lt;table&gt; 内。


    演示3

    .content { display: table-cell;...

    Fiddle

    /* Begin Defaults */
    
    * {
      margin: 0;
      padding: 0;
      border: 0;
      box-sizing: border-box;
    }
    
    html,
    body {
      background: white;
      font: 400 16px/1.45 Arial;
      height: 100%;
      width: 100%;
    }
    
    /* End Defaults */
    
    /* Begin Optional Layout */
    
    #page01 {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-evenly;
      align-items: flex-start;
      background: rgba(45, 99, 198, 0.6);
      margin: 0 auto 20px;
      height: fit-content;
      min-width: 100%
    }
    
    /* End Optional Layout */
    
    /* Begin Primary Styles */
    
    .editor {
      width: 350px;
      height: 600px;
      border: 1px solid black;
      background: #fff;
    }
    
    .vertRule {
      float: right;
      clear: right;
      width: 30px;
      height: 600px;
    }
    
    .content {
      display: table-cell;
      word-break: break-word;
    }
    
    mark {
      display: block;
      pointer-events: none;
    }
    
    .break {
      min-height: 80px;
    }
    
    /* End Primary Styles */
    
    /* Begin Control */
    
    /* https://jsfiddle.net/q4pu37dn/15 */
    
    .textframe {
      width: 350px;
      height: 600px;
      border: 1px solid black;
      background: #fff;
    }
    
    .flowbox {
      float: right;
      clear: right;
      width: 30px;
      height: 600px;
    }
    
    .spacer {
      background: yellow;
    }
    
    .pagebreak {
      display: block;
      min-height: 80px;
    }
    
    /* End Control */
    
    /* Begin Demo Test */
    
    .btn {
      display: inline-block;
      font: inherit;
      margin: 5px 10px;
      padding: 2px 5px;
      border: 5px outset grey;
      border-radius: 8px;
      color: #000;
      cursor: pointer;
    }
    
    [type='checkbox']:checked+label {
      background: rgba(255, 12, 34, 0.75);
      border: 5px inset grey;
      color: #fff;
    }
    
    #outline1:checked+label+#outline2+label+hr+#page01>.editor>.content *,
    #outline2:checked+label+hr+#page01>.textframe>#textOutline *:not(.spacer) {
      color: #fff;
      background: tomato;
      outline: 2px solid red;
    }
    
    #outline1:checked+label+#outline2+label+hr+#page01>.editor>.content>.break,
    #outline2:checked+label+hr+#page01>.textframe>#textOutline>.spacer {
      background: yellow;
      outline: none;
    }
    
    /* End Demo Test */
    <!-- Begin Demo Test -->
    
    <input id="outline1" type='checkbox' hidden>
    <label for='outline1' class='btn'>Outline 1</label>
    
    <input id="outline2" type='checkbox' hidden>
    <label for='outline2' class='btn'>Outline 2</label>
    
    <hr>
    
    <!-- End Demo Test -->
    
    <!-- Begin Optional Layout Part 1 -->
    
    <main id='page01'>
    
      <!-- End Optional Layout Part 1 -->
    
      <!-- Begin Primary Markup -->
    
      <section class="editor" contenteditable='true'>
        <mark class="vertRule" contenteditable='false'></mark>
        <article class='content'>
          <span>
          Clicking here is not a problem
        </span>
          <br>
          <br>
          <span>
          Lorem ipsum
        </span>
          <mark class="break" contenteditable='false'></mark>
          <span>
          Clicking here (on empty space, not directly on text) will put the caret above the first .break element.
        </span>
          <br>
          <br>
          <span>
          Lorem ipsum
        </span>
          <mark class="break" contenteditable='false'></mark>
          <br>
          <span>
          Clicking here is not a problem
        </span>
          <br>
          <br>
        </article>
      </section>
    
      <!-- End Primary Markup -->
    
      <!-- Begin Control -->
    
      <div class="textframe" contenteditable>
    
        <p id='textOutline'>
    
          <span class="spacer flowbox"></span>
          <span>
          Clicking here is not a problem
        </span>
          <br>
          <br>
          <span>
          Lorem ipsum
        </span>
          <span class="spacer pagebreak"></span>
          <span>
          Clicking here (on empty space, not directly on text) will put the caret above the first .pagebreak element.
        </span>
          <br>
          <br>
          <span>
          Lorem ipsum
        </span>
          <span class="spacer pagebreak"></span>
          <br>
          <span>
          Clicking here is not a problem
        </span>
          <br>
          <br>
        </p>
      </div>
    
      <!-- End Control -->
    
      <!-- Begin Optional Layout Part 2 -->
    
    </main>
    
    <!-- End Optional Layout Part 2 -->


    更新 2(演示 2)


    关于演示 1 的操作:

    “你为我设计的例子解决了这个问题,是的。不幸的是,无法在实际应用中的元素上设置这些值,那里的流程完全不合常理。” p>

    参见Demo 2,它比Demo 1效果更好。由于它仅使用定位元素,因此流中没有冲突。为了使 Demo 2 适应您的应用程序,您只需将position:relative 添加到父元素即可。相关样式如下:

    article p {display: table;...
    

    必须将position:relative 分配给嵌套在.textframe 中的所有内容,否则static 元素将不会与定位的元素交互。表格和表格组件遵循的规则不仅适用于其内容,还适用于它们与相邻元素的交互方式。


    演示 2

    article p {display: table...

    .container {
      width: 400px;
      float: left
    }
    
    .textframe {
      width: 350px;
      height: 650px;
      outline: 2px dotted lightblue;
      overflow: hidden;
      margin: 0 15px 0 0;
      /* Needed for long words */
      word-break: break-word;
    }
    
    .textframe article {
      position: relative;
      height: 650px; 
    }
    
    article p {
      display: table;
      margin: 0;
      position:relative;
    }
    
    .flowbox {
      width: 2px;
      height: 650px;
      float: right;
      clear: right;
      outline: 1px solid red;
    }
    
    .pagebreak {
      display: block;
      pointer-events:none;
      position:relative;
    }
    <div class="container">
          <h4>
           article p {display: table; position: relative;}<br>
           all children of .textframe have: position: relative;  
          </h4>
          <div class="textframe a">
            <div class="flowbox"></div>
            <article contenteditable="true">
               <p>
                <span>
                  <span>Foo bar baz</span>
                <br>
                <mark class="pagebreak" contenteditable="false" style="min-height: 80px"></mark>
                <span>Foo bar baz</span>
                <br>
                <span>Lorem ipsum dolor sit amet, consectetur adi piscing elit.</span>
                <br>
                <br>
                <mark class="pagebreak" contenteditable="false" style="min-height: 80px"></mark>
                <br>
                <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
                </span>
              </p>
              <hr>
            </article>
          </div>
        </div>

    引用

    MDN - Float

    MDN - Position

    CSS Tricks - Absolute Positioning Inside Relative Positioning

    CSS Tricks - All About Floats

    display: table/table-cell

    word-break:break-word


    【讨论】:

    • 通过将article 位置设置为绝对位置,您有效地删除了右对齐.flowbox 元素的功能,然后第二行上的单词“adi”像示例框一样跳了起来在我的示例中位于右侧。
    • 使用最新的 Chrome 并不会跳起来。添加了 Demo 2,它也不会跳起来。
    • @HelgeFox 这是演示 1 所需行为的screen recording
    • @HelgeFox 另外.flowbox 的位置不受影响...您确定您正在查看我的答案而不是将其与其他人混淆吗?
    • 听着,我可以看到不同的浏览器和不同的默认字体使我的示例难以理解。尝试将文本添加到其中一行,并确保 .flowbox 执行应有的操作:将文本换行。在您最近的示例中,我遇到了这种情况:i.imgur.com/qI1sGJE.jpg
    【解决方案2】:

    问题出在显示器上,我对此也很陌生,但是当我将您的 span 更改为 div 时它工作正常,请告诉我它是否正确,或者我无法正确理解您的问题。

    现在我不确定为什么会发生这种情况,因此无法为您提供深入的解释。

    注意 - 此后 span 和 div 的使用将不正确,因此也必须在其他地方更改为 div。

    .title {
      left: 20px;
    }
    .container {
      float: left;
      width: 400px;
    }
    .textframe {
      width: 311px;
      height: 650px;
      outline: 2px dotted lightblue;
      overflow: hidden;
      margin: 0 15px 0 0;
    }
    .textframe.b {
      left: 380px;
    }
    .textframe article {
      position: relative;
      height: 650px;
    }
    article p {
      margin: 0;
    }
    .pagebreak {
      display: block;
      position: relative;
      background: yellow;
    }
    .flowbox {
      width: 2px;
      height: 650px;
      float: right;
      clear: right;
      outline: 1px solid red;
    }
    <div class="container">
      <h4>
        With problem:
      </h4>
      <div class="textframe a">
        <div class="flowbox"></div>
        <article contenteditable="true">
          <p>
            <span>
              <span>Foo bar baz</span>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <div>Foo bar baz</div>
              <br>
              <div>Lorem ipsum dolor sit amet, consectetur adi piscing elit.</div>
              <br>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <br>
              <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
            </span>
          </p>
        </article>
      </div>
    </div>
    <div class="container">
      <h4>
        Without problem:
      </h4>
      <div class="textframe b">
        <article contenteditable="true">
          <p>
            <span>
              <span>Foo bar baz</span>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <div>Foo bar baz</div>
              <br>
              <div>Lorem ipsum dolor sit amet, consectetur adi piscing elit.</div>
              <br>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <br>
              <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
            </span>
          </p>
        </article>
      </div>
    </div>

    【讨论】:

    • 是的,它有助于使元素 display: block 像使它们伸展到其父元素的整个宽度一样。不过,这对空行(br's)的情况没有帮助。
    • @HelgeFox 所以,我们可以替代 br 标签,stackoverflow.com/questions/10996237/… 你甚至可以使用行高。大多数人不建议使用 br 标签。
    • 嗯...所以你说我应该使用空的divp 并将lineHeight 设置为用作换行元素,而不是我的br
    • No not not an empty div or an empty tag (这将是非常错误的),只需为两个 div 使用 line-height ,它可以增加行之间的高度。你基本上想用 br 标签实现什么?只是您想给用户一个额外的空白行来单击并开始编辑吗?我确定我现在没有得到你真正的问题。
    • 这个元素有 contenteditable=true 所以我们应该允许用户输入任意多的空行。这就是 br 元素的用途,例如文本框可能的用途。大多数用户喜欢在他们的文本中有几个换行符。
    【解决方案3】:

    出现这个问题是因为你使用float: right;

    如果不需要,请不要使用 CSS 属性 float: right;。你可能会遇到很多问题。在您的情况下,您不需要它。而不是这个,您可以使用 inline-block 元素作为 &lt;div class="flowbox"&gt;&lt;article contenteditable="true"&gt;

    float:right 的最小示例(有问题)

    .textframe {
        width: 311px;
        height: 650px;
        outline: 2px dotted lightblue;
        overflow: hidden;
        margin: 0 15px 0 0;
    }
    .flowbox {
        width: 2px;
        height: 650px;
        float: right;
        clear: right;
        outline: 1px solid red;
    }
    .pagebreak {
        display: block;
        position: relative;
        background: yellow;
    }
    <div class="container">
      <h4>
        With problem:
      </h4>
      <div class="textframe a">
        <div class="flowbox"></div>
        <article contenteditable="true">
          <p>
            <span>
              <span>Foo bar baz</span>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <span>Foo bar baz</span><br>
              <span>Lorem ipsum CLICK ABOVE THIS WORDS sit amet, consectetur adi piscing elit.</span>
              <br>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <br>
              <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
            </span>
          </p>
        </article>
      </div>
    </div>

    解决方案

    display:inline-block 的最小示例(没有问题)

    注意:现在我已将您的 &lt;div class="flowbox"&gt;&lt;/div&gt; 放在 &lt;article&gt; 元素之后。

    .textframe {
        width: 311px;
        height: 650px;
        outline: 2px dotted lightblue;
        overflow: hidden;
        margin: 0 15px 0 0;
    }
    .flowbox {
        width: 2px;
        height: 650px;
        outline: 1px solid red;
    }
    .pagebreak {
        display: block;
        position: relative;
        background: yellow;
    }
    .flowbox, article{display:inline-block;vertical-align:top;}
    article{width: 305px;}
    <div class="container">
      <h4>
        With problem:
      </h4>
      <div class="textframe a">
        <article contenteditable="true">
          <p>
            <span>
              <span>Foo bar baz</span>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <span>Foo bar baz</span><br>
              <span>Lorem ipsum CLICK ABOVE THIS WORDS sit amet, consectetur adi piscing elit.</span>
              <br>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <br>
              <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
            </span>
          </p>
        </article>
        <div class="flowbox"></div>
      </div>
    </div>

    【讨论】:

    • 首先,我需要那些.flowbowdivs 来强制每行文本在父框之前中断。在我的示例中,您可以看到左侧的框在单词“consectetur”之后换行,但在右侧的框中,允许下一个单词“adi”向上流动一行。此外,在您的解决方案中,.textframe 框似乎不再局限于特定宽度;我也需要那个:)
    • @HelgeFox,现在我添加了.textframe 类,它现在看起来像您的 jsfiddle.net 示例 - 具有特定宽度.flowbow div 放在 &lt;article&gt; 元素之后。请不要忘记 StackOferflow 上的 rools:一篇帖子只允许一个问题(问题)。
    • 我并不是要引入另一个问题,只是澄清了原问题的前提。我会看看你更新的答案。
    • 很遗憾,我无法像您在解决方案中那样为 article 元素设置特定宽度。
    • @HelgeFox,请回答我两个问题: 1. 你用什么
      ? 2. 为什么不能给文章元素设置特定的宽度?
    【解决方案4】:

    我在 Linux/Ubuntu 上使用最新版本的 Chrome,这似乎已经解决了这个问题。我刚刚从文章中删除了 contenteditable 并将其放在您想要编辑的 span 上。

    <article>
          <p>
            <span>
              <span contenteditable="true">Foo bar baz</span>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <span contenteditable="true">Foo bar baz</span>
              <br>
              <span contenteditable="true">Lorem ipsum dolor sit amet, consectetur adi piscing elit.</span>
              <br>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <br>
              <span contenteditable="true">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
            </span>
          </p>
        </article>
    

    【讨论】:

    • 不幸的是,在 Windows 上更新 Chrome(v68 到 v69)并没有帮助。此外,重点是让整个.textframe 可编辑具有使用箭头键浏览换行符和分页符的可能性。
    【解决方案5】:

    如果您的跨度为空,我认为问题在于跨度。我在使用 contenteditable 时遇到了这个问题,所以光标显示在那里,但你无法让它移动。

    我会建议你从你的p 中删除跨度到每个段落,这样如果跨度为空,请尝试在退格/删除时将其删除。

    或者参考CKEDITOR,因为它已经解决了这个问题

    article p, article div
    {
        line-height: 1.25;
        margin-top: 12px;
        margin-bottom: 12px; /*  margin-bottom: 10px; removed for proper pagebreak 31-1-2017*/
        font-family: Helvetica;
    }
    
    .title {
      left: 20px;
    }
    .container {
      float: left;
      width: 400px;
    }
    .textframe {
      width: 311px;
      height: 650px;
      outline: 2px dotted lightblue;
      overflow: hidden;
      margin: 0 15px 0 0;
    }
    .textframe.b {
      left: 380px;
    }
    .textframe article {
      position: relative;
      height: 650px;
    }
    article p {
      margin: 0;
    }
    .pagebreak {
      display: block;
      position: relative;
      background: yellow;
    }
    .flowbox {
      width: 2px;
      height: 650px;
      float: right;
      clear: right;
      outline: 1px solid red;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="container">
      <h4>
        With problem:
      </h4>
      <div class="textframe a">
        <div class="flowbox"></div>
        <article contenteditable="true">
          <p>
            <span>
              <span>Foo bar baz</span>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <span>Foo bar baz</span>
              <br>
              <span>Lorem ipsum dolor sit amet, consectetur adi piscing elit.</span>
              <br>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <br>
              <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
            </span>
          </p>
        </article>
      </div>
    </div>
    <div class="container">
      <h4>
        Without problem:
      </h4>
      <div class="textframe b">
        <article contenteditable="true">
          <p>
            <span>
              <span>Foo bar baz</span>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <span>Foo bar baz</span>
              <br>
              <span>Lorem ipsum dolor sit amet, consectetur adi piscing elit.</span>
              <br>
              <br>
              <span class="pagebreak" contenteditable="false" style="min-height: 80px"></span>
              <br>
              <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
            </span>
          </p>
        </article>
      </div>
    </div>

    【讨论】:

    • 嗯...我不明白你的答案,因为在我的示例中没有空跨度,除了具有pagebreak 类的那些是有目的的。即起到分页符的作用。如果您有更好的解决方案可以在可编辑文本框中创建任意空间,请详细说明:)
    • 我说的是同一个分页类 span,如果 contenteditable 在 p 标记中找到一个空跨度,它的行为与你得到的一样
    • 在删除某个时间点时,它会在该跨度之间发生碰撞,或者它会随机移动到任何地方
    • CKEditor 也有这个问题,当你插入有序或无序列表时,它的行为是一样的
    • 很高兴听到 CKEditor 有同样的问题。但是关于导致该问题的.pagebreak 跨度,是的,我知道。问题是我仍然需要某种方式来下推内容,所以在我看来,我仍然需要那些......
    猜你喜欢
    • 2011-06-13
    • 2014-11-11
    • 2015-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-26
    • 2015-06-19
    • 2018-10-02
    相关资源
    最近更新 更多