ebody: Funktion innerhalb von addEventListener aufrufen

problematische Seite

Hallo,

ich rufe 2 Funktionen mit onclick im HTML Code auf:

<a href="https://codepen.io/" onclick="beforeDOM(); afterDOM();">addEventListener</a>

Die Funktion liegt innerhalb eines Event Listeners:

<script>
window.addEventListener('load', (event)=>{
function beforeDOM(){
  console.log('Before DOM!');
}
});
</script>
<p><a href="https://codepen.io/" onclick="beforeDOM(); afterDOM();">addEventListener</a></p>
<script>
window.addEventListener('load', (event)=>{  
  console.log('load!', event);
  
  function afterDOM(){
    console.log('After DOM!');
  }
  
});
</script>

Diese Zeile wird ausgeführt, wenn ich die Seite lade:

console.log('load beforeDOM!', event);

Dennoch ist die Funktionen nicht bekannt:

beforeDOM is not defined

Ich hätte jetzt vermutet, dass das Problem darin liegt, das onclick="beforeDOM()" zu erst geladen wird, zu diesem Zeitpunkt beforeDOM() noch nicht geladen wurde und daher nicht bekannt ist.

Aber das Problem besteht auch, wenn die Funktion innerhalb einer anderen Funktion liegt:

<a href="https://codepen.io/" onclick="beforeDOM2(); afterDOM2();">Function</a>
<script>
(function(){  
  
  function beforeDOM2(){
    console.log('Before DOM 2!');
  }
  
});
</script>
<p><a href="https://codepen.io/" onclick="beforeDOM2(); afterDOM2();">Function</a></p>
<script>
(function(){  
  
  function afterDOM2(){
    console.log('After DOM 2!');
  }
  
});
</script>

Wenn möglich, würde ich das Script erst am Ende des </body> ausführen. Aber warum werden die Funktionen nicht erkannt?

Gruß ebody

  1. problematische Seite

    Tach!

    Aber warum werden die Funktionen nicht erkannt?

    Weil sie in lokalen Scopes liegen und alles in einem lokalen Scope nur dort drin bekannt ist. Das hat nichts damit zu tun, dass man mehrere Eventhandler für dasselbe Event aufsetzen kann. Das sind ja auch nur jeweils neue Funktionen mit einem lokalen Scope und nicht irgendein Gebilde das scopetechnische Extrawürste von Javascript bekommt.

    dedlfix.

  2. problematische Seite

    Hallo ebody,

    da hast Du aber eine Teufelssuppe angerührt.

    Das einzige, was mir daran schräg vorkommt, ist der Aufruf von beforeDOM. Da hätte ich geschworen, dass das genauso schief geht wie die anderen 3.

    Bilden Pfeilfunktionen ihre Scopes auf andere Weise als normale Funktionen?! Jetzt muss ich erstmal an den Basteltisch.

    Und ich lese sie auch noch falsch. Dedlfix hat es hinreichend erklärt: Die Funktionen existieren nur in lokalen Scopes.

    Lösung: Registriere den click-Handler in dem Scope, wo auch die Funktionen leben, und verwende dafür addEventListener. Die Verwendung von onclick ist eh unschön.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. problematische Seite

      Hallo Rolf,

      sicherheitshalber zur Info:

      beforeDOM() ist nur ein Test, weil ich dachte es könnte evtl. mit der Lade Reihenfolge zu tun haben, wie im Beitrag beschrieben. Das Script möchte ich am Ende </body> ausführen, also nach dem HTML Code mit onclick.

      Gruß ebody

      1. problematische Seite

        Hallo ebody,

        nein, (Jedi-Memorytrick Handbewegung) du möchtest das nicht mit onclick machen.

        Das ist nicht der Droide, ahem, die Registrierung, nach der Du suchst.

        Du möchtest addEventListener verwenden.

        Spaß beiseite - du kapselst afterDOM doch in einer anderen Funktion, um den globalen Namespace nicht zuzuschütten, ja? Und dann geht onclick nicht, weil onclick nur globale Funktionen aufrufen kann.

        Wenn Du die Registrierung am Ende des <body> notierst, ist das a Element bereits im DOM und Du kannst es mit getElementById finden. Wenn die Handler-Funktion nur einmal gebraucht wird, kannst Du sie auch gleich als anonyme Funktion übergeben und brauchst Dich um Scope-Vermüllung nicht zu bekümmern.

        <a id="codepenLink" href="...">Click Me Or Click Me Not, See If I Care</a>
        
        <script>
        document.getElementById("codepenLink")
                .addEventListener("click", function(event) {
                   console.log("I don't care!");
                });
        </script>
        

        Das sieht natürlich etwas anders aus, wenn Du mit dem Elementobjekt des Links mehr machen willst als nur einen Click-Handler anzukleben, oder wenn die Handlerfunktion für mehrere Elemente registriert werden soll. Dann brauchst Du Variablen oder auch Funktionen, und dann sollte das Ganze in einer IIFE oder einem type=module Script liegen (Überraschung: Module gibts im IE nicht)

        Exkurs: wenn Du die Handlerfunktion nur für ein a Element registrieren willst, dann bist Du damit fertig.

        Aber wenn Du mehrere a Elemente hast und bei allen auf click reagieren willst, dann registriere den click-Handler auf dem Container und lass die click-Events dorthin blubbern. Du musst dann nur auf dem Target-Objekt abfragen, ob es ein Link ist.

        (function() {
           const cpLink = document.getElementById("codepenLink");
           cpLink.addEventListener("click", clickHandler);
        
           function clickHandler(event) {
              console.log("I don't care!");
           }
        })();
        

        Und wenn Du mit Bubbling arbeiten willst - und der Container bspw. der ganze body ist, kommst Du ggf. auch wieder ohne IIFE aus:

        document.body.addEventListener("click", function(event) {
           if (event.target.matches("a[href]")) {
              console.log("I still don't care!");
           }
        });
        

        Allerdings - Vorsicht mit event.target, wenn im a Element noch Kind-Elemente stehen.

        <a href="#">Guten <b>Tag</b></a>
        

        Klickst Du auf "Guten", ist event.target das a Element. Klickst Du auf "Tag", ist event.target das b Element. D.h. man muss nicht event.target prüfen, sondern event.target.closest("a[href]") - was natürlich nicht unbedingt existiert. Und closest gibt's wieder im IE nicht, d.h. Du brauchst ggf. einen Polyfill dafür. Es ist schon nervig.

        Rolf

        --
        sumpsi - posui - obstruxi