Peter Thomassen: onclick ...

Tag,

ich hab grad eine wunderhübsche Diashow (bspw. http://www.brasil-service.com/de/leistungen/) programmiert und hab dazu jetzt eine kleine Frage.

In http://www.brasil-service.com/slideshow.js, Zeile 13 befindet sich ein hässliches eval()-Statement, auf das ich erst nach einigen Fehlschlägen gekommen bin, denn führt man die zu Grunde liegenden Anweisung ohne eval() aus, werden den verschiedenen control-Objekten nicht verschiedene onclick-Handler zugewiesen, sondern stets derselbe (nämlich der mit dem letzten angenommen Wert von j), weil j wohl nicht aufgelöst wird, was ich mit eval() erzwinge.

Gibt es dennoch eine Möglichkeit, eval() hier zu umgehen? Ist bloß eine Frage der Ästhetik unter der Haube :-)

Danke!
Peter

  1. Hallo,

    Gibt es dennoch eine Möglichkeit, eval() hier zu umgehen? Ist bloß eine Frage der Ästhetik unter der Haube :-)

    Der Anweisung:

    eval('control.onclick = function() { slideshow_switch(' + j + '); }');

    würde entsprechen:

    control.onclick = new Function("slideshow_switch(" + j + ");");

    viele Grüße

    Axel

    1. Hallo,

      control.onclick = new [link:http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Function@title=Function]("slideshow_switch(" + j + ");");

      Ich find das immer noch unschön. Man sollte zu vermeiden suchen, JavaScript-Code dynamisch zu erzeugen, sei es nun eval, new Function oder setInterval/setTimeout. In der Regel verwendet man für sowas Closures, also einfach control.onclick = function () { slideshow_switch(j); };. j ist dann in der Funktion verfügbar. Hier ist das aber nicht so einfach, weil die Variable j nach der onclick-Zuweisung noch geändert wird. Gut, dafür bietet sich hier eine andere elegante Möglichkeit an: Man speichert die Nummer einfach direkt am betreffenden Element, bei dessen Event-Handler sie benötigt wird.

      var control = document.createElement('img');  
      control.src = '/player_play.png';  
      control.className = 'control';  
      control.slideshow_number = j;  
      control.onclick = slideshow_switch;  
        
      function slideshow_switch (e) {  
         alert(this.slideshow_number);  
      }
      

      Anstatt des mehrdimensionalen numerischen Arrays slideshow würde ich Object-Objekte verwenden und diese in den Array hängen:

      slideshow = {  
         image_list : elements[i].getElementsByTagName('img'),  
         active_image : 1,  
         active : false,  
         interval : null  
      };  
      slideshows.push(slideshow);
      

      slideshows[number].active ist dann lesbarer als slideshows[number][2].

      Ein weiterer Schritt wäre, alles mit Closures und dem Speichern an den Elementknoten zu lösen. Das zentral-globale Speichern aller Informationen in einem mehrdimensionalen Array ist wahrscheinlich nicht nötig. Ein Beispiel:

      window.onload = function () {  
       mod_slideshow.init();  
      };  
        
      function hasClass (element, className) {  
       var regexp = new RegExp("(^|\\s)" + className + "(\\s|$)");  
       return regexp.test(element.className);  
      }  
        
      var mod_slideshow = new Object;  
        
      mod_slideshow.interval = 3000;  
        
      mod_slideshow.init = function () {  
       var elements = document.getElementsByTagName('*');  
       for (var i = 0, element, slideshow; i < elements.length; i++) {  
        element = elements[i];  
        if (!hasClass(element, "slideshow")) {  
         continue;  
        }  
        slideshow = {  
         image_list : element.getElementsByTagName('img'),  
         active_image : 0,  
         active : false,  
         interval_id : null  
        };  
        var control = document.createElement('img');  
        control.src = '/player_play.png';  
        control.className = 'control';  
        control.slideshow = slideshow;  
        control.onclick = mod_slideshow.switcher;  
        element.appendChild(control);  
       }  
      };  
      mod_slideshow.switcher = function (e) {  
       var control = this;  
       var slideshow = control.slideshow;  
       if (slideshow.active) {  
        window.clearInterval(slideshow.interval_id);  
        control.src = '/player_play.png';  
       } else {  
        mod_slideshow.trigger(control);  
        control.src = '/player_pause.png';  
        var closure = function () { mod_slideshow.trigger(control); };  
        slideshow.interval_id = window.setInterval(closure, mod_slideshow.interval);  
       }  
       slideshow.active = !slideshow.active;  
      };  
        
      mod_slideshow.trigger = function (control) {  
       var slideshow = control.slideshow;  
       slideshow.image_list[slideshow.active_image].style.display = 'none';  
       slideshow.active_image++;  
       slideshow.active_image = slideshow.active_image % (slideshow.image_list.length - 1);  
       slideshow.image_list[slideshow.active_image].style.display = 'inline';  
      };  
      
      

      Der globale Array mit allen Slideshow-Informationen fällt damit weg, die jeweiligen Infos werden in einem Object an der Control gespeichert.

      Wie in https://forum.selfhtml.org/?t=130522&m=843795 habe ich alle zusammenhängenden Methoden und Eigenschaften in einem Object gruppiert, der globalen Scope bleibt aufgeräumt.

      Den HTML-Code könnte man natürlich im Hinblick auf Semantik optimieren, in der Regel setzt man eine ol oder ul ein. Außerdem kann man das ganze auch noch zugänglich ohne Javascript und Maus gestalten.

      Mathias