Apropos: Problem mit onclick in Opera 9

Hi,

ich bin doch etwas erstaunt, dass es dieses Mal ausgerechnet Opera ist, das zickt...

Also, ich habe folgenden Quelltext:

  
[...]  
  
<form action="#" method="post">  
	<p><textarea name="text"></textarea></p>  
	<p>  
		<button type="button" onclick="insert('<a>','</a>');">A</button>  
		<button type="button" onclick="alert(selectionStart());">Anfang</button>  
	</p>  
</form>  
  
[...]

Nun hat Opara v9.64 (im Gegensatz zu FF, Chrome und IE) offenbar Probleme mit dem onclick-Attribut beim zweiten Button (beim ersten klappt's). Und zwar meldet Opara diesen typischen, nichtssagenden "Type mismatch" mit Verweis auf das Script im onclick-Attribut.

Ich dachte erst, es wäre eigentlich ein Problem mit dem Script in der Funktion selectionStart(). Aber das kann eigentlich nicht sein, denn wenn ich das onclick-Attribut entferne und statt dessen

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

ergänze, funktioniert es auf einmal wunderbar.

Was bitte soll das? Warum ist es ein Unterschied, ob ich den EventHandler per Hand oder per Script einfüge?

viel Spasz,
Apropos

PS: Mir ist bei alledem aufgefallen, dass Opera 9 offenbar für Textauswahlen jetzt nicht mehr das IE-Modell benutzt wie die Vorgänger. Aber das nur am Rande.

  1. Was bitte soll das? Warum ist es ein Unterschied, ob ich den EventHandler per Hand oder per Script einfüge?

    Der Kontext. Aber wie kommst du drauf, dass selectionStart eine Funktion ist? Ich kenn nur die Eigenschaft.

    Struppi.

    1. Aber wie kommst du drauf, dass selectionStart eine Funktion ist? Ich kenn nur die Eigenschaft.

      Ich habe ein Funktion geschrieben, die selctionStart heißt. Aber offennbar ist genau das der Punkt. Als einziger Browser (der genannten) interpretiert Opera den Aufruf im onclick-Attribut offenbar als

        
      this.selectionStart()  
      
      

      was natürlich nicht klappen kann. Nachdem ich die Funktion umbenannt habe, funktioniert es. Danke!

      1. Als einziger Browser (der genannten) interpretiert Opera den Aufruf im onclick-Attribut offenbar als

        this.selectionStart()

        
        > was natürlich nicht klappen kann.  
          
        Jein, er ist nicht der einzige Browser. Für den JS-Code in Event-Handler-Attributen liegt das Elementobjekt als erstes in der Scope Chain. Beim Auflösen von Identifiern wie »selectionStart« wird also zuerst beim Elementobjekt geschaut, ob dort ein entsprechender Member existiert. Das machen prinzipiell auch andere Browser. Nur offenbar haben die anderen Browser eine Fehlererkennung drin: »Wenn this.selectionStart existiert, aber keine Funktion ist, meint der Programmierer sicher nicht diese Eigenschaft, sondern eine bei einem anderen Objekt in der Scope Chain.« So eine Fehlererkennung ist aber nirgendwo spezifiziert. Was du tun kannst:  
          
        - Verzicht auf Inline-Event-Handler.  
        - Globale Funktionen sind Methoden des window-Objektes und als solche kann man sie ansprechen: window.meineGlobaleFunktion(). Das ist eindeutiger als bloß meineGlobaleFunktion.  
          
        Mathias
        
        -- 
        [JavaScript-Erweiterung für das SELFHTML-Forum](http://forum.de.selfhtml.org/js/doku/)
        
  2. <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