Compter les mots et caractères : emoji, CJK et limites de formulaire

Ajoutez un compteur propre à votre application sans remplacer le document à chaque frappe. Ce guide fournit un module testé et un exemple complet qui vérifie une limite de 200 caractères en conservant intégralement les brouillons trop longs.
Définir ce que représente un caractère
La longueur d’une chaîne JavaScript compte des unités UTF-16. Un emoji familial ou une lettre avec accent combiné peut contenir plusieurs unités tout en ressemblant à un caractère. Cet exemple compte les graphèmes avec Intl.Segmenter, inclut espaces et sauts de ligne, et normalise CRLF ou CR en LF.
Les mots sont les segments reconnus comme tels pour la langue explicitement choisie du document. Ce n’est pas une promesse de correspondance avec Microsoft Word, le compteur intégré de l’éditeur, la facturation SMS ou les réseaux sociaux. Distinguez ces politiques et expliquez aux auteurs l’unité de votre limite.
Télécharger le module de comptage
Téléchargez le module de statistiques textuelles. Il accepte du texte brut, pas du HTML. Lisez le document avec getPlainText() plutôt que de compter le HTML de getHTMLCode(). Les balises ne sont pas des mots ; testez explicitement les séparations de paragraphes et les marqueurs produits par votre extraction.
Le module nécessite la segmentation sur le navigateur ou le serveur. Si elle manque, il signale une erreur au lieu de basculer discrètement vers un découpage aux espaces, inadapté au chinois et au japonais.
Relier le compteur au contrôle du formulaire
Servez les fichiers de l’éditeur et le module depuis votre application. L’exemple vérifie uniquement les conditions du formulaire, sans envoyer ni enregistrer de contenu. Le calcul est regroupé pendant la saisie, puis refait immédiatement lors de la soumission. Un refus laisse le brouillon intact.
<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css">
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<style>body{font:16px/1.6 system-ui,sans-serif;max-width:960px;margin:24px auto;padding:0 16px}button{min-height:44px;padding:8px 16px}</style>
<h1>Word and character counter</h1>
<form id="message-form">
<div style="max-width:100%;overflow-x:auto"><div id="editor"></div></div>
<p id="counter"></p>
<p>Limit: 200 graphemes, including spaces and line breaks. This demo does not save.</p>
<button type="submit">Check submission</button>
<p id="result" role="status" aria-live="polite"></p>
</form>
<script type="module">
import { countText } from "/js/text-metrics.mjs";
const editor = new RichTextEditor("#editor", { toolbar: "full", height: "320px", showStatistics: false });
editor.setHTMLCode("<p>Write a short note.</p>");
const LOCALE = "en"; // Choose the document language, not necessarily the UI language.
const LIMIT = 200;
const counter = document.getElementById("counter");
const result = document.getElementById("result");
function updateCount() {
try {
const metrics = countText(editor.getPlainText(), LOCALE);
counter.textContent = metrics.words + " words · " + metrics.characters +
" / " + LIMIT + " characters (graphemes)";
return metrics;
} catch {
counter.textContent = "Counter unavailable. Check locale and Intl.Segmenter support.";
return null;
}
}
let timer, checkedText = null;
editor.attachEvent("change", () => {
if (editor.getPlainText() !== checkedText) result.textContent = "";
clearTimeout(timer);
timer = setTimeout(updateCount, 200);
});
document.getElementById("message-form").addEventListener("submit", event => {
event.preventDefault();
clearTimeout(timer);
const metrics = updateCount(); // Do not trust a delayed displayed count.
checkedText = editor.getPlainText();
if (!metrics) { result.textContent = "Cannot validate this count."; return; }
if (metrics.characters > LIMIT) {
result.textContent = "Too long. Shorten the draft; your content is still here.";
editor.focus();
return;
}
result.textContent = "Within limit. Ready for your server-side validation.";
// Add your authenticated save here. This demo sends no request.
});
updateCount();
// Exposed only so you can inspect this standalone demo in developer tools:
window.counterDemo = { editor, updateCount };
</script>
Appliquer la même politique sur le serveur
Répétez la validation sur le serveur à partir du document transmis et d’une politique d’extraction convenue. Ne faites confiance ni au nombre fourni par le client ni à un champ de texte brut séparé. Vérifiez à part les droits, l’assainissement HTML et la taille de la requête en octets : 200 graphèmes ne garantissent pas un petit contenu HTML.
Après extraction du texte brut, le module est réutilisable dans une application Node.js moderne. Alignez normalisation et langue et partagez les cas de test : les versions du navigateur et du serveur peuvent utiliser des données Unicode et de segmentation différentes. Pour une limite contractuelle, choisissez et versionnez une politique serveur faisant autorité.
Tester les langues et modes de saisie
- Vérifiez le texte vide, les espaces, les sauts de paragraphe et CRLF.
- Essayez un emoji familial, un drapeau et un accent combiné. Ne les découpez pas pour respecter la limite.
- Utilisez de vrais documents chinois et japonais plutôt que de supposer que les mots sont séparés par des espaces.
- Collez trop de contenu et soumettez immédiatement, avant la mise à jour du compteur affiché. Le contrôle doit refuser sans perdre le brouillon.
- Supprimez assez de texte puis réessayez. Testez aussi la composition IME et l’annulation dans votre intégration.
Le compteur ne gère pas les règles de contenu obligatoire et ne décide pas si un document composé d’images est valide. Réécrire constamment le HTML ou annuler chaque frappe peut endommager sélection, historique et saisie IME.
Poursuivre avec les guides associés
Consultez l’extraction de texte brut, la validation de formulaires, l’enregistrement automatique sur serveur et les choix de comptage des mots.