Maîtriser contenteditable=plaintext-only : le guide technique pour des saisies propres et sécurisées

Schéma technique comparant une zone de saisie
classique polluée par du HTML et une zone utilisant plaintext-only propre

Dans le développement d’interfaces modernes, la gestion des entrées utilisateur est un défi permanent. Vous avez sûrement déjà été confronté au problème suivant : un utilisateur copie-colle un texte riche depuis une page web ou un document Word dans votre zone de commentaire, et voilà que votre mise en page explose, polluée par des balises <span> inutiles, des styles inline, voire des scripts malveillants.

Pendant longtemps, nous avons utilisé des textarea pour leur simplicité, au prix d’une gestion complexe de l’ auto-expansion. Ou alors, nous avons forcé contenteditable en développant des usines à gaz en JavaScript pour nettoyer les données à la volée.

Il existe désormais une solution native, robuste et extrêmement performante : l’attribut contenteditable="plaintext-only".

Qu’est-ce que contenteditable=“plaintext-only” ?

Il s’agit d’une valeur spécifique pour l’attribut HTML contenteditable. Lorsqu’elle est appliquée à un élément ( généralement un <div> ou un <span>), le navigateur restreint strictement le comportement de saisie et de collage.

Contrairement au mode contenteditable="true" classique qui autorise la création de structures DOM complexes (nœuds de texte, balises de formatage, images), le mode plaintext-only impose une contrainte forte : seuls les caractères textuels et les retours à la ligne sont acceptés.

Tout contenu riche collé est immédiatement converti en texte brut par le moteur de rendu du navigateur avant même d’atteindre votre gestionnaire d’événements.

Pourquoi est-ce un levier stratégique ?

1. Sécurité renforcée par le design

Le premier argument est la sécurité. En autorisant du texte riche via contenteditable="true", vous vous exposez potentiellement à des failles XSS (Cross-Site Scripting) si votre processus de nettoyage (sanitization) est incomplet ou contourné.

Avec plaintext-only, la surface d’attaque est drastiquement réduite. Le navigateur n’interprète pas les balises HTML insérées. Un attaquant qui tente d’injecter <script>alert(1)</script> verra ces caractères s’afficher comme du texte littéral, sans aucune exécution. C’est le principe de la défense en profondeur par le choix technologique.

2. Performance et expérience utilisateur (UX)

Dans un contexte où chaque milliseconde compte, éviter le traitement JavaScript lourd est crucial.

  • Réduction du DOM : Plus besoin de parser des arbres DOM complexes générés par des copier-coller malveillants.
  • Auto-expansion native : En utilisant un <div> avec contenteditable="plaintext-only", vous bénéficiez nativement d’une zone qui s’adapte à la hauteur du contenu, sans écouteurs d’événements JavaScript input complexes pour recalculer la hauteur (comme c’était le cas pour les textarea auto-ajustables).

3. Cohérence du contenu

Vos bases de données recevront des données uniformes. Fini les mélanges de polices, les couleurs disparates et les structures DOM inattendues qui rendent l’affichage ou le traitement backend (comme la génération de PDF ou l’exportation) imprévisibles.

Implémentation technique

L’implémentation est triviale, mais nécessite une attention particulière sur le style CSS pour conserver l’aspect d’une zone de saisie.

.input-field {
  /* Utilisation de clamp pour une largeur fluide et réactive */
  inline-size    : clamp(18.75rem, 50%, 50rem);
  min-block-size : 1.5em;
  padding        : .625rem;
  border         : .0625rem solid #ccc;
  /* Assurer une typographie lisible */
  font-family    : sans-serif;

  /* Éviter les surécritures inutiles avec :not */
  &:not([contenteditable='plaintext-only']) {
    border-color : red;
  }
}

Voici comment structurer votre composant HTML :


<div
  class="input-field"
  contenteditable="plaintext-only"
  role="textbox"
  aria-multiline="true"
  placeholder="Enter your text here...">
</div>

Le rôle de l’accessibilité

Il est indispensable d’ajouter role=textbox et aria-multiline=true pour que les technologies d’assistance comprennent qu’il s’agit d’un champ de saisie textuel. Sans cela, l’élément n’est pas correctement interprété comme un contrôle de formulaire.

Comparaison avec les alternatives

ApprocheSécuritéPerformanceEffort de dev
textareaÉlevéeÉlevéeMoyen (auto-resize)
contenteditable=trueFaibleFaibleTrès élevé (nettoyage)
contenteditable=plaintext-onlyÉlevéeÉlevéeTrès faible

Limitations et bonnes pratiques

Bien que puissant, ce mode a ses limites :

  1. Support navigateur : Il est largement supporté dans les navigateurs modernes (Chromium, WebKit). Vérifiez toujours la compatibilité si vous ciblez des environnements très spécifiques.
  2. Gestion des événements : Même si le texte est “propre”, vous devez toujours gérer les événements input ou blur pour capturer la donnée et l’envoyer à votre backend.
  3. Le placeholder : contenteditable ne supporte pas l’attribut placeholder nativement. Vous devrez utiliser une astuce CSS avec la pseudo-classe :empty pour afficher un texte d’aide lorsque le champ est vide.
[contenteditable='plaintext-only']:empty::before {
  content : attr(data-placeholder);
  color   : #888;
}

Conclusion

Le passage à contenteditable="plaintext-only" n’est pas seulement une amélioration syntaxique. C’est un changement de paradigme qui place la sécurité et la performance au cœur du design de vos interfaces. En déléguant la contrainte de formatage au navigateur, vous libérez du temps de développement pour des fonctionnalités à plus haute valeur ajoutée, tout en garantissant une expérience utilisateur cohérente et rapide.

N’attendez pas pour auditer vos champs de saisie actuels et migrer vers cette approche native.


FAQ

Est-ce que je peux quand même autoriser les emojis ? Oui. Les emojis sont des caractères Unicode standard. plaintext-only les autorise parfaitement car ils ne constituent pas du formatage HTML.
Comment récupérer la valeur avec JavaScript ? Vous accédez au contenu exactement comme pour n'importe quel élément : utilisez element.innerText ou element.textContent pour récupérer le texte brut.
Qu'arrive-t-il si l'utilisateur essaie de coller du texte avec des liens ? Le navigateur va automatiquement extraire uniquement la chaîne de caractères du lien, sans conserver la balise <a> ni l'attribut href. Le lien deviendra du texte brut non cliquable.
Puis-je utiliser cela dans un formulaire complexe ? Oui, mais attention : un div n'est pas soumis automatiquement avec le formulaire comme un input ou textarea. Vous devez capturer le contenu en JavaScript et l'injecter dans un champ hidden lors de la soumission du formulaire.
Lionel Péramo
Lionel Péramo
Expert Performance Web & Écoconception

Développeur full stack et créateur du framework OTRA (PHP) et de la librairie EcoComposer. J'écris pour rendre le web plus rapide et inclusif.

En savoir plus →