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.
Techniquement, setStart/setEnd peuvent faire nâimporte quoi, mais plus de mĂ©thodes fournissent plus de commoditĂ©.
Dans toutes ces mĂ©thodes, node peut ĂȘtre un noeud de texte ou dâĂ©lĂ©ment : pour les noeuds de texte, offset saute autant de caractĂšres, tandis que pour les noeuds dâĂ©lĂ©ments, autant de noeuds enfants.
Encore plus de méthodes pour créer des plages :
selectNode(node)dĂ©finit la plage pour sĂ©lectionner lenodeentier.selectNodeContents(node)sĂ©lectionne le contenu dunodedans son intĂ©gralitĂ©.collapse(toStart)sitoStart=true, dĂ©finissez end=start, sinon dĂ©finissez start=end, ce qui rĂ©duit la plage de sĂ©lection.cloneRange()crĂ©e une nouvelle plage avec le mĂȘme dĂ©but et la mĂȘme fin.
MĂ©thodes dâĂ©dition des plages
Une fois que la plage est créée, nous pouvons manipuler son contenu en utilisant ces méthodes :
deleteContents()â supprime le contenu de la plage du document.extractContents()â supprime le contenu de la plage du document et le retourne en tant que DocumentFragment.cloneContents()â clone le contenu dâune plage et le renvoie en tant que DocumentFragment.insertNode(node)â InsĂšrenodedans le document au dĂ©but de la plage.surroundContents(node)â entourenodedu contenu de la plage. Pour que cela fonctionne, la plage doit contenir Ă la fois des balises dâouverture et de fermeture pour tous les Ă©lĂ©ments quâelle contient : pas de plages partielles comme<i>abc.
Avec ces mĂ©thodes, nous pouvons faire pratiquement nâimporte quoi avec les noeuds sĂ©lectionnĂ©s.
Voici le banc dâessai pour les voir en action :
Cliquez sur les boutons pour exécuter des méthodes sur la sélection, "resetExample" pour la réinitialiser.
<p id="p">Example: <i>italic</i> and <b>bold</b></p>
<p id="result"></p>
<script>
let range = new Range();
// Chaque méthode démontrée est représentée ici :
let methods = {
deleteContents() {
range.deleteContents()
},
extractContents() {
let content = range.extractContents();
result.innerHTML = "";
result.append("extracted: ", content);
},
cloneContents() {
let content = range.cloneContents();
result.innerHTML = "";
result.append("cloned: ", content);
},
insertNode() {
let newNode = document.createElement('u');
newNode.innerHTML = "NEW NODE";
range.insertNode(newNode);
},
surroundContents() {
let newNode = document.createElement('u');
try {
range.surroundContents(newNode);
} catch(e) { console.log(e) }
},
resetExample() {
p.innerHTML = `Example: <i>italic</i> and <b>bold</b>`;
result.innerHTML = "";
range.setStart(p.firstChild, 2);
range.setEnd(p.querySelector('b').firstChild, 3);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
};
for(let method in methods) {
document.write(`<div><button onclick="methods.${method}()">${method}</button></div>`);
}
methods.resetExample();
</script>
Il existe également des méthodes permettant de comparer des plages, mais elles sont rarement utilisées. Si vous en avez besoin, veuillez vous reporter à la spec ou au manuel MDN.
Sélection
Range est un objet gĂ©nĂ©rique pour gĂ©rer les plages de sĂ©lection. Bien que la crĂ©ation dâune Range ne signifie pas que nous voyons une sĂ©lection Ă lâĂ©cran.
Nous pouvons crĂ©er des objets Range, les faire circuler â ils ne sĂ©lectionnent rien visuellement par eux-mĂȘmes.
La sĂ©lection du document est reprĂ©sentĂ©e par un objet Selection, qui peut ĂȘtre obtenu par window.getSelection() ou document.getSelection(). Une sĂ©lection peut inclure zĂ©ro ou plusieurs plages. Câest du moins ce que dit la spĂ©cification de Selection API. En pratique cependant, seul Firefox permet de sĂ©lectionner plusieurs plages dans le document en utilisant Ctrl+click (Cmd+click pour Mac).
Voici une capture dâĂ©cran dâune sĂ©lection avec 3 plages, rĂ©alisĂ©e dans Firefox :
Les autres navigateurs prennent en charge au maximum 1 plage. Comme nous allons le voir, certaines mĂ©thodes de Selection impliquent quâil peut y avoir plusieurs plages, mais lĂ encore, dans tous les navigateurs sauf Firefox, il y a au maximum 1 plage.
Voici une petite démo qui montre la sélection actuelle (sélectionner quelque chose et cliquer) sous forme de texte :
Propriétés de Selection
Comme nous lâavons dit, une sĂ©lection peut en thĂ©orie contenir plusieurs plages. Nous pouvons obtenir ces objets range en utilisant la mĂ©thode :
getRangeAt(i)â obtient la i-iĂšme plage, en commençant par0. Dans tous les navigateurs sauf Firefox, seul0est utilisĂ©.
Il existe également des propriétés qui sont souvent plus pratiques.
Comme pour une plage, un objet selection a un dĂ©but, appelĂ© âanchorâ, et une fin, appelĂ©e âfocusâ.
Les principales propriétés de selection sont :
anchorNodeâ le noeud oĂč la sĂ©lection commence.anchorOffsetâ le dĂ©calage (offset) dansanchorNodeoĂč la sĂ©lection commence.focusNodeâ le noeud oĂč la sĂ©lection se termine.focusOffsetâ le dĂ©calage dansfocusNodeoĂč la sĂ©lection se termine.isCollapsedâtruesi la sĂ©lection ne sĂ©lectionne rien (plage vide), ou si elle nâexiste pas.rangeCountâ nombre de plages dans la sĂ©lection, maximum1dans tous les navigateurs sauf Firefox.
Il y a une diffĂ©rence importante entre anchor/focus dâune sĂ©lection et start/end dâun objet Range.
Comme nous le savons, les objets Range ont toujours leur début avant leur fin.
Pour les sĂ©lections, ce nâest pas toujours le cas.
La sĂ©lection dâun objet avec une souris peut se faire dans les deux sens : de gauche Ă droite ou de droite Ă gauche.
En dâautres termes, lorsque le bouton de la souris est enfoncĂ©, puis quâil avance dans le document, sa fin (focus) se trouvera aprĂšs son dĂ©but (anchor).
E.g. si lâutilisateur commence Ă sĂ©lectionner avec la souris et passe de âExampleâ Ă âitalicâ :
âŠMais la mĂȘme sĂ©lection pourrait ĂȘtre faite en sens inverse : en partant de âitalicâ vers âExampleâ (sens inverse), alors sa fin (focus) sera avant le dĂ©but (anchor) :
ĂvĂ©nements de sĂ©lection
Il y a des événements pour suivre la trace de la sélection :
elem.onselectstartâ quand une sĂ©lection dĂ©bute spĂ©cifiquement sur lâĂ©lĂ©mentelem(ou Ă lâintĂ©rieur de celui-ci). Par exemple, lorsque lâutilisateur appuie sur le bouton de la souris sur cet Ă©lĂ©ment et commence Ă dĂ©placer le pointeur.- En empĂȘchant lâaction par dĂ©faut, on annule le dĂ©marrage de la sĂ©lection. Le dĂ©marrage dâune sĂ©lection Ă partir de cet Ă©lĂ©ment devient donc impossible, mais lâĂ©lĂ©ment reste tout de mĂȘme sĂ©lectionnable. Le visiteur doit simplement dĂ©marrer la sĂ©lection Ă partir dâun autre endroit.
document.onselectionchangeâ quand une sĂ©lection change ou commence.- Attention : ce gestionnaire ne peut ĂȘtre dĂ©fini que sur
document, il suit toutes les sĂ©lections quâil contient.
- Attention : ce gestionnaire ne peut ĂȘtre dĂ©fini que sur
Démonstration du suivi des sélections
Voici une petite démo. Elle suit la sélection courante sur le document et montre ses limites :
<p id="p">Select me: <i>italic</i> and <b>bold</b></p>
From <input id="from" disabled> â To <input id="to" disabled>
<script>
document.onselectionchange = function() {
let selection = document.getSelection();
let {anchorNode, anchorOffset, focusNode, focusOffset} = selection;
// anchorNode et focusNode sont des nĆuds de texte habituellement
from.value = `${anchorNode?.data}, offset ${anchorOffset}`;
to.value = `${focusNode?.data}, offset ${focusOffset}`;
};
</script>
DĂ©monstration de la copie dâune sĂ©lection
Il existe deux approches pour copier le contenu sélectionné :
- Nous pouvons utiliser
document.getSelection().toString()pour le récupérer sous forme de texte. - Sinon, pour copier le DOM complet, par exemple si nous devons garder le formatage, nous pouvons obtenir les plages sous-jacentes avec
getRangesAt(...). Un objetRange, à son tour, a la méthodecloneContents()qui clone son contenu et retourne un objetDocumentFragment, que nous pouvons insérer ailleurs.
Voici la démonstration de la copie du contenu sélectionné à la fois comme texte et comme noeuds DOM :
<p id="p">Select me: <i>italic</i> and <b>bold</b></p>
Cloned: <span id="cloned"></span>
<br>
As text: <span id="astext"></span>
<script>
document.onselectionchange = function() {
let selection = document.getSelection();
cloned.innerHTML = astext.innerHTML = "";
// Cloner les noeuds du DOM Ă partir de plages (nous supportons le multiselect ici)
for (let i = 0; i < selection.rangeCount; i++) {
cloned.append(selection.getRangeAt(i).cloneContents());
}
// Obtenir sous forme de texte
astext.innerHTML += selection;
};
</script>
Méthodes de sélection
Nous pouvons travailler avec la sélection en ajoutant/supprimant (add/remove) des plages :
getRangeAt(i)â obtient la i-iĂšme plage, en commençant par0. Dans tous les navigateurs sauf Firefox, seul0est utilisĂ©.addRange(range)â ajouterangeĂ la sĂ©lection. Tous les navigateurs, Ă lâexception de Firefox, ignorent lâappel, si la sĂ©lection a dĂ©jĂ une plage associĂ©e.removeRange(range)â supprimerangede la sĂ©lection.removeAllRanges()â supprime toutes les plages.empty()â alias deremoveAllRanges.
Il y a aussi des méthodes pratiques pour manipuler directement la plage de sélection, sans appels intermédiaires à Range :
collapse(node, offset)â remplace la plage sĂ©lectionnĂ©e par une nouvelle qui commence et se termine aunodedonnĂ©, Ă la positionoffset.setPosition(node, offset)â alias decollapse.collapseToStart()â rĂ©duit (remplace par une plage vide) au dĂ©but de la sĂ©lection.collapseToEnd()â rĂ©duit Ă la fin de la sĂ©lection.extend(node, offset)â dĂ©place le focus de la sĂ©lection vers lenodeet la positionoffsetdonnĂ©s.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset)â remplace la plage de sĂ©lection avec les valeurs de dĂ©butanchorNode/anchorOffsetet de finfocusNode/focusOffset. Tout le contenu situĂ© entre les deux est sĂ©lectionnĂ©.selectAllChildren(node)â sĂ©lectionne tous les enfants dunode.deleteFromDocument()â supprime le contenu sĂ©lectionnĂ© du document.containsNode(node, allowPartialContainment = false)â vĂ©rifie si la sĂ©lection contientnode(partiellement si le second argument esttrue).
Pour la plupart des tĂąches, ces mĂ©thodes sont suffisantes, il nây a pas besoin dâaccĂ©der Ă lâobjet Range sous-jacent.
Par exemple, sélectionner le contenu entier du paragraphe <p> :
<p id="p">Select me: <i>italic</i> and <b>bold</b></p>
<script>
// sélectionnez du 0-Úme enfant de <p> au dernier element fils
document.getSelection().setBaseAndExtent(p, 0, p, p.childNodes.length);
</script>
La mĂȘme chose en utilisant Range :
<p id="p">Select me: <i>italic</i> and <b>bold</b></p>
<script>
let range = new Range();
range.selectNodeContents(p); // ou selectNode(p) pour sélectionner également la balise <p>
document.getSelection().removeAllRanges(); // effacer la sélection existante s'il y en a une
document.getSelection().addRange(range);
</script>
Si une sĂ©lection du document existe dĂ©jĂ , videz-la dâabord avec removeAllRanges(). Puis ajoutez des plages. Sinon, tous les navigateurs, sauf Firefox, ignorent les nouvelles plages.
Lâexception est certaines mĂ©thodes de sĂ©lection, qui remplacent la sĂ©lection existante, comme setBaseAndExtent.
Sélection dans les contrÎles de formulaires
Les Ă©lĂ©ments de formulaire, tels que input et textarea fournissent une API spĂ©ciale pour la sĂ©lection, sans objets Selection ou Range. Comme une valeur dâentrĂ©e est un texte pur, pas du HTML, il nây a pas besoin de tels objets, tout est beaucoup plus simple.
Propriétés :
input.selectionStartâ position du dĂ©but de la sĂ©lection (accessible en Ă©criture).input.selectionEndâ position de la fin de la sĂ©lection (accessible en Ă©criture).input.selectionDirectionâ direction de la sĂ©lection, une parmi : âforwardâ, âbackwardâ ou ânoneâ (si, par exemple, la sĂ©lection est effectuĂ©e par un double clic de souris).
les événements :
input.onselectâ se dĂ©clenche lorsque quelque chose est sĂ©lectionnĂ©.
Méthodes :
-
input.select()â sĂ©lectionne tout dans le contrĂŽle de texte (peut ĂȘtretextareaau lieu deinput). -
input.setSelectionRange(start, end, [direction])â modifie la sĂ©lection pour quâelle sâĂ©tende de la positionstartĂend, dans la direction donnĂ©e (facultatif). -
input.setRangeText(replacement, [start], [end], [selectionMode])â remplace une plage de texte par le nouveau texte.Les arguments facultatifs
startetend, sâils sont fournis, dĂ©finissent le dĂ©but et la fin de la plage, sinon la sĂ©lection de lâutilisateur est utilisĂ©e.Le dernier argument,
selectionMode, dĂ©termine comment la sĂ©lection sera dĂ©finie aprĂšs que le texte ait Ă©tĂ© remplacĂ©. Les valeurs possibles sont :"select"â le texte nouvellement insĂ©rĂ© sera sĂ©lectionnĂ©."start"â la plage de sĂ©lection se rĂ©duit juste avant le texte insĂ©rĂ© (le curseur sera immĂ©diatement devant)."end"â la plage de sĂ©lection se rĂ©duit juste aprĂšs le texte insĂ©rĂ© (le curseur sera juste aprĂšs)."preserve"â tente de prĂ©server la sĂ©lection. Câest la valeur par dĂ©faut.
Voyons maintenant ces méthodes en action.
Exemple : suivi de sélection
Par exemple, ce code utilise lâĂ©vĂ©nement onselect pour suivre la sĂ©lection :
<textarea id="area" style="width:80%;height:60px">
Selecting in this text updates values below.
</textarea>
<br>
From <input id="from" disabled> â To <input id="to" disabled>
<script>
area.onselect = function() {
from.value = area.selectionStart;
to.value = area.selectionEnd;
};
</script>
Veuillez noter :
onselectse dĂ©clenche lorsque quelque chose est sĂ©lectionnĂ©, mais pas lorsque la sĂ©lection est supprimĂ©e.- LâĂ©vĂ©nement
document.onselectionchangene devrait pas se dĂ©clencher pour les sĂ©lections Ă lâintĂ©rieur dâun contrĂŽle de formulaire, selon la spec, car il nâest pas liĂ© Ă la sĂ©lection et aux plages dudocument. Certains navigateurs le gĂ©nĂšrent, mais il ne faut pas sây fier.
Exemple : déplacement du curseur
Nous pouvons changer selectionStart et selectionEnd, cela définit la sélection.
Un cas particulier important est celui oĂč selectionStart et selectionEnd sont Ă©gaux. Dans ce cas, il sâagit exactement de la position du curseur. Ou, pour le dire autrement, lorsque rien nâest sĂ©lectionnĂ©, la sĂ©lection est rĂ©duite Ă la position du curseur.
Donc, en mettant selectionStart et selectionEnd Ă la mĂȘme valeur, on dĂ©place le curseur.
Par exemple :
<textarea id="area" style="width:80%;height:60px">
Focus on me, the cursor will be at position 10.
</textarea>
<script>
area.onfocus = () => {
// setTimeout de délai zéro à exécuter aprÚs la fin de l'action "focus" du navigateur
setTimeout(() => {
// nous pouvons définir n'importe quelle sélection
// si start=end, le curseur se trouve exactement Ă cet endroit
area.selectionStart = area.selectionEnd = 10;
});
};
</script>
Exemple : modifier la sélection
Pour modifier le contenu de la sélection, on peut utiliser la méthode input.setRangeText(). Bien sûr, nous pouvons lire selectionStart/End et, avec la connaissance de la sélection, modifier la chaßne partielle (substring) correspondante de value, mais setRangeText est plus puissant et souvent plus pratique.
Câest une mĂ©thode plus ou moins complexe. Dans sa forme la plus simple Ă un argument, elle remplace la plage sĂ©lectionnĂ©e par lâutilisateur et supprime la sĂ©lection.
Par exemple, ici la sĂ©lection de lâutilisateur sera entourĂ©e de *...* :
<input id="input" style="width:200px" value="Select here and click the button">
<button id="button">Wrap selection in stars *...*</button>
<script>
button.onclick = () => {
if (input.selectionStart == input.selectionEnd) {
return; // rien n'est sélectionné
}
let selected = input.value.slice(input.selectionStart, input.selectionEnd);
input.setRangeText(`*${selected}*`);
};
</script>
Avec plus dâarguments, nous pouvons dĂ©finir start et end de range.
Dans cet exemple, nous trouvons "THIS" dans le texte de saisie, le remplaçons et gardons le remplacement sélectionné :
<input id="input" style="width:200px" value="Replace THIS in text">
<button id="button">Replace THIS</button>
<script>
button.onclick = () => {
let pos = input.value.indexOf("THIS");
if (pos >= 0) {
input.setRangeText("*THIS*", pos, pos + 4, "select");
input.focus(); // focus pour rendre la sélection visible
}
};
</script>
Exemple : insérer au curseur
Si rien nâest sĂ©lectionnĂ©, ou si nous utilisons des start et end Ă©gaux dans setRangeText, alors le nouveau texte est juste insĂ©rĂ©, rien nâest supprimĂ©.
On peut aussi insĂ©rer quelque chose âau curseurâ en utilisant setRangeText.
Voici un bouton qui insĂšre "HELLO" Ă la position du curseur et place le curseur immĂ©diatement aprĂšs. Si la sĂ©lection nâest pas vide, elle est remplacĂ©e (nous pouvons le dĂ©tecter en comparant selectionStart!=selectionEnd et faire autre chose Ă la place) :
<input id="input" style="width:200px" value="Text Text Text Text Text">
<button id="button">Insert "HELLO" at cursor</button>
<script>
button.onclick = () => {
input.setRangeText("HELLO", input.selectionStart, input.selectionEnd, "end");
input.focus();
};
</script>
Rendre un élément non sélectionnable
Pour rendre quelque chose non sélectionnable, il y a trois approches :
-
Utiliser la propriété CSS
user-select: none.<style> #elem { user-select: none; } </style> <div>Selectable <div id="elem">Unselectable</div> Selectable</div>Cela ne permet pas Ă la sĂ©lection de commencer Ă
elem. Mais lâutilisateur peut commencer la sĂ©lection ailleurs et inclureelemdans celle-ci.Dans ce cas,
elemdeviendra une partie dedocument.getSelection(), et la sélection aura bien lieu, mais son contenu sera généralement ignoré dans le copier-coller. -
EmpĂȘcher lâaction par dĂ©faut dans les Ă©vĂ©nements
onselectstartoumousedown.<div>Selectable <div id="elem">Unselectable</div> Selectable</div> <script> elem.onselectstart = () => false; </script>Cela empĂȘche de commencer la sĂ©lection sur
elem, mais le visiteur peut la commencer sur un autre Ă©lĂ©ment, puis lâĂ©tendre Ăelem.Câest pratique quand il y a un autre gestionnaire dâĂ©vĂ©nement sur la mĂȘme action qui dĂ©clenche la sĂ©lection (par exemple
mousedown). Nous dĂ©sactivons donc la sĂ©lection pour Ă©viter tout conflit, tout en permettant au contenu deelemdâĂȘtre copiĂ©. -
On peut aussi effacer la sélection aprÚs le fait avec
document.getSelection().empty(). Câest rarement utilisĂ©, car cela provoque un clignotement indĂ©sirable lorsque la sĂ©lection apparaĂźt-disparaĂźt.
Quelques références
Résumé
Nous avons couvert deux API différentes pour les sélections :
- Pour le document : objets
SelectionetRange. - Pour
input,textarea: méthodes et propriétés supplémentaires.
La deuxiĂšme API est trĂšs simple, puisquâelle fonctionne avec du texte.
Les recettes les plus utilisées sont probablement :
- Obtenir la sélection :
let selection = document.getSelection(); let cloned = /* Ă©lĂ©ment pour cloner les nĆuds sĂ©lectionnĂ©s vers */; // puis appliquer les mĂ©thodes Range Ă selection.getRangeAt(0) // ou, comme ici, Ă toutes les plages (range) pour supporter la sĂ©lection multiple. for (let i = 0; i < selection.rangeCount; i++) { cloned.append(selection.getRangeAt(i).cloneContents()); } - Mise en place de la sĂ©lection :
let selection = document.getSelection(); // directement: selection.setBaseAndExtent(...from...to...); // ou nous pouvons créer une plage et : selection.removeAllRanges(); selection.addRange(range);
Et enfin, Ă propos du curseur. La position du curseur dans les Ă©lĂ©ments modifiables, comme <textarea> est toujours au dĂ©but ou Ă la fin de la sĂ©lection. Nous pouvons lâutiliser pour obtenir la position du curseur ou pour le dĂ©placer en dĂ©finissant elem.selectionStart et elem.selectionEnd.
Commentaires
<code>, pour plusieurs lignes â enveloppez-les avec la balise<pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepenâŠ)