Chris: Event Problem in einer Schleife

Hallo,

ich arbeite gerade mit Typo3 und will sein erzeugten JavaScript nicht nutzen. Dazu muss ich die onmouseover-Events ändern.
Hier das Script:

function changeScript(){
 links = document.getElementById("secSubmenu").getElementsByTagName("a");
 for(x = 0; x < links.length; x++){
  name = links[x].firstChild.getAttribute("name");
  links[x].setAttribute("onmouseover", function(){ myover(name); }, true);
 }
}

function myover(name){
 alert(name);
}

Soweit so gut, die Funktion konnte ich erfolgreich ändern, jedoch wird bei jedem <a>-Tag der Name des letzten Schleifendurchgangs übergeben.

Wenn ich in der Schleife ein Alert(name) hinzufüge, gibt er mir unterschiedliche Namen aus, so sollte es auch sein, aber wieso dann immer derselbe Name bei allen <a>-Tags?

  1. Hallo,


      links[x].setAttribute("onmouseover", function(){ myover(name); }, true);

    Hier musst du dich entscheiden. Entweder willst du ein onmouseover-Attribut erzeugen, was dann so aussehen müsste:

    links[x].setAttribute("onmouseover","myover(name)");

    …oder du erstellst ein Event:

    links[x].onmouseover = function(){myover(name);}

    mfg. Daniel

    1. Hallo,

      links[x].setAttribute("onmouseover","myover(name)");

      Man sollte dazu sagen, dass diese Möglichkeit nicht dazu führt, dass beim Mouseover die gewünschte Funktion ausgeführt wird - zumindest nicht in allen Browsern.

      …oder du erstellst ein Event:

      links[x].onmouseover = function(){myover(name);}

      Man sollte dazu sagen, dass »name« hier garantiert nicht der gewünschte, in dem jeweiligen Schleifendurchgang in Erfahrung gebrachte Name ist.

      Genauer gesagt wird in diesem Kontext eine Closure notiert (siehe auch):

      for (var x = 0, link, name; x < links.length; x++) {  
         link = links[x];  
         name = link.firstChild.name;  
         link.onmouseover = function () { myover(name); };  
      }
      

      Es werden viele Funktionsobjekte erzeugt. Diese Funktionen haben nur deshalb Zugriff auf die Variable name, weil durch das Notieren der anonymen, inneren Funktion in der äußeren Funktion die lokalen Variablen der äußeren auch in der inneren verfügbar sind (das ist der Closure-Effekt).

      Man hat in diesen vielen Funktionen also Zugriff auf die Variable name, aber deren Wert ist nicht der gewünschte. Der Wert ist - wie gesagt - der, den die Variable als letztes bei der Ausführung der äußeren Funktion (die mit der Schleife) hatte, also beim letzten Schleifendurchgang. Wenn der Wert z.B. »abc« ist, dann wird bei egal welchem Mouseover immer myover("abc") aufgerufen.

      Das liegt daran, dass einfach die lokalen Scopes der Funktionen verknüpft werden. Die Namen zeigen in allen Funktionen auf dieselbe Speicherstelle, die auch nach der Ausführung der Funktion mit der Schleife erhalten bleibt. Das Problem von Closures und Schleifen hatte ich bereits hier erklärt:
      </archiv/2006/9/t136283/#m885076>

      Gut, wie kann man das Problem lösen?
      Dazu hatte ich Alternativen beschrieben:
      </archiv/2006/10/t138533/#m900197>
      Und auch hier:
      http://aktuell.de.selfhtml.org/artikel/javascript/organisation/#datenspeicherung

      So kann es z.B. funktionieren:

      for (var x = 0, link, name; x < links.length; x++) {  
         link = links[x];  
         link.name = link.firstChild.name;  
         link.onmouseover = myover;  
      }  
      ...  
      function myover (e) {  
         alert(this.name);  
      }
      

      Mathias

      1. Vielen Dank Mathias, dein Posting hat mir sehr geholfen.

        Chris

  2. Hier das Script:

    function changeScript(){
    links = document.getElementById("secSubmenu").getElementsByTagName("a");
    for(x = 0; x < links.length; x++){
      name = links[x].firstChild.getAttribute("name");
      links[x].setAttribute("onmouseover", function(){ myover(name); }, true);
    }
    }

    Was ist firstChild?
    Normalerweise ist das bei einem Link ein Textknoten, der keine Attribute hat. Falls es ein Bild ist, würde ich das auch explizit abfragen. Den Event kann man auch ohne setAttribute setzen, was auch zuverlässiger ist. Und dann hast du natürlich das Problem mit dem closure, dass dort name nach dem durchlaufen der Schleife immer den Wert des letzten Namens hat. Ausserdem würde ich nie globale Variabeln verwenden.

    var links = document.getElementById("secSubmenu").getElementsByTagName("a");
    for(var i = 0; i < links.length; i++)
    {
    links[x].onmouseover = function()
    {
    myover(this.firstChild.name);
    // oder: myover(this.getElementsByTagName('img')[0].name);
    };
    }

    Struppi.

    --
    Javascript ist toll (Perl auch!)