3
votes

Comment maintenir l'alignement et le dimensionnement absolus des couches lors du défilement et du redimensionnement?

J'ai besoin de créer l'éditeur wysiwyg comme tel:

  1. Une zone de texte transparente, avec une police transparente est le composant d'édition.
  2. Il est superposé sur un div qui obtient sa valeur de la zone de texte et le rend.
  3. les interactions d'édition se produisent dans la zone de texte, mais la vue provient du div sous-jacent.

De cette façon, nous pouvons rendre le texte en surbrillance de syntaxe, tout en conservant la fonctionnalité d'édition d'une zone de texte.

Mon problème est mon manque d'expertise en CSS. Donc, peu importe le nombre de variantes que j'essaie, il y a toujours un problème avec le retour à la ligne, le défilement ou les alignements qui ne sont pas synchronisés.

Voici une variante. Le composant éditeur a un texte gris transparent tandis que le div sous-jacent est du texte orange, afin que nous puissions voir quand ils s'alignent.

Le défi est de les faire toujours aligner, quel que soit le contenu, le défilement ou la taille .

<div class="scrollable">
  <div id="view" class="content">
  <textarea id="editor" class="content" onInput="update();" class="content"></textarea>
  <div id="richtext" class="content"></div>
</div>
</div>
.scrollable {
  width: 300px;
  height: 100px;
  overflow: scroll;
  border : 1px solid grey;
}

.content {
  font-family: monospace;
  font-size: 18px;
  position: absolute;
  top: 0;
  left: 0;
  border: none;
  overflow: hidden;
  min-width: 100%;
  min-height: 100%;
  outline: none;
  resize: none;
  margin: none;
  padding: 0;
  box-shadow: none;
  box-sizing: border-box;
  white-space: pre;
  display: block;
  overflow: none;
}

#richtext {
  z-index: -10;
  color: orange;
}

#view {
  height: 100%;
  position: relative;
  display: inline-block;
}

#editor {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;  
  background-color: transparent;
  color: #00000055;
  caret-color: black;
}
function update(){
  document.getElementById("richtext").innerHTML = 
  document.getElementById("editor").value;
}


7 commentaires

Ce que vous décrivez n'est pas clair (d'un point de vue technique). Veuillez créer un exécutable exemple minimal reproductible . Pensez également à ajouter les résultats de votre recherche à la question. Sinon, il apparaîtra comme si vous n'en avez effectué aucune.


Je vais travailler pour rendre cela plus clair. Il n'y a aucune obligation de SO pour montrer le travail effectué. Et en fait, mon travail se fait en elm-ui, en elm. La solution à mon problème commence par comprendre les mécanismes d'une solution CSS, mais mon code existant n'aidera personne ici car ce n'est pas un problème spécifique à elm / elm-ui. Si vous êtes curieux, voici le code gist.github.com/z5h /…


Quels que soient les outils, la technologie backend ou le framework frontend utilisés, cela se résume à la façon dont les navigateurs interprètent votre sortie CSS et vous semblez avoir besoin d'aide pour cela. Ne fournissez pas le code source, fournissez un exemple de travail (basé sur la quantité minimale de code de sortie (traité = ce qui frappe le navigateur) nécessaire pour le reproduire). Personne n'a dit qu'il y avait une exigence SO pour montrer votre recherche ou votre code existant, mais cela augmenterait considérablement vos chances d'obtenir une réponse utile, ce qui semble, à son tour, être votre exigence / attente.


@ z5h On dirait que vous avez répondu à votre propre question. Mettez les 2 divs dans un wrapper et donnez au wrapper une hauteur et un overflow: auto


Ok, j'ai ajouté ce que j'ai. Ça ne marche pas. Toute aide est appréciée.


Le moyen le plus simple de résoudre votre problème consiste à supprimer textarea et à utiliser div avec contenteditable = "true" . Vous voudrez peut-être voir comment CKEditor le fait.


@Duannx J'utilise Elm et il existe de nombreux problèmes connus avec contenteditable. Je l'évite exprès.


4 Réponses :


1
votes

Changez la classe .content min-height: 100%; en min-height: 100vh; et changez également white- space: pre; to white-space: pre-wrap;

Voici le violon mis à jour:

<div class="scrollable">
  <div id="view" class="content">
    <textarea id="editor" class="content" onInput="update();" class="content"></textarea>
    <div id="richtext" class="content"></div>
  </div>
