molily: Scope Chain bei Inline-Event-Handlern

Beitrag lesen

<body onload="document.forms[0].elements[2].onclick = function() { alert(selectionStart()); };">

  

> Was bitte soll das? Warum ist es ein Unterschied, ob ich den EventHandler per Hand oder per Script einfüge?  
  
Mal weit ausgeholt:  
  
1\. Inline-Event-Handler sind quasi Funktionen  
  
<element onclick="JS-Code"> erzeugt ein Funktionsobjekt, das in der Eigenschaft onclick liegt. In Grunde genauso wie beim traditionellen Event-Handling (element.onclick = handlerfunktion).  
  
2\. Der Kontext der Funktion (this)  
  
Mit Kontext ist gemeint, auf welches Objekt das Schlüsselwort this zeigt. this zeigt in Inline-Event-Handlern auf das Elementobjekt, bei dem das Attribut notiert wurde.  
  
3\. Zugriff auf das Event-Objekt  
  
Die erzeugte Funktion erhält das Event-Objekt als ersten Parameter, man kann mit arguments[0] oder einfach event darauf zugreifen.  
  
4\. Die Scope Chain und die Identifier Resolution  
  
Sobald man in JavaScript einen Bezeichner notiert, versucht der JavaScript-Interpreter diesen aufzulösen. Beispiel:  
  
alert('hallo');  
  
Die Objekte, an denen nach dem Member »alert« gesucht wird, sowie deren Reihenfolge werden in der Scope Chain gespeichert (»Auflösungskette«).  
  
Wenn du in einem tradtionell registrierten Event-Handler alert() aufrufst, dann sieht die Scope Chain so aus (in der JS-Arrayschreibweise):  
  
[ window ]  
  
Sprich, es liegt nur das oberste, sogenannte globale Objekt drin. Das liegt immer am Ende der Scope Chain. Es wird also geschaut, ob window einen Member namens alert hat, was der Fall ist. Deshalb kann man in diesem Fall window.alert oder nur alert schreiben, das kommt auf dasselbe heraus.  
  
Die Scope Chain innerhalb von Inline-Event-Handlern sieht nun ganz anders aus und ist manipuliert. Der Chrome-Browser gibt uns einige seiner Interna preis, wenn wir die erzeugte Funktion ausgeben:  
  
~~~javascript
function onclick(evt) {  
	with (this.ownerDocument ? this.ownerDocument : {}) {  
		with (this.form ? this.form : {}) {  
			with (this) {  
				return (function(evt){alert('hallo')}).call(this, evt):  
			}  
		}  
	}  
}

Mit with () {} werden hier gleich drei Objekte in die Scope Chain eingefügt, sodass sie am Ende so aussieht:

[ element, element.form, document, window ]

Wenn ich im Inline-Event-Handler nun den Bezeichner »alert« notiere, dann wird diese Scope Chain von vorne abgearbeitet: Erst wird beim Elementobjekt geschaut, dann beim zugehörigen Formular (falls das Element ein Formularelement ist), dann beim document-Objekt und schließlich beim window-Objekt.

D.h. wenn du selectionStart schreibst, dann wird zuerst beim Element nach einem Member mit diesem Namen gesucht, was wie gesagt this.selectionStart entspricht. Erst wenn da kein Member gefunden wird, wird beim form-Element nachgeschaut, dann bei document, schließlich bei window.

Das ist das Prinzip gemäß ECMAScript, jetzt hast du festgestellt, dass die Browser nicht bloß die Scope Chain abarbeiten (dann müsste selectionStart() in allen Browsern, die die Eigenschaft kennen, einen Fehler auslösen), sondern auch offenbar noch eine Fehlererkennung einsetzen. Diese schaut anscheinend nach, was man mit dem aufgelösten Bezeichner, also dem Ergebnis des Ausdrucks macht. Hier sieht der Browser, dass das Ergebnis mit () aufgerufen werden soll, was natürlich nur geht, wenn der Bezeichner auf eine Funktion aufgelöst wird.

Mathias