web-dev-qa-db-fra.com

Curseur dans contenteditable ne se comporte pas correctement dans Chrome

J'ai un problème où le curseur saute au mauvais endroit quand l'utilisateur clique à l'intérieur de contenteditable div mais en dehors du texte réel. Cela semble être un problème uniquement dans les versions les plus récentes de Chrome (et également d'Opera): c'est par hasard que j'ai testé mon exemple dans un navigateur plus ancien (Chrome version 55) et que le problème n'était pas présent du tout. Pas de problème dans Edge/IE11/FireFox non plus.

Le problème se produit uniquement lorsque l'utilisateur clique derrière une ligne de texte ou sur une ligne vide située entre les deux balises jaunes divs avec la classe pagebreak. Le curseur se termine au-dessus de la première pagebreakdiv. Et je ne sais pas s'il est directement lié ou non, mais le problème disparaît lorsque la variable div avec la classe flowbox est supprimée. Malheureusement, je ne peux pas supprimer cette div avec la classe flowbox de l'application.

J'ai rassemblé un exemple illustrant mon problème dans ce violon: 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>

Le problème sur le champ de texte de gauche, celui de droite fonctionne comme prévu car le .flowboxdiv a été supprimé.

Modifier 1:

J'ai créé un nouvel exemple qui est peut-être plus facile à comprendre. Les autres éléments du .textframe (tels que les éléments pagebreak et flowbox) ont effectivement des objectifs spécifiques et ne peuvent donc pas être ignorés. Voici le lien vers la démo améliorée: https://jsfiddle.net/q4pu37dn/15/

6
HelgeFox

Mise à jour 3 (démo 3)


 enter image description here

Changements

J'ai remarqué qu'il n'y a plus de position: relative utilisé dans le code OP le plus courant ce qui est bien, mais je crois que c'était à cause de l'oubli:

<span class='pagebreak spacer' contenteditable = "false" ></span>

Je crois que vous avez utilisé à l'origine contenteditable="false" afin de donner à votre .pagebreak une fonctionnalité supplémentaire et de l'empêcher d'être supprimé. Je les ai donc rajoutés.


Comparaison

Demo 3 a ma solution côte à côte avec code OP pour comparer le comportement. Demo 3 comporte également 2 boutons (1 pour chaque éditeur de contenu) mettant en évidence chaque <span> de texte. Ce qui suit est une liste des classes du code OP (l'éditeur de contenu à droite) et la liste de chaque classe équivalente de mon code (éditeur de contenu à gauche).

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

Le PO se préoccupe de 2 exigences:

  1. Lorsque vous cliquez sur les zones vides entourant le <span>s, le curseur passe au coin de la zone de contenu.

  2. Le nombre de caractères par ligne doit correspondre à la capacité actuelle du code OP.

Ce problème existe depuis des années, mais la raison en est le nébuleux. Par conséquent, si vous traitez cette aberration comme un simple comportement, vous pouvez simplement le contrer en instillant un comportement différent. 

Demo2 et Demo3 respectez ces critères en appliquant simplement les jeux de règles suivants:

Démo 2

article p {display: table;...

Démo 3

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

Le comportement des tables-cellules est rigide et bien établi, et AFAIK est le seul élément non- remplacé qui se conforme par défaut à son contenu et aux éléments de table environnants. En prime, un élément avec display: table-cell (pas <td>) n'est pas obligé d'être imbriqué dans un <tr> qui se trouve dans un <table>.


Démo 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 Optionak 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 -->


Mise à jour 2 (démo 2)


OP concernant Demo 1:

"Vous l'avez résolu pour mon exemple artificiel, oui. Malheureusement, il n'est pas possible de définir ces valeurs sur les éléments de l'application réelle, le flux est totalement hors d'atteinte."

Voir Demo 2 , cela fonctionne mieux que Demo 1 . Comme il n'utilise que des éléments positionnés, il n'y a pas de conflits en flux. Pour adapter Demo 2 à votre application, il vous suffit d'ajouter position:relative aux éléments parents. Le style relavent est le suivant:

article p {display: table;...

Il était nécessaire d'attribuer position:relative à tout ce qui était imbriqué dans .textframe, sinon les éléments static n'interagiraient pas avec les éléments positionnés. Il existe des règles auxquelles adhèrent les tables et les composants de table, qui s'appliquent non seulement à son contenu, mais également à la manière dont elles interagissent avec les éléments voisins.


Démo 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>


Des références

MDN - Float

MDN - Position

Astuces CSS - Positionnement absolu à l'intérieur du positionnement relatif

Astuces CSS - Tout sur les flotteurs

display: table/table-cell

Coupure du mot: break-Word


1
zer00ne

Ce problème se produit car vous utilisez float: right;.

N'utilisez pas la propriété CSS float: right; si vous n'en avez pas besoin. Vous pourriez avoir beaucoup de problèmes avec cela. Dans votre cas, vous n'en avez pas besoin. Au lieu de cela, vous devez utiliser les éléments inline-block en tant que <div class="flowbox"> et <article contenteditable="true">.

L'exemple minimal avec float:right (AVEC problème)

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

La solution

L'exemple minimal avec display:inline-block (sans problème)

Remarque: maintenant j'ai placé votre élément <div class="flowbox"></div> après l'élément <article>.

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

0
Bharata

Le problème est lié à l'affichage. Je suis également novice en la matière, mais lorsque j'ai modifié votre étendue pour diviser cela a bien fonctionné, faites-moi savoir si c'est juste ou si je n'ai pas bien compris votre question.

Maintenant, je ne sais pas pourquoi cela se produisait, donc je ne suis pas en mesure de vous fournir une explication détaillée.

Remarque - Après cela, l'utilisation de span et div ne sera plus appropriée, vous devrez donc changer de div à d'autres endroits également.

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

0
Abhilash Bhargava

J'utilise la dernière version de Chrome sur Linux/Ubuntu et cela semble avoir résolu le problème. Je viens de supprimer contenteditable de l'article et de le mettre sur les plages que vous souhaitez modifier.

<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>
0
Lazar Nikolic

Je pense que le problème est de durée si vous avez une durée vide. J'ai parcouru ce problème alors que je travaillais avec contenteditable. Le curseur y apparaît, mais vous ne pouvez pas le faire bouger.

Je vous suggérerai de supprimer span de votre p à chaque paragraphe afin que, si span est vide, essayez de le supprimer en effectuant un retour arrière/une suppression.

Ou référez-vous CKEDITOR car il a résolu ce problème

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>

0
Nikhil Ghuse