Le retour du compteur de caractères

Après être revenu dernièrement sur ma série de tutos d’introduction aux canvas, j’ai décidé de m’attaquer à l’un de mes anciens articles me valant le plus d’affichage sur le moteur de recherche d’Alphabet : le compteur de caractères.

Comme pour mes derniers articles traitant de JavaScript, une fois de plus je n’utiliserai que des fonctions natives, sans aucune librairie.

Organisation du nouveau code

Le nouveau code sera réparti dans deux fonctions : une fonction d’initialisation, et la fonction en charge du compte des caractères. Cette dernière fonction étant appelée une fois à l’initialisation, pour pouvoir être utilisable sur une page où les champs de formulaire contiennent déjà du texte (une amélioration par rapport à l’ancien code), et ensuite attachée à un événement sur les champs.

Un des gros changement est la manière dont on va passer les paramètres à notre champ compteur de caractères : au lieu de passer ces paramètres à notre fonction d’initialisation, on va utiliser des attributs HTML.

Ces attributs HTML sont au nombre de 5 :

  • pour la longueur maximale de notre champ, on va utiliser l’attribut maxlength, qui en plus permet d’empêcher l’ajout de caractères au-delà de la limite, ce que l’ancienne version de faisait pas (cet attribut existait déjà à l’époque de la rédaction du premier article)
  • pour la valeur de seuil, on va utiliser un attribut de données (data-*) data-threshold
  • pour la couleur « normale », on utilisera un attribut data-normal
  • pour la couleur à utiliser quand la valeur de seuil est atteinte, ce sera l’attribut data-alert
  • pour la couleur utilisée quand plus aucun caractère ne peut être rajouté, on utilisera un attribut data-reached

La fonction d’initialisation

Cette fonction prend un seul paramètre d’entrée : l’objet à utiliser par la suite.

Premier truc à faire, stocker les réglages de notre compteur. Pour cela on utilise un objet settings, dans lequel on stocke les valeurs des différents data-attributes, récupérées via la propriété dataset de notre objet. Si les attributs n’ont pas été définis, une valeur par défaut est utilisée.

const settings = {
    'threshold' : (t.dataset.threshold?parseInt(t.dataset.threshold): Math.floor(t.maxLength/2)),
    'normalColor' : (t.dataset.normal?t.dataset.normal: '#000000'),
    'alertColor' : (t.dataset.alert?t.dataset.alert: '#FFA500'),
    'reachedColor' : (t.dataset.reached?t.dataset.reached: '#FF0000')
};

Viens ensuite le moment de créer l’élément HTML à utiliser pour afficher l’état du compteur.

const countSpan = document.createElement('span'); // création dʼun nouvel élément HTML
countSpan.style.setProperty('margin-left', '8px'); // on lui ajoute une propriété de style
                
t.after(countSpan); // on lʼinjecte dans le DOM après la cible du script

Puis on appelle une première fois la fonction en charge du décompte, et on l’attache à l’événement ‘input‘.

charCount(t, settings);

t.addEventListener('input', (e) => { charCount(e.target, settings) });

La fonction de décompte

La fonction de décompte prend deux paramètres d’entrée : l’objet cible et un objet contenant les paramètres à utiliser.

Première étape, on récupère le nombre de caractères dans le champ, puis, on calcule le nombre de caractères restants.

let nbChars = t.value.length;
let remChars = t.maxLength - nbChars;

Viens ensuite le changement de l’affichage.

const countSpan = t.nextElementSibling; // sélectionne le <span>
                
let color = s.normalColor;

let text = remChars + ' caractères restants'; 

// Change la couleur et le texte selon la situation
if (remChars <= s.threshold && remChars > 0) {
    color = s.alertColor;
} else if(remChars == 0) {
    color = s.reachedColor;
    text = 'Maximum atteint';
}

countSpan.style.setProperty('color', color); // couleur du texte
countSpan.innerText = text; // contenu du <span>

Utilisation du code

Ce code est utilisable de manière très simple : on sélectionne le/les champ(s) auquel/auxquels on veut rattacher un compteur via document.getElementById() ou document.querySelectorAll(), et ensuite on lance la fonction d’initialisation sur chaque champ sélectionné.

document.querySelectorAll('.charcount').forEach((t) => {
    initCount(t);
});

Autre utilisation possible

Dernièrement j’ai commencé à m’intéresser aux web components, et c’est ce qui m’a donné envie de revenir sur cette histoire de compteur de caractères.

Dans notre cas, pas besoin de créer un élément 100 % personnalisé. On va se contenter de simplement étendre les classes des <input> (HTMLInputElement) et des <textarea> (HTMLTextAreaElement).

Ces classes étendues ne contiennent que un constructeur, qui après l’appel obligatoire à super() lance la fonction d’initialisation sur l’objet lui-même.

class CharCountInput extends HTMLInputElement {
    constructor() {
        super();
        
        initCount(this);
    }
}

class CharCountTextArea extends HTMLTextAreaElement {
    constructor() {
        super();

        initCount(this);
    }
} 

Viens ensuite la « création » de ces nouveaux objets via customElements.define() :

customElements.define('charcount-input', CharCountInput, { extends: 'input' });
customElements.define('charcount-textarea', CharCountTextArea, { extends: 'textarea' });

Pour utiliser ces nouveaux types d’objet, il suffit de rajouter un attribut is aux champs devant être de ce nouveau type.

Pour conclure

J’espère une fois de plus vous avoir appris quelque chose ou que cet article vous aura permis de résoudre un problème dans votre code.

Le résultat final est visible dans la partie démonstration de mon site.

À bientôt pour d’autres aventures codistiques !

Laisser un commentaire