• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

[FRAGE] Zugriff auf Globale Variable durch Eventlistener

sascha83

New member
Ich greife in einer Schleife auf ein globales Array zu und lasse mit dann die einzelnen Elemente in einer Schleife per alert ausgeben. Soweit kein Problem. Jetzt mochte ich mir diese Werte auch ausgeben lassen, wenn ich meinen Container names mit einem Eventlistener 'clickable' setze. Mit jedem Klich auf den Container sollen ein Name ausgegeben werden.

Das Problem ist nun, dass das Array an diese Stelle(siehe Kommentar), leer ist bzw. für ein einzelnes Element am Index i 'undefined' angezeigt wird. Was mache ich falsch.

Code:
var name = ['Suse', 'Rainer', 'Karl-Heinz'];

function myFunction() {
   /* CODE DER FUNZT */
   for(var i = 0; i < name.length; i++) {
        alert(name[i]);
        $(document).ready(function() {
            names.addEventListener("click", function() {
                console.log(name[i]); //hier hat die Globale Variable plötzlich keine Werte
            });
        });
    }
   /* CODE DER FUNZT */
}

Vielen Dank für Eure Hilfe ;)
 
Bin mir nicht 100 % sicher aber ich glaube, das hat damit zu tun, dass das Array nur zur Laufzeit des Scripts bekannt ist. Später bei Auslösen des Event Listeners kennt der Code das nicht mehr, weil die Schleife ja längst fertig ist. In solchen Situationen gehe ich immer her und speichere mir den gebrauchten Wert im DOM Node in einem data-* Tag. Das lese ich dann beim klick aus und hab was ich brauche.
Ich weiß aber nicht, wie das heißt und was dafür die professionelle Lösung ist. Außerdem hast du ja garkein Node, brauchst also eh eine andere Lösung. Wir hatten dazu kürzlich einen Thread, weil ich das da auch schon nicht richtig verstanden hatte aber ich finde den nicht mehr.

@kkapsner: Kannst du uns da bitte nochmal auf die Sprünge helfen.
 
Ich bin zwar nicht kkapsner, aber das Problem heißt Closure. In dem Fall sind i und name die geclosureten Variablen, was bedeutet, daß wenn der Event-Listener ausgeführt wird, der letzte Wert von i (nämlich i = name.length) verwendet wird, was halt undefined ergibt.

Variante 1: wie schon beschieben eine Property des Event-behandelten Objekts als Speicher nutzen

Variante 2: Closure durch ein anderes Closure (z.B. ein IIFE) zu ersetzen
Code:
while (i--) {
    (function (x) {
        obj.addEventListener('click', function (evt) {
            doSomethingWith(x);
        });
    })(i);
}
 
Kann da nur noch ergänzend den Ansatz über .forEach einwerfen:
Code:
function myFunction(){
   name.forEach(function(name){
   	alert(name);
        $(document).ready(function() {
            names.addEventListener("click", function() {
                console.log(name);
            });
        });
   });
}

PS: globale Variablen... tztztz
PPS: Ich würde ja nicht für jedes Element in name einen document.ready-Listener registrieren, sondern die Iterierung in einem document.ready machen...
 
@Korbinian: Weil ich jetzt in dem Code deines letzten Posts keinen Unterchied in der Struktur gesehen habe, hab ich mal versucht, den zum laufen zu bekommen. Aber es tut sich weder im Browserfenster noch in der Console irgend etwas. Was mache ich falsch?
HTML:
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Test</title>
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
	<style type="text/css">
	body {
		color:		#000000;
		font-size:	12px;
	}
	</style>
</head>
<body>

<div>Hallo Welt!</div>

<script>
var name = ['Erika','Edeltraut','schinkenspeck'];
function myFunction(){
   name.forEach(function(name){
   	alert(name);
        $(document).ready(function() {
            names.addEventListener("click", function() {
                console.log(name);
            });
        });
   });
}
</script>

</body>
</html>
 
@mikdoe: du rufst die Funktion myFunction() ja gar nicht auf... ABER wir haben hier noch ein kleines Browserproblem: window.name ist der Name des Fensters und deswegen kann man keine globale Variable, die so heißt selbst erstellen (bzw. man kann window.name schon etwas zuweisen, aber das ist dann immer ein String).

HTML:
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Test</title>
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
	<style type="text/css">
	body {
		color:		#000000;
		font-size:	12px;
	}
	</style>
</head>
<body>

<div>Hallo Welt!</div>

<script>
var names = ['Erika','Edeltraut','schinkenspeck'];
function myFunction(){
   names.forEach(function(name){
   	alert(name);
        $(document).ready(function() {
            names.addEventListener("click", function() {
                console.log(name);
            });
        });
   });
}
myFunction();
</script>

</body>
</html>
 
Ja, das mit name und names hatte mich nämlich stutzig gemacht, daher überhaupt erst meine Nachfrage.

Aber es bleibt bei mir trotzdem im IE:
Das Objekt unterstützt die Eigenschaft oder Methode "addEventListener" nicht
Datei: test.html, Zeile: 24, Spalte: 13
 
names hatte ich aus dem Code von sascha übernommen - keine Ahnung, wo diese Variable herkommt. Zum Testen kannst du das ja durch document.body ersetzen
 
