Partage
  • Partager sur Facebook
  • Partager sur Twitter

Une enieme question sur setTimeout en récursif.

une affectation de valeur qui ne se fait pas correctement...

Sujet résolu
    7 avril 2009 à 1:01:48

    Bonjour !
    J'essaye de progresser en Javascript, mais je m'évertue a vouloir utiliser setTimeout ou setInterval et j'ai beaucoup de mal...
    Je précise que j'ai effectué des recherches, mais apres 2 bonnes heures de trifouillage, je seche...

    Ce soir, j'essai de faire apparaitre une petite image en jouant sur sa valeur .style.opacity.
    Cette valeur est fournie dans le CSS correspondant au HTML et vaut 0 au chargement de la page.

    Au passage de la souris sur une zone particuliere d'une image-map, cette petite image, située au-dessus (dans l'axe des z) de l'image-map, deviens opaque.

    J'ai donc créé ces fonctions:
    function increment_alpha(id)
    {
    alert(id.style.opacity); 
    if ((id.style.opacity)<1)
    	{alert('Inferieur a 1');
    	id.style.opacity+=0.1;
    	alert(id.style.opacity);
    	setTimeout(function(){increment_alpha(id);},100);}
    else
    		return null;
    }
    
    function bringon(arg)
    {
    /*Plusieurs choses ici mais rien d'important...*/
    var pp=document.getElementById('premier_plan');
    pp.style.opacity= "0";
    increment_alpha(pp);}
    



    Donc... Suivons pas a pas l'execution:

    bringon() est d'abord lancée et appelle la bien-nommée increment_alpha()
    C'est la qu'il y a un pépin. J'ai mis plein de "alert" pour voir ou j'en suis.

    Dans increment_alpha, au début, le 1er alert nous apprend que id.style.opacity vaut 0 (mis a 0 juste avant dans bringon(): le fait qu'il soit a 0 dans le CSS ne suffisait pas, id.style.opacity retournait un NaN).
    Le second alert nous dit qu'on est dans la boucle (inferieur a 1)
    Le 3eme qu'on a bien 0.1 maintenant.

    On relance cette fonction depuis elle-meme.

    1er alert: la on voit que id.style.opacity vaut 0.1, normal
    le 2eme alert nous renseigne qu'on est retourné dans la boucle car toujours inférieur a 1, c'est bon.
    Donc on ajoute 0.1 encore et...
    Patatras: le 3eme alert nous informe que id.style.opacity est toujours a 0.1 !!!!

    Mais pourquoi !? On vient de lui remettre un +0.1. On devrait etre a 0.2.
    Evidemment a ce rythme la on ne s'en sort pas et donc un ctrl-alt-suppr s'impose...


    Quelqu'un sait ce qui se passe ?
    • Partager sur Facebook
    • Partager sur Twitter
    Anonyme
      7 avril 2009 à 1:25:05

      c'est tout bête comme problème.

      quand tu fais truc.opacity += 0.1, hé ben… ça fait de la merde;

      truc.opacity est une chaine de caractère et tu concatène le nombre 0.1 à cette chaine. Javascript fait un peu de magie des fois (pour justement que les bourrins comme toi n'ai pas de problèmes avec leurs script de bourrin, c'est pas méchant juste un petit surnom affectif). Donc vraisemblablement javascript convertit truc.opacity en nombre pour pouvoir faire une addition et non pas une concaténation (le + pour la concaténation c'est un des points qui induit beaucoup en erreur en js). Sauf qu'il convertit pas en décimales par défaut mais en entier et quand tu arrondis 0.1 tu obtiens… 0 auquel tu ajoutes 0.1, moralité tu as toujours 0.1

      Pour t'en sortir il faut explicitement converir truc.opacity en nombre décimal avec parseFloat(truc.opacity, 10);

      function increment_alpha(id)
      {
       var opacity = parseFloat(id.style.opacity, 10);
      alert(id.style.opacity); 
      if ( opacity < 1)
      	{alert('Inferieur a 1');
      	id.style.opacity= opacity + 0.1;
      	alert(id.style.opacity);
      	setTimeout(function(){increment_alpha(id);},100);}
      else
      		return null;
      }
      
      function bringon(arg)
      {
      /*Plusieurs choses ici mais rien d'important...*/
      var pp=document.getElementById('premier_plan');
      pp.style.opacity= 0;
      increment_alpha(pp);}
      
      • Partager sur Facebook
      • Partager sur Twitter
        7 avril 2009 à 1:44:59

        Haaaaaaaaaaaaaaaaa je vois...

        Punaise j'aurais pu chercher longtemps.
        Alors 2 ou 3 commentaires:
        _ Instinctivement j'ai d'abord utilisé des parseInt pour repecher les valeurs de id.style.opacity. Je me suis assez vite rendu compte que ca ramenait les valeurs genre 0.4 a 0 donc je les ai vite laissé tombé. Si j'avais su qu'un cousin (parseFloat) existait j'aurais gagné 2 heures ! :-(
        _ Ce que tu appele la magie de JS (les transformation de type, ce qui rend "20" * "5" possible) ca me trouble énormément ! Je préfererais une erreur.
        Le fait que ca soit toléré ne me force pas a faire bien attention a ce genre de détails... Et comme j'ai finalement peu d'experience en programation (a part un peu de scripts python sous blender et un peu de PHP), je vais développer de mauvaises habitudes...
        _ Bourrin, je te l'accorde. Mais grace a toi déja un peu moins !
        _ Je me trompe ou JS est un peu... comment dire... bordélique ? Je veux dire que pour l'instant, sans bcp d'experience, j'ai l'impression de manquer de reperes. J'ai l'impression que certaines choses sont arbitrairement tordues, d'autres étrangement permissives etc. J'ai du mal a cerner des barrieres quoi... Avec python par exemple, les choses me parraissaient plus... carrées, prévisibles...


        Encore merci nod_ !


        EDIT: peux-tu me dire pourquoi il faut initialiser pp.style.opacity a 0 dans bringon() alors que opacity est déja a 0 par le CSS ?
        • Partager sur Facebook
        • Partager sur Twitter
          7 avril 2009 à 10:21:59

          Je pense qu'il faut l'initialiser, parce que JS ne peut lire le CSS que quand il est directement dans le code HTML (avec l'attribut style).

          Sinon, il est nécessaire de l'initialiser, pour être sûr de ne pas se baser sur une valeur inexistante...
          • Partager sur Facebook
          • Partager sur Twitter
          Anonyme
            7 avril 2009 à 19:29:51

            La magie du JS c'est voulu. Lors de la BWWI (Browser world war 1) dans les années 90 il fallait faire accepter le JS aux developpeurs et netscape a fait en sorte de crée un langage avec lequel il est très très simple de « faire des trucs », qu'ils soient bons ou mauvais.

            La barrière d'apprentissage du JS est très basse grâce à ce genre de magie. Mais en même temps c'est ce qui a fait sa mauvaise réputation. C'est très facile de faire de la merde et de la merde pour en voir il suffit de faire un tour sur l'éditeur javascript ou ce genre de site…

            Comme la plupart des langages, tu peux faire des trucs pas rigoureux avec js, mais tu peux aussi faire des choses très rigoureuses. Tout dépend de toi. Le genre de choses que j'ai expliqué une fois que tu as compris le Js ce n'est plus un problème il suffit de ne pas l'oublier.

            Pour ne pas passer du coté obscur de la force, lit mon tuto et lit tous les liens qu'il y a en conclusion :D

            Que le force soit avec toi.
            • Partager sur Facebook
            • Partager sur Twitter
              8 avril 2009 à 2:12:08

              Merci golmote ! C'est plus clair maintenant.

              @ nod_:

              Citation : nod_

              Tout dépend de toi


              Oui je l'espere! Il me manque des bases d'algorithmique alors je tatonne et transforme le code petit a petit. Ce n'est pas non plus tres bon pour la clarté !

              Je suis dans la phase "découverte" donc chaque erreur m'est chere: ca fait une de plus que je ne ferais plus !

              J'avais déja jeté un oeil a ton tuto (enfin la partie sur timeOut/setInterval): j'y retourne ce soir meme (enfin demain paarceque la il est un peu tard...)

              Bon... Je crois qu'on peut clore cette discussion !

              Merci encore
              • Partager sur Facebook
              • Partager sur Twitter
                9 avril 2009 à 20:20:21

                Bon , en fait, j'ai une autre question qui,je pense, va te titiller un peu nod_ ...

                En effet, plus loin dans mon script, j'utilise d'autres setTimeout()pour déclencher de fort belle maniere l'apparition de paragraphes,images etc...
                Je ne fournis ici que l'extrait du code qui m'interpelle.
                actif.liste=actif.getElementsByTagName('*');      //où actif est un <div> contenant un <h1>, plusieurs <p> et <img>
                
                for (j=0;j<actif.liste.length;j++)
                {actif.liste[j].style.display="block";
                setTimeout(function(){increment_alpha(actif.liste[j]);},800+j*175);}
                

                Cette méthode ne fonctionne pas: la fonction increment_alpha(voire qq posts plus haut) n'aime pas ce qu'on lui envoie et dit que 'id is undefined'.

                (en revanche, laissetr tomber le setTImeout et faire un increment_alpha(actif.liste[j]); fonctionne. ce n'est donc pas un soucis de type d'argument.

                En revanche, avec cette séquence:
                actif.liste=actif.getElementsByTagName('*');      //où actif est un <div> contenant un <h1>, plusieurs <p> et <img>
                
                for (j=0;j<actif.liste.length;j++)
                {actif.liste[j].style.display="block";
                setTimeout(increment_alpha,800+j*175,actif.liste[j]);}
                

                Tout fonctionne à merveille.

                Ces 2 syntaxes ne sont donc pas tout à fait équivalentes.
                Malheureusement, nod_ mentionne dans son tuto que la 2eme maniere n'est pas supportée par IE...
                • Partager sur Facebook
                • Partager sur Twitter
                  9 avril 2009 à 22:38:24

                  Il mentionne aussi les closures (mais vite fait je crois ?) :
                  for (j=0;j<actif.liste.length;j++)
                  {actif.liste[j].style.display="block";
                  setTimeout((function(id){ return function(){increment_alpha(id);};})(actif.liste[j]),800+j*175);}
                  

                  pour avoir l'équivalent compatible IE ;) .
                  • Partager sur Facebook
                  • Partager sur Twitter
                  Anonyme
                    9 avril 2009 à 22:43:12

                    j'ai eu la flemme d'expliquer encore :p j'ai posté une explication il y'a pas longtemps de ça sur le même sujet, avec quelques détails :)
                    • Partager sur Facebook
                    • Partager sur Twitter
                      9 avril 2009 à 22:45:16

                      Faut dire qu'une fois compris, c'est facile, mais a expliquer, c'est la galère !!
                      • Partager sur Facebook
                      • Partager sur Twitter
                        10 avril 2009 à 0:01:16

                        Bon ok, je me lance dans une brève explication.

                        Alors alors...

                        En gros, si tu fais :
                        window.onload=ta_fonction();
                        

                        Elle va être exécuté directement... Et non pas au chargement... Pourquoi ? A cause des "()"...

                        Alors la solution est :
                        window.onload=ta_fonction;
                        

                        La tu affectes à l'événement onload la variable contenant ta fonction... Ainsi, la fonction ne sera exécutée qu'une fois le chargement fini.

                        Tu peux également faire :
                        window.onload=function() {
                          ta_fonction();
                        };
                        

                        Ca fera la même chose que le code précédent. La fonction anonyme ne sera pas exécutée tout de suite. Seulement au chargement.

                        Mais comment faire pour faire s'exécuter tout de suite la fonction anonyme ? Il faut en fait l'entouré de parenthèses, et ajouter "()" à la fin pour "forcer" l'exécution.

                        Donc :
                        window.onload = (function() {
                          ta_fonction();
                        })();
                        

                        (Bon, faire ceci est relativement inutile ici, mais c'est pour l'exemple...)

                        Quel est l'avantage de cette syntaxe ? Et bien c'est dans le cas de ton problème actuel. ^^ (En gros, le contexte qui revient le plus souvent, c'est une boucle, et dedans un setTimeout qui prend les valeurs de la boucle...

                        Donc si dans ton code tu fais :
                        for (j=0;j<actif.liste.length;j++){
                          actif.liste[j].style.display="block";
                          setTimeout(function(){
                            increment_alpha(actif.liste[j]); // Ici ça plante... j est constant
                          },800+j*175);
                        }
                        

                        Tu constates que ça plante... j n'a pas les différentes valeurs de la boucle, mais juste une valeur constante... (la valeur d'arrivée).

                        Alors comment faire pour que la fonction "mémorise" la valeur de "j" ? Et bien on va utiliser le principe des closures, pour passer un paramètre à notre function anonyme... Ce paramètre sera transmis à ta fonction et sera mémorisé !

                        for (j=0;j<actif.liste.length;j++){
                          actif.liste[j].style.display="block";
                          setTimeout((function(id){
                            return function() {
                              increment_alpha(id);
                            };
                          })(actif.liste[j]),800+j*175);
                        }
                        

                        Ici, on a donc une fonction anonyme exécuté immédiatement... et qui retourne une autre fonction anonyme...
                        Ca paraît compliqué, mais exécutons le code avec logique... ton code va "devenir" ceci :

                        for (j=0;j<actif.liste.length;j++){
                          actif.liste[j].style.display="block";
                          setTimeout(function() {
                              increment_alpha(actif.liste[j]); // Ici par contre ça plante pas !
                          },800+j*175);
                        }
                        

                        Mais ici, le actif.liste[j] a bien été mémorisé ! Et j prendra chaque valeur de la boucle.


                        C'est pas évident au premier abord, je te l'accorde. Mais c'est en le refaisant dans des contextes similaires (et mine de rien ça arrive souvent), qu'on prend le coup de main. :)
                        • Partager sur Facebook
                        • Partager sur Twitter
                          10 avril 2009 à 0:29:32

                          Wawawa... Je viens de passer 30 min a faire des essais avec la console Firebug (Trrrrrrrres bien pour apprendre ce truc !) et je suis arrivé a la conclusion qu'en fait j'envoyais a chaque fois j=9, que j n'étais pas évalué á chaque boucle, mais au moment ou le setTimeout expirait.
                          J'ai donc plus ou moins mis le doigt sur le schmilblick: j ne prend pas les 8 premieres valeurs.

                          Et la je vois que Golmote m'a pondu un explicatif détaillé !
                          Merci ! Je vois que j'avais vu vaguement ou se situait le problème.

                          Je vais aller bosser ta réponse et je reviens te dire si j'ai bien tout comprit !

                          En tous cas merci à tous pour votre patience et votre courtoisie !
                          J'ai vraiment l'impression de progresser assez vite avec votre aide: bcp de tâtonnement mais j'emmagasine beaucoup de choses et j'ai lentement des intuitions qui se mettent en place !
                          • Partager sur Facebook
                          • Partager sur Twitter
                            10 avril 2009 à 0:37:19

                            Crois-moi, à traîner sur le forum on apprend beaucoup...

                            (A vrai dire, j'ignorais l'existence des closures il y a de ça... 2 mois je pense. xD)



                            Et longue vie à Firebug :p
                            • Partager sur Facebook
                            • Partager sur Twitter
                              10 avril 2009 à 0:40:26

                              Wolooooloooo c'est bizarre ce truc ! :-)
                              Pour l'instant ca a du mal a passer. Disons que je l'accepte sans saisir tout le pourquoi du comment.
                              On verra ca a tete reposée demain matin.
                              • Partager sur Facebook
                              • Partager sur Twitter
                                10 avril 2009 à 23:42:53

                                Bon alors...
                                @ golmote: jusqu'á ton avant-dernier bout de code, je suis. J'ai du mal mais ca va.
                                Par contre le passage de l'avant-dernier au dernier morceau, je vois pas... Ca va venitr en utilisant ces "closures" je suppose...
                                D'ailleurs j'ai vu qu'il y avait un passage la dessus dans le livre de Flanagan sur JavaScript (celui avec le Rhinoceros).

                                Encore merci !
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  11 avril 2009 à 0:48:50

                                  Le dernier code est abstrait... Ca représente ce qu'il se passe, quand la closure renvoit la function anonyme...

                                  Sauf que l'argument que contenait la closure a été "sauvegardé" dans la fonction. Donc plus de problème de valeur constante !

                                  Mais en effet, utilise-le et tu verras qu'on finis par reconnaître les endroits où les closures sont nécessaires. ;)
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    11 avril 2009 à 1:02:17

                                    Citation : nod_

                                    C'est très facile de faire de la merde et de la merde pour en voir il suffit de faire un tour sur l'éditeur javascript ou ce genre de site…



                                    D'après les admins du site, y'a pas beaucoup de truc pourri xD

                                    Citation : Pas de titre

                                    Pas tant que ça, ce sont souvent des petits scripts, qui seront rapides à réécrire comme il faut.

                                    • Partager sur Facebook
                                    • Partager sur Twitter

                                    Une enieme question sur setTimeout en récursif.

                                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                    • Editeur
                                    • Markdown