Transvasement de select multiples en javascript
By Geoffrey on Monday 25 July 2005, 16:44 - Coding - Permalink
Derrière ce nom barbare se cache un concept qui vous est surement déjà connu. Le principe est simple: on à deux objets select-multiple (c'est à dire des select mais dans lesquels on peut sélectionner plusieurs éléments), et on veut faire passer des éléments de l'un à l'autre (et vice et versa en général). Un peu de javascript devrait nous aider à résoudre cette problématique très facilement (je vous avais prévenu pour le javascript :-)
Il y a plusieurs méthodes d'accomplir ce que nous désirons, commençons par la plus simple, avec un morceau de code:
function moveSelected(from, to) { var from = document.getElementById(from); var to = document.getElementById(to); if (from.type != 'select-mutiple' || to.type != 'select-multiple') { return false; } to.options[to.options.length] = new Option(from.options[from.options.selectedIndex].text); from.options[from.options.selectedIndex] = null; return true; }
Bon alors d'abord, on met les objets select dans des variables, parceque DOM c'est sympa, mais l'API est vraiment indigeste (pour rester poli). Ensuite on a la présence d'esprit de vérifier que les deux objets sont bien du type voulus, parceque bon, faut pas nous prendre pour une tulipe non plus.
C'est après que cela devient interressant. L'objet HTMLSelectElement (c'est le nom que fournit un alert(from)) intègre un objet Option, accessible via from.options (oui tous les exemples utiliseront la variable from, ce sera plus simple). Cet objet dispose de quelques méthodes (dont je vous passerais le détail parceque 1) c'est pas le but de cet article et 2) j'y connais pas grand chose finalement), mais ce qui nous interresse vraiment sont ses attributs, et en particulier l'attribut selectedIndex, qui retourne l'index de l'élément sélectionné dans l'objet select. Il ne nous reste plus qu'a créer un nouvel objet Options dans le select destinataire, à supprimer l'entrée correspondante dans le from, et le tour est joué.
Cette méthode fonctionne, mais ne permet le déplacement que d'un seul élement à la fois, ce qui peut s'avérer lourd à l'utilisation dans certains cas. Pour remédier à cela, nous allons maintenant voir une version qui permet de déplacer plusieurs éléments d'un seul coup:
function moveSelected(from, to) { from = document.getElementById(from); to = document.getElementById(to); if (from.type != 'select-mutiple' || to.type != 'select-multiple') { return false; } len = from.options.length - 1; for (i = len; i >= 0; i--) { if (from.options[i].selected) { to.options[to.options.length] = new Option(from.options[i].text); from.options[i] = null; } } return true; }
Pour se faire, on n'utilise plus l'attribut selectedIndex, mais on boucle sur tous les éléments pour voir lesquels sont sélectionnés, et on les déplace en conséquence. Le seul piège à éviter, c'est le sens de la boucle. Si on commence à zéro, un problème se pose dès lors qu'on déplace un élément. En effet, dès qu'un élément est supprimé d'une liste, les autres éléments sont retriés. Exemple, si on a une liste de deux éléments, foo et bar, et que foo est sélectionné; la première itération, d'index 0, concerne foo, qui sera déplacé, entrainant automatiquement l'accès de bar à l'index 0. Le problème qui se pose étant que notre boucle, elle, continuera à l'index 1, qui n'existe plus.
C'est clair ?
Donc pour éviter ça, on boucle à partir de la fin, c'est aussi simple que ça :-) Le reste du code est fondamentalement le même, donc pas de problème de ce côté là.
Comments
Petite info au passage, le composant "hierselect" de PEAR Quickform fait ça tout seul comme un grand, il s'occupe de générer le javascript et de remplir les combos ... (certes le JS n'est pas aussi propre!)
en tout cas sympa d'avoir la petite fonction qui va bien en DOM JS, toujours utile ce genre de trucs, merci bien !
http://pear.php.net/manual/en/package.html.html-quickform.html-quickform-hierselect.php
Bon à savoir, même si j'évite au maximum d'utiliser PEAR. Comme disait quelqu'un sur son blog (JMF sur dying culture je crois), j'évite au maximum d'utiliser des librairies que je ne maîtrise pas a fond (la seule que j'utilise étant IXR_Library.php, l'implémentation XML-RPC d'incutio), et je n'ai malheureusement pas le temps en ce moment de me pencher sur PEAR, qui me semble s'améliorer de jours en jours.
Cela dit, si j'ai développé cette fonction tout seul, c'est aussi et avant tout pour approfondir ma connaissance de javascript :-)
ben moi je suis pas à fond de PEAR non plus, je prefere coder moi même mes classes en général mais au boulot je bosse avec et j'avoue qui a quelques classes qui déchirent, notamment le package QuickForm pour ne citer que lui ...
Celà dit j'ai déjà eu à coder cette fonction en JS et j'avoue que la tienne est plus optimisé que la mienne ;-)
YEAH trop merci tout plein, c'est tout bete, mais ca m'evite de chercher :) Ah et au fait, je chipotte mais "Il y a plusieurs méthodes d'accomplir ce que nous désirons" n'est pas tres francais... "pour accomplir" ou "afin d'accomplir" serait plus logique a mon gout. Bref, merci pour le tuto js :)
Bon ok il y'a prescription mais je tenais juste a preciser un truc qui risque de poser des soucis... en effet il est bon de recuperer le text de l'option qu'on transvase mais aussi (et surtout) sa valeur.
or si on fait: to.options[to.options.length] = new Option(from.options[i].text); on ne passe que le text et non la valeur... il vaudrait donc mieux faire: to.options[to.options.length] = new Option(from.options[i].text, from.options[i].value); et ainsi passer AUSSI le value.
voila.