mardi 20 février 2007, 14:36
Position du curseur dans un textarea
J'ai essayé de faire 5 actions, que je vais décrire dans cet article :
- obtenir le texte sélectionné dans un textarea donné ;
- obtenir la position du début de la selection du contenu du textarea (en commençant à 0) ;
- obtenir la position de la fin de la selection du contenu ;
- déplacer le curseur à une position, en précisant éventuellement une position de fin pour créer une selection ;
- remplacer le texte sélectionné par un autre.
D'abord, examinons le comportement standard (testé sous Mozilla Firefox >= 1.5).
- Chaque textarea sauvegarde sa propre selection.
- Chaque textarea contient 2 variables
selectionStart
etselectionEnd
qui contiennent à tout moment les coordonnées de la selection (si rien n'est selectionné,selectionStart
est égal àselectionEnd
) - Chaque textarea possède une méthode
setSelectionRange(start, end)
qui permet de créer une selection ou de positionner le curseur (en passant 2 fois la même valeur). - Pour remplacer le texte selectionné, il faut couper le texte en 3 : avant, selection, après, et le remplacer par avant + nouvelle selection + après. Il faut penser à remettre d'indice de scroll afin de conserver la barre de défilement à la même position.
Voyons maintenant le comportement d'Internet Explorer, qui, comme a son habitude, est totalement illogique, incohérent et, disons-le, complètement con.
- Internet Explorer ne gère qu'une seule sélection pour toute le document, accessible via
document.selection
. En conséquence de quoi, il faut toujours penser à faire unfocus()
sur le textarea à traiter, sans quoi on risque d'avoir des résultats totalement incohérents. - Cependant, il est possible de créer une selection pour chaque textarea. Mais ces selections ne contiendront pas le texte selectionné, elles servent uniquement à déplacer le curseur (!) (j'avais prévenu que c'était complètement con...).
- Pour manipuler une selection, il faut créer un objet
TextRange
, qui permet de faire des tas de choses (y compris des choses documentées comme « opaque » par Microsoft), mais pas de récupèrer la position du curseur. - Il est impossible se spécifier directement la position du curseur.
- Il y a une méthode propriétaire pour remplacer la sélection, en mettant à jour l'attribut
text
de l'objetrange
(range.text = 'toto'
).
Voyons maintenant comment dominer IE.
Note : Les fonctions proposées ici sont destinées à être utilisée dans une classe où this
représente l'objet textarea. N'hésitez à pas adapter pour votre propre besoin :)
Obtenir la sélection
La fonction suivante permet d'obtenir le texte sélectionné dans un textarea.
getSelection: function () { if (this.setSelectionRange) return this.value.substring(this.selectionStart, this.selectionEnd); else if (document.selection) { this.focus(); return document.selection.createRange().text; } }
Obtenir les coordonnées du curseur
Alors là, ça devient rigolo. La solution : récupérer la selection globale, puis la recopier dans une selection locale au textarea. Ensuite, déplacer la fin de la selection le plus possible (la selection locale, contraiement à la selection globale du document, s'arrettera à la fin de texte). Enfin, effectuer une petite soustraction.
getSelectionStart: function() { if ( typeof this.selectionStart != 'undefined' ) return this.selectionStart; // IE Support this.focus(); var range = this.createTextRange(); range.moveToBookmark(document.selection.createRange().getBookmark()); range.moveEnd('character', this.value.length); return this.value.length - range.text.length; }
Même principe pour la fin.
getSelectionEnd: function() { if ( typeof this.selectionEnd != 'undefined' ) return this.selectionEnd; // IE Support this.focus(); var range = this.createTextRange(); range.moveToBookmark(document.selection.createRange().getBookmark()); range.moveStart('character', - this.value.length); return range.text.length; }
Positionner le curseur
Cette fois ci, la technique est de créer une selection vide, de la positionner au bon endroit, de d'utiliser la méthode select()
pour la faire apparaitre.
setCaretPos: function(start, end) { end = end || start; this.focus(); if (this.setSelectionRange) this.setSelectionRange(start, end); else if (document.selection) { var range = this.createTextRange(); range.moveStart('character', start); range.moveEnd('character', - this.value.length + end); range.select(); } }
Remplacer la sélection
Avec toutes ces méthodes cross-browser, la fonction de remplacement est un jeu d'enfant !
J'ai ajouté un paramètre keep
indiquant s'il faut garder ou non le texte selectionné après remplacement.
replaceSelection: function (str, keep) { this.focus(); var start = this.getSelectionStart(); var stop = this.getSelectionEnd(); var end = start + str.length; var scrollPos = this.scrollTop; this.value = this.value.substring(0, start) + str + this.value.substring(stop); if ( keep ) this.setCaretPos(start, end); else this.setCaretPos(end); this.scrollTop = scrollPos; }
En esperant que tout ceci vous servira :)
Par cgo2, dans Développement web
Commentaires
Exactement ce dont j'avais besoin pour mon éditeur! Merci!
Je tombe sur ce site en recherche de solutions à quelques problèmes. Juste quelques remarques :
Un truc un peu agaçant de tomber trop souvent sur des "anti IE" ou "anti FF". C'est plus souvent un phénomène de mode qu'une réelle interprétation générale.
Effectivement, IE contient des incohérences. FF n'en est pas exempte pour autant. On peut multiplier les exemples à la pelle pour critiquer l'un ou l'autre...
Le plus important n'est pas là. Le plus important, c'est que ces différences de comportement sont en fait une véritable mine de formation pour la plupart des programmeurs qui se donnent la peine de s'y plonger. La preuve, les solutions trouvées pour contourner les manières "totalement illogique, incohérent et, disons-le, complètement con". Pour qui se donne la peine de comprendre ce qui est écrit, ça sera mille fois plus instructifs que de betement copier coller des solutions toutes faites sans chercher à comprendre les mécanismes internes de fonctionnement de nos navigateurs.
Et pour peu que l'on se plonge dans cette compréhension, sans partie pris, on se rendra alors compte que derrière ces "manques" de la part de IE se cachent toute une série de fonction qui peuvent aussi avoir leur utilité qu'il faudrait alors aussi reprogrammer sous FF qui en est dépourvu.
Tout dépend de sa manière de programmer et de ses besoins de programmations. IE correspondra plus à certaines habitudes de programmations. FF pour d'autres habitudes.
De mon point de vue, je peste quasiment autant contre IE que contre FF à différents niveaux. A ce jour, en toute bonne foi, je ne peux pas prétendre à dire que FF ou IE soit "plus totalement illogique, incohérent et, disons-le, complètement con" par rapport à son concurrent.
La critique est facile, mais en attendant, ces pb apportent des solutions de contournement qui font appel à de l'astuce, de l'intelligence, ou des ruses. Et franchement, pour tout bon programmeur qui se respecte, qui n'aime pas relever ce genre de défi ?
Sinon, en dehors de cette remarque, excellent article qui apporte une solution à un pb qui n'en est plus un puisque solutionné ;)
Bien à vous
en réponse à magostranger :
ça ne serait pas plus simple si tous les navigateurs suivaient les standards et avaient à peu près les mèmes comportements ?
Car je crois pas que "les différences de comportement sont en fait une véritable mine de formation pour la plupart des programmeurs" mais plutôt un vrai casse-tête qui fait parfois perdre beaucoup de temps inutilement.
Jérome, si tu me cites, cites moi correctement :)
Tu me cites sur """
Car je crois pas que "les différences de comportement sont en fait une véritable mine de formation pour la plupart des programmeurs" mais plutôt un vrai casse-tête qui fait parfois perdre beaucoup de temps inutilement.
"""
en coupant ma citation, tu tronque un petit détail : " ... pour la plupart des programmeurs qui se donnent la peine de s'y plonger"
Ce n'est pas fatalement perte de temps inutile (sinon, on se lance dans une chaine de "à quoi ça sert de faire des choses" et on conclu à la fin "a quoi ça sert de vivre ?" ;) )
Faut pas pousser, la plupart des navigateurs suivent les standarts de base et ont à peu près le même comportement. Les différences et les problèmes apparaissent sitot que l'on commence à faire de la programmation un peu plus pointue, non forcément référencée.
Si tous les navigateurs avaient le même comportement à tous les niveaux, alors il n'y aurait plus d'intérêt à avoir plusieurs navigateurs et un seul prendrait un monopole. Qui voudrait voir une firme prendre un tel monopole franchement ?
Ce sont les petites différences, les "innovations" qui permettent de faire évoluer les standarts et les navigateurs.
Pour le positionnement du curseur dans un textarea en javascript, bah on est en présence de deux comportements relativement complémentaires et apparement contradictoire (mais qui sont plutot sur deux approches différentes de voir les choses). La manière de FF me semble bien plus facile d'accès que la manière IE.
Autre exemple : un comportement de base :
pour ceux qui ont testé la dernière version de safari sur pc, ils auront remarqué que les champs textarea sont redimensionnables !!! Ce n'est pourtant pas référencé dans les standarts il me semble, mais c'est tout simplement excellent et pertinent à mon sens. J'espère bien que tous les navigateurs s'y colleront et que ça apparaitra en standart.
On pourrait multiplier les exemples.
Ce qui est intéressant là dedans, c'est de voir comment les bons programmeurs solutionnent ces problèmes pour contribuer à donner des comportements plus similaires entre les navigateurs. Et comme ça, on aura toujours besoin d'eux ? :)
Si tous les navigateurs étaient standarts, ça serait plus triste non ? ;)
Un édtieur web ne pourra jamais tenir compte des évolutions des différents navigateurs, un programmeur s'adaptera plus facilement.
Bien à toi
J'admire la dévotion à l'apprentissage, mais selon moi, il s'agit d'apprentissage inutile. Cet apprentissage aurait pu facilement etre éviter par une standardisation du language. Cet apprentissage ne te permet pas d'approfondir la compréhension, mais oublige les programmeurs a gèrer 2 manières de faire 1 chose, alors que le but d'abstraction de l'informatique dicte plus de faire 2 chose d'une seule manière. Je programme depuis plusieurs années et ce genre de bug de "IE marche comme ci" et "FF marche comme ca" est la pire perte de temps, le pire casse tête, qui ne se solutionne pas par une compréhension hors du commun de l'informatique ou de la programmation, mais par des heures de recherches a savoir se que chaque concepteur de navigateur a bien voulu donner comme fonction a son language. Je me suis ramasser sur se site parce que je voulais ajouter du texte à la position de mon curseur sur le clique d'un lien. Ajouter du texte à la position d'un curseur ... sa semble compliquer pour vous ? Combien de temps sa prendrait pour coder sa en C/C++/C# ? en VB ? Et bien en "Web" j'ai trouver un espece de paquets de site qui explquait pour IE mais pas pour FF, ou vice versa ... Et attention ... selon la logique que c'est "préférable" de ne pas avoir des languages standardisé, il faut que tu considere que chacun des codes que tu as fait, qui n'ont jamais bugger, sont à la merci d'un nouveau Browser qui lui décide de supporter d'autre function ... L'informatique, l'electronique, le transport, a toujours évolué selon la standardisation des outils.
Il n'y a rien de mal a developper plusieurs browser différent, pour leur sécurité, leur priorité d'affichage des outils, etc... etc..., mais que chaque browser developpe sa propre version de javascript selon ces gout n'est rien d'autre que la résultat de l'esprit de compétition, plutot que de coopération, de l'être humain. My 2 cents
il me semble que la fonction getSelectionStart renvoie un chiffre erronné...
j ai essayé ces fonctions sur un textarea contenant plusieurs retours à ligne
Je poste ici une correction que je pense correcte (ben tiens...) en ce qui concerne la fonction getSelectionEnd. Elle m'a renvoyé plusieurs valeurs erronées, et d'après les 2mns de test que je vient de faire, ça marche avec ce code (quand à getSelectionStart, je n'ai pas encore essayé).
getSelectionEnd : function() {
if(typeof this.selectionEnd != 'undefined') {
return this.selectionEnd;
}
this.focus();
var range = this.createTextRange();
var size = range.text.length;
range.moveToBookmark(document.selection.createRange().getBookmark());
range.moveEnd('character',this.value);
var retour = this.value.length+range.text.length-size;
return retour;
}
Désolé, je viens de m'apercevoir que je m'était trompé dans les formules.
V'là une correction:
getSelectionEnd: function() {
if(typeof this.selectionStart != 'undefined') {
return this.selectionStart;
}
this.focus();
var range = this.createTextRange();
range.moveToBookmark(document.selection.createRange().getBookmark());
range.moveStart('character',-this.value.length);
return range.text.length;
}
Mon seul souci c'est que tu raisonnes en terme de classe et que je ne comprend malheureusement pas grand chose à cette logique.
Cela dit chaque fonction est très clairement présentée et donc j'espère m'en tirer avec mon approche trop patachone.
Cette ressource est tout à fait précieuse.
Donc merci :)
slt je trouve que ton code est très utile, je me demandais si ca marche aussi sur les autre parties du document html
Bonjour,
Je débute en programmation orientée objet avec javascript et je n'ai pas bien saisie la solution que vous proposez , est ce que le code est complet ?
si je peux avoir quelques éclaircissement merci bcp.
Parfait!! Exactement ce que je cherchai :)
slider > Il y a tout ce qu'il faut là, mais évidemment il faut pas espérer que ça fonctionne avec un copier-coller... Si t'essayes de comprendre tu saura arranger le code à tes besoins!
J'ai justement un problème de selection, le code donné dans cet exemple ne fonctionne pas très bien car il y a un problème avec les caractère \n et \r. Ce qui fait que je récupère bien la position start et end mais lorsque je souhaite positionner le curseur j'ai des décalage d'index et cela uniquement si dans le texte j'ai des retour chariot et new line...
Et je ne trouve pas de solution adequat. A part des bidouilles foireuse.
Si quelqu'un à le même problème voir une réponse ça serait le pied...