11521 sujets

JavaScript, DOM et API Web HTML5

Bonjour

Suite à diverses discussions sur ce forum, je suis en train de mettre en œuvre l'utilisation de this.method.bind(this) dans mes gestionnaires d'évènements, en particulier pour des formulaires.

J'ai donc écrit le code suivant:

function myObject(...) {
    ........
    this.form = ........;
     ........
    this.changeItem = function() {
         ........
    }
    var inputs = this.form.getElementsByTagName('input');
    for(var i = 0; i < inputs.length; i++) {
        var curInput = inputs[ i ];
        switch(curInput.type) {
            case 'text':
                if(...) curInput.addEventLisnter('change', this.changeItem.bind(this);
                ........
           case 'button':
                ........
    }
}


Cela fonctionne très bien, mais je me pose une question:
Pour pratiquement toutes les balises <input type="text"> je crée en fait une fonction de traitement qui est un "double" de la même fonction. Il y a donc, me semble-t-il autant de duplicaton de cette fonction avec son environnement qu'il y a de balises concernées en gros cela doit correspondre à une multiplication des copies de mon objet.
Je n'ai qu'une vingtaine de ces balises dans ma page, donc ce n'est pas un gros problème, mais je me demande s'il n'y a pas une façon plus "économe" de traiter les choses, par exemple est-ce qu'il ne serait pas préférable d'écrire:

   var inputs = this.form.getElementsByTagName('input');
   var changeFunction = this.changeItem.bind(this);
   for(var i = 0; i < inputs.length; i++) {
        var curInput = inputs[ i ];
        switch(curInput.type) {
            case 'text':
                if(...) curInput.addEventLisnter('change', changeFunction);
                ........
           case 'button':
                ........
    }


Votre avis m'intéresse au plus haut point!
Modifié par PapyJP (31 Dec 2016 - 12:46)
Bonsoir.

Il y a quelque temps, je m'étais fait un petit programme de création de dégradés CSS, un truc assez rudimentaire...

Dans les dégradés, on ne sait pas par avance combien de couleurs il va y avoir, au départ, il n'y a que deux inputs (parce qu'il y a forcément deux couleurs).

En appuyant sur un bouton, un autre input apparait... et un autre bouton pour faire éventuellement apparaitre une quatrième et ainsi de suite...

Il faut forcément un écouteur pour mettre à jour le dégradé - la visualisation et le code. La methode addEventListener est appliquée sur la div qui va contenir tous les inputs. Chaque fois qu'un input est modifié, l'événement remonte le DOM jusqu'au div, là il est 'attrapé' par l'écouteur, la fonction passée à addEventListener vérifie que l'événement change concerne un des inputs (event.target est l'élément qui a déclenché l'événement) , si c'est le cas , il y a mise à jour...

Je pense que cela peut aussi s'appliquer dans votre cas : si j'ai bien compris, votre this, c'est event.target...

Smiley smile
Zelena a écrit :
si j'ai bien compris, votre this, c'est event.target...

Non, pas du tout.

En fait "this" se réfère à un objet qui contient une trentaine d'attributs et le but du jeu c'est d'afficher ces attributs dans un formulaire pour qu'ils soient modifiables par l'utilisateur.
Je crée donc un <input type="text"> pour chaque attribut, et je dois réagir aux évènements "change" sur ces <input> pour refléter le changement dans l'objet après avoir validé le changement.

Précédemment, c'était fait par <input type="text" ... onchange="checkItem(this)">
dans ce contexte, l'argument de la fonction checkItem est la balise sur laquelle le changement a eu lieu
La fonction checkItem est définie "à l'extérieur" de l'objet, mais comme les changements sont effectués sur les attributs de l'objet, la fonction doit "savoir" de quel objet il s'agit, ce qui n'est pas génial comme design.

J'ai remplacé cela par des <input type="text"> sans "onchange" et c'est dans la création de l'objet lui-même que j'effectue les opérations suivantes:
1) initialiser input.value avec la bonne valeur comme précédemment
2) affecter un gestionnaire d'évènement à chaque <input> pour traiter les changements.

Dans un premier temps, j'ai écrit
element.addEventListener('change', this.checkItem.bind(this))

dans ce contexte, l'argument de la fonction this.checkItem est un objet event et pour retrouver la balise <input> correspondante, il suffit d'écrire var element = event.target;

Si j'ai bien compris comment ça fonctionne, cela veut dire que pour chacune des entrées du formulaire on effectue la méthode "bind(this)", donc on crée une copie de la fonction avec son environnement.
En d'autres termes, autant de fonctions que d'entrées dans le formulaire.

Dans l'autre design, je commence par créer une seule variable locale:
var localCheckItem = this.checkItem.bind(this);

et l'affectation du gestionnaire d'évènements se fait sous la forme
element.addEventListener('change', localCheckItem)

Si je comprends bien, il n'y a plus qu'une seule fonction pour le formulaire, et c'est la même qui est appelée pour chaque champ.

Les deux façons de faire produisent en pratique le même résultat.
La question que je me pose et pour laquelle je voudrais votre avis est
"est-ce du pinaillage sans grand importance, ou bien est-ce que cela a un impact sensible sur le fonctionnement (occupation mémoire, durée d'exécution, ...)?"
Bien entendu, ça ne doit pas changer grand chose pour une trentaine d'entrées, mais s'il y en avait 300 ou 3000?
En réalité bind fait à peu près ça (code ES6) :

Function.prototype.bind = function(thisObj) {
return (...args)=>this.apply(thisObj, ...args);
}


Donc, ton raisonnement est tout à fait correct: une nouvelle fonction est crée à chaque appel de bind. Par contre le contexte de cette nouvelle fonction se limite au thisObj passé.


Cela dit, pour 30 ou même 300 functions en plus ou en moins, je ne pense pas que ça fera une grosse différence, sauf peut-être sur des très petites configurations. A partir de 3000 je n'en serais pas si sûr. C'est à tester pour vraiment voir où se situent les limites.
@QuentinC
Merci de ta réponse.
Comme tu l'as compris, c'est plutôt la curiosité de savoir comment ça marche qui m'a fait me poser cette question.

Bonne année à tous!