Ja, ok, so läuft es.
Aber was ist denn jetzt an kkapsner's Lösung weniger closure als bei sascha? Wieso schreiben die Namen von Sascha beim Anklicken "undefined" in die Konsole? Vorher die alert's sind ja noch gleich. Wo genau liegt da jetzt der Unterschied?
Macht hier jetzt wirklich die Art der Schleife den ausschlaggebenden Unterschied? Ist forEach kein closure, for aber schon?

HTML:
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Test http://forum.jswelt.de/javascript/61035-zugriff-globale-variable-eventlistener.html</title>
</head>
<body>
<div id="Suse">Suse</div>
<div id="Rainer">Rainer</div>
<div id="Karl-Heinz">Karl-Heinz</div>
<hr>
<div id="Erika">Erika</div>
<div id="Edeltraut">Edeltraut</div>
<div id="schinkenspeck">schinkenspeck</div>
<script>
var namessascha = ['Suse','Rainer','Karl-Heinz'];
function myFunction_sascha() {
	for(var i = 0; i < namessascha.length; i++) {
		alert(namessascha[i]);
		document.getElementById(namessascha[i]).addEventListener("click", function() {
			console.log(namessascha[i]);
		});
	}
}
myFunction_sascha();
//
//
//
var nameskkapsner = ['Erika','Edeltraut','schinkenspeck'];
function myFunction_kkapsner(){
	nameskkapsner.forEach(function(name){
		alert(name);
		document.getElementById(name).addEventListener("click", function() {
			console.log(name);
		});
	});
}
myFunction_kkapsner();
</script>
</body>
</html>
 
Wo genau liegt da jetzt der Unterschied?
Macht hier jetzt wirklich die Art der Schleife den ausschlaggebenden Unterschied? Ist forEach kein closure, for aber schon?
es ist beides ein Closure, aber jeweils ein anderes /1/. Denn forEach() hat einen anderen Scope als for(). Der Scope bei forEach() enkoppelt dich vom globalen Scope, genauso wie das IIFE in Post #3.

1 - genauer gesagt entsteht das Closure am Event-Handler. Die geclosurten Variablen kommen aber bei for() (global) und forEach() (loop) aus unterschiedlichen Scopes.
 
Zuletzt bearbeitet:
Oh wei, das ist aber heiße Materie. Schwer auseinander zu halten, zumindest für mich. Trotzdem danke für die Erklärung.
Wenn jemand das nochmal mit anderen Worten erklären kann wäre ich daran sehr interessiert. Gibt es einen Tipp, wie man sich das merken kann?
 
hm, eigentlich mehr eine Regel: Der Wert einer geclosurten Variablen wird beim Aufruf (des Closures) bestimmt, nicht wenn das Closure erzeugt wird.
 
Danke Dormilich, aber wie kommen da jetzt die beiden Schleifenvarianten ins Spiel? Wo genau beginnt und endet da das closure?
 
aber wie kommen da jetzt die beiden Schleifenvarianten ins Spiel?
direkt gesehen gar nicht. der entscheidende Punkt ist, daß du für forEach() ein callback brauchst und damit (und das ist der entscheidende Punkt) einen neuen Scope aufmachst.

Wo genau beginnt und endet da das closure?
Ein Closure im direkten Sinne hat keinen Beginn und Ende /1/. Es ist eine Situation, die unter bestimmten Randbedingungen auftritt. Im Falle eines Closures ist das:
- eine Variable aus einem äußeren Scope wird verwendet
- die innere Funktion bleibt auch nach dem Abarbeiten der äußeren Funktion erhalten (die häufigsten Möglichkeiten dazu sind 1) innere Funktion via return übergeben, 2) innere Funktion als Event-Handler verwenden)

Closure mal erklärt mit ASCII:
Code:
+-[outer scope]-------+
|                     |
|   var foo           |
|                     |
|   +-[inner scope]---+----+
|   |                 ·    |
|   | doSomethingWith(foo) |
|   |                 ·    |
|   +-----------------+----+
|                     |
+---------------------+

noch ein Hinweis zum Originalcode:
Code:
console.log(name[i]); //hier hat die Globale Variable plötzlich keine Werte
Die Schlußfolgerung ist falsch. name ist sehr wohl definiert (und enthält auch alle Werte), ebenso wie die globale Variable i. Nur die Kombination aus beiden ergibt halt undefined.


1 - bei einer Konjunktion (Astronomie) gibt es ja auch keinen Anfang und Ende
 
So ganz ist das noch nicht gesackt bei mir.
Frage: Wenn ich den später benötigten Wert vorher in einem 'data-*' Attribut eines DOM Elements speichere, ist das best practice oder zumindest noch als OK zu bezeichnen oder wird davon abgeraten?
 
Ich wüßte nicht, daß davon abgeraten würde. Außerdem lassen sich so jede Menge Daten speichern, sofern sie denn zur Erstellung des HTML bekannt sind. Andererseits fände ich es komisch data-* Attribute on demand (also direkt vor dem event-handler-assignment) zu erzeugen, nur um sie als Speicher zu verwenden (soll heißen, die Daten in data-* müssen schon was mit dem Element zu tun haben, z.B. eine AJAX-URL in einem Button zu speichern halte ich für legitim).
 
Zurück
Oben