Dans ce chapitre, nous aborderons la sélection dans le document, ainsi que la sélection dans les champs de formulaire, tels que <input>.
JavaScript peut accéder à une sélection existante, sélectionner/désélectionner des nœuds du DOM dans leur ensemble ou partiellement, supprimer le contenu sélectionné du document, l’envelopper dans une balise, etc.
Vous pouvez trouver quelques recettes de tâches courantes à la fin du chapitre, dans la section “Résumé”. Peut-être que cela couvre vos besoins actuels, mais vous obtiendrez beaucoup plus si vous lisez le texte en entier.
Les objets Range et Selection sont faciles à comprendre, et vous n’aurez besoin d’aucune recette pour les faire faire ce que vous voulez.
Range
Le concept de base de la sélection est la plage (Range), qui est essentiellement une paire de “points limites”: le début et la fin de la plage.
Un objet Range est créé sans paramètres :
let range = new Range();
Ensuite, nous pouvons définir les limites de la sélection en utilisant range.setStart(node, offset) et range.setEnd(node, offset).
Comme vous pouvez le deviner, nous allons utiliser les objets Range pour la sélection, mais créons d’abord quelques-uns de ces objets.
Sélection partielle du texte
La chose intéressante est que le premier argument node dans les deux méthodes peut être soit un noeud de texte ou un noeud d’élément, et la signification du deuxième argument dépend de cela.
Si node est un noeud de texte, alors offset doit être la position dans son texte.
Par exemple, étant donné l’élément <p>Hello</p>, nous pouvons créer la plage contenant les lettres “ll” comme suit :
<p id="p">Hello</p>
<script>
let range = new Range();
range.setStart(p.firstChild, 2);
range.setEnd(p.firstChild, 4);
// toString d'une plage renvoie son contenu sous forme de texte
console.log(range); // ll
</script>
Ici, nous prenons le premier enfant de <p> (c’est le noeud texte) et nous spécifions les positions du texte à l’intérieur de celui-ci :
Sélection des noeuds d’éléments
Alternativement, si node est un noeud d’élément, alors offset doit être le numéro de l’enfant.
C’est pratique pour faire des plages qui contiennent les noeuds dans leur ensemble, et non pas s’arrêter quelque part dans leur texte.
Par exemple, nous avons un fragment de document plus complexe :
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
Voici sa structure DOM avec les nœuds d’élément et de texte :
Créons une plage pour "Example: <i>italic</i>".
Comme nous pouvons le voir, cette phrase est composée d’exactement deux enfants de <p>, avec les indices 0 et 1 :
-
Le point de départ a
<p>commenodeparent, et0comme décalage (offset).On peut donc le définir comme
range.setStart(p, 0). -
Le point de fin a aussi
<p>commenodeparent, mais2comme décalage (il spécifie la plage jusqu’à, mais sans inclureoffset).On peut donc le définir comme
range.setEnd(p, 2).
Voici la démo. Si vous l’exécutez, vous pouvez voir que le texte est sélectionné :
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<script>
let range = new Range();
range.setStart(p, 0);
range.setEnd(p, 2);
// toString d'une plage renvoie son contenu sous forme de texte, sans balises
console.log(range); // Example: italic
// appliquer cette plage pour la sélection du document (expliqué plus loin)
document.getSelection().addRange(range);
</script>
Voici un banc d’essai plus flexible dans lequel vous pouvez définir des valeurs de début et de fin de plage et explorer d’autres variantes :
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
From <input id="start" type="number" value=1> – To <input id="end" type="number" value=4>
<button id="button">Click to select</button>
<script>
button.onclick = () => {
let range = new Range();
range.setStart(p, start.value);
range.setEnd(p, end.value);
// appliquer la sélection, expliquée plus loin
document.getSelection().removeAllRanges();
document.getSelection().addRange(range);
};
</script>
E.g. en sélectionnant dans le même <p> de l’offset 1 à 4 on obtient <i>italic</i> and <b>bold</b>:
Nous ne sommes pas obligés d’utiliser le même noeud dans setStart et setEnd. Une plage peut s’étendre sur de nombreux noeuds non liés. Il est seulement important que la fin soit après le début dans le document.
Sélection d’un plus grand fragment
Faisons une sélection plus grande dans notre exemple, comme ceci :
Nous savons déjà comment faire. Nous devons juste définir le début et la fin comme un offset relatif dans les nœuds de texte.
Nous devons créer une plage, qui :
- commence à la position 2 dans
<p>firstChild (en prenant toutes les lettres sauf les deux premières de "Example: "). - se termine à la position 3 dans
<b>firstChild (en prenant les trois premières lettres de “bold”, mais pas plus) :
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<script>
let range = new Range();
range.setStart(p.firstChild, 2);
range.setEnd(p.querySelector('b').firstChild, 3);
console.log(range); // ample: italic and bol
// utiliser cette plage pour la sélection (expliqué plus loin)
window.getSelection().addRange(range);
</script>
Comme vous pouvez le voir, il est assez facile de créer une plage de ce que l’on veut.
Si nous voulons prendre les noeuds dans leur ensemble, nous pouvons passer des éléments dans setStart/setEnd. Sinon, nous pouvons travailler au niveau du texte.
Propriétés de la plage
L’objet range que nous avons créé dans l’exemple ci-dessus a les propriétés suivantes :
startContainer,startOffset– nœud et offset du début,- dans l’exemple ci-dessus : premier noeud de texte à l’intérieur de
<p>et2.
- dans l’exemple ci-dessus : premier noeud de texte à l’intérieur de
endContainer,endOffset– noeud et offset de la fin,- dans l’exemple ci-dessus : premier noeud de texte dans
<b>et3.
- dans l’exemple ci-dessus : premier noeud de texte dans
collapsed– booléen,truesi la plage commence et se termine sur le même point (donc il n’y a pas de contenu à l’intérieur de la plage),- dans l’exemple ci-dessus :
false.
- dans l’exemple ci-dessus :
commonAncestorContainer– l’ancêtre commun le plus proche de tous les noeuds de la plage,- dans l’exemple ci-dessus :
<p>.
- dans l’exemple ci-dessus :
Méthodes de sélection de plages
Il existe de nombreuses méthodes pratiques pour manipuler les plages.
Nous avons déjà vu setStart et setEnd, voici d’autres méthodes similaires.
Définir le début de la plage :
setStart(node, offset)définit le début à : positionoffsetdansnode.setStartBefore(node)définit le début à : juste avantnode.setStartAfter(node)définit le début à : juste aprèsnode.
Définir la fin de la plage (méthodes similaires) :
setEnd(node, offset)définit la fin à : positionoffsetdansnode.setEndBefore(node)définit la fin à : juste avantnode.setEndAfter(node)définit la fin à : juste aprèsnode.