</div>
.scrollable {
  width: 300px;
  height: 100px;
  overflow: scroll;
  border: 1px solid grey;
}

.content {
  font-family: monospace;
  font-size: 18px;
  position: absolute;
  top: 0;
  left: 0;
  border: none;
  overflow: hidden;
  min-width: 100%;
  min-height: 100vh;
  outline: none;
  resize: none;
  margin: none;
  padding: 0;
  box-shadow: none;
  box-sizing: border-box;
  white-space: pre-wrap;
  display: block;
  overflow: none;
}

#richtext {
  z-index: -10;
  color: orange;
}

#view {
  height: 100%;
  position: relative;
  display: inline-block;
}

#editor {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: transparent;
  color: #00000055;
  caret-color: black;
}
function update() {
  document.getElementById("richtext").innerHTML =
    document.getElementById("editor").value;
}


1 commentaires

Au fur et à mesure que le contenu s'allonge, il se désynchronise.



4
votes

Avez-vous BESOIN de la zone de texte et du div sous-jacent? J'essayais de trouver un bon éditeur de texte riche récemment et j'ai remarqué que beaucoup de ceux modernes utilisent, juste un div avec contenteditable = "true" comme attribut HTML5. Voici quelques bons exemples de ceci dans le monde réel.


Si vous avez vraiment besoin de vous en tenir aux deux éléments, je les réduirais tous les deux en métal nu en réinitialisant TOUS les attributs:

textarea,
.preview {
  all: initial;
}

Ensuite, construisez le style du texte donc c'est complètement le même sur les deux. font-family , font-size , font-weight , line-height , etc. assurez-vous qu'il y a une bonne réinitialisation ou normalisation CSS en place.

Le problème est que les navigateurs traitent le rendu du texte différemment dans une zone de texte qu'ailleurs, le but serait donc de les forcer à être les mêmes en étant un peu lourds à main levée.


2 commentaires

J'utilise Elm et il existe de nombreux problèmes connus avec contenteditable. Je l'évite exprès.


C'est tout à fait compréhensible. Je vous ai également donné quelques éléments CSS à essayer. Le problème est que les navigateurs traitent le rendu du texte différemment dans une zone de texte qu'ailleurs, le but serait donc de les forcer à être les mêmes en étant un peu lourd.



1
votes

L'alignement se désynchronise car vous finirez par dépasser la hauteur définie de la zone de texte et son défilement interne entre en action.

Pour éviter cela, mettez à jour la hauteur de la zone de texte afin qu'elle s'étende automatiquement.

<div class="scrollable">
  <div id="view" class="content">
  <textarea id="editor" class="content" onInput="update();" class="content"></textarea>
  <div id="richtext" class="content"></div>
</div>
</div>

.scrollable {
  width: 300px;
  height: 100px;
  overflow: scroll;
  border : 1px solid grey;
}

.content {
  font-family: monospace;
  font-size: 18px;
  position: absolute;
  top: 0;
  left: 0;
  border: none;
  overflow: hidden;
  min-width: 100%;
  min-height: 100%;
  outline: none;
  resize: none;
  margin: none;
  padding: 0;
  box-shadow: none;
  box-sizing: border-box;
  white-space: pre;
  display: block;
  overflow: none;
}

#richtext {
  z-index: -10;
  color: orange;
}

#view {
  height: 100%;
  position: relative;
  display: inline-block;
}

#editor {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;  
  background-color: transparent;
  color: #00000055;
  caret-color: black;
}
var richtext = document.getElementById("richtext")
var editor = document.getElementById("editor")

function update(){
  richtext.innerHTML = editor.value;
  
  editor.style.width = 'auto';
  editor.style.width = editor.scrollWidth+'px';
  
  editor.style.height = 'auto';
  editor.style.height = editor.scrollHeight+'px';
}
function update(){
  richtext.innerHTML = editor.value;

  editor.style.width = 'auto';
  editor.style.width = editor.scrollWidth+'px';

  editor.style.height = 'auto';
  editor.style.height = editor.scrollHeight+'px';
}

Edit: il en va de même pour la largeur, évidemment.


0 commentaires

1
votes

Vous avez utilisé la position absolue. Ainsi div et textarea sont placés l'un sur l'autre. supprimer la position, la largeur min et la largeur max pour div et textarea. et utilisez display: flex-inline pour div # view; vous pouvez changer la valeur de l'espace blanc pour un meilleur résultat.


0 commentaires