Parameter bei Event-handler
ds2k5
- javascript
hallo,
ich arbeite zurzeit an einer kleinen Webanwendung, geschrieben in (Objekt orientiertem-) JavaScript (wobei ich sagen muss, dass ich mit JS nicht so erfahren bin). Bisher hat alles funktioniert, allerdings bin ich Gestern auf das folgende Problem gestoßen:
Ich habe eine mit JS generierte Liste. Nun will ich, dass beim Überfahren eines Eintrages mit der Maus, bestimmte Informationen zum Eintrag angezeigt werden. Diese sind in einem Objekt gespeichtert.
function Viewer(parent)
{
this.parent = parent;
this.ListeDarstellen = function (eintraege)
{
for(var i = 0; i < this.Eintraege.length; i++)
{
neuerEintrag = this.Eintraege[i];
listenEintrag = document.createElement('div');
listenEintrag.innerHTML = neuerEintrag.Name;
listenEintrag.onmouseover = zeigeInfo;
....
}
}
}
So ein etwa sieht bei mir die Klasse aus die für die Darstellung von Daten verantwortlich ist. Man kann dem Event-handler aber keine Parameter mitgeben, und ich brauche auf jeden Fall eine Referenz zum Objekt des Eintrages.
Ich komme nur auf eine Lösung mit globalen Variablen, was ich aber nur machen würde wenn es auch die einzige Möglichkeit ist.
Ich hoffe es gibt eine bessere Lösung, und jemand kann mir verraten wie ich mein Problem lösen kann :)
Vielen Dank im Voraus
mfG
Hallo,
Zunächst fällt auf, dass mit der Zeile
this.ListeDarstellen = function (eintraege)
ein parameter "eintraege" entgegengenommen, aber nicht weiter verwendet wird. Statt dessen wird im folgenden ein evtl. undefiniertes etwas namens "Einträge" angesprochen:
for(var i = 0; i < this.Eintraege.length; i++)
Dann hast du eine innere Funktion "ListeDarstellen", deren "this" evtl. nicht dasselbe ist, wie das "this" der Funktion "Viewer". In "ListeDarstellen" brauchst du das "this" auch nicht, da ihr Parameter "eintraege" ja übergeben wird.
So geht es möglicherweise besser:
function Viewer(parent)
{
this.parent = parent;this.ListeDarstellen = function (eintraege)
{
for(var i = 0; i < eintraege.length; i++)
{
neuerEintrag = eintraege[i];
listenEintrag = document.createElement('div');
listenEintrag.innerHTML = neuerEintrag.Name;
listenEintrag.onmouseover = zeigeInfo;
....
}
}
}
Gruß, Don P
Hallo,
Zunächst fällt auf, dass mit der Zeile
this.ListeDarstellen = function (eintraege)
ein parameter "eintraege" entgegengenommen, aber nicht weiter verwendet wird. Statt dessen wird im folgenden ein evtl. undefiniertes etwas namens "Einträge" angesprochen:
for(var i = 0; i < this.Eintraege.length; i++)
hallo Don P, die Klasse an sich funktioniert bestens, das Stück code war nicht aus meinem echten Code, sondern nur um zu verdeutlichen wie die Liste generiert wird (deswegen auch die komischen Funktions- und Variablen Namen ;)), aber du hast natürlich Recht, dass es in der Form nicht funktionieren würde
Dann hast du eine innere Funktion "ListeDarstellen", deren "this" evtl. nicht dasselbe ist, wie das "this" der Funktion "Viewer". In "ListeDarstellen" brauchst du das "this" auch nicht, da ihr Parameter "eintraege" ja übergeben wird.
this.eintraege ist natürlich falsch, allerdings ist "ListeDarstellen" eine Member Funktion von "Viewer", genauso wie Member Variablen, welche auch mit "this." deklariert werden, weshalb ich sagen würde, dass es eigentlich stimmen müsste und funktieren tut es auch
Hallo,
this.eintraege ist natürlich falsch, allerdings ist "ListeDarstellen" eine Member Funktion von "Viewer", genauso wie Member Variablen, welche auch mit "this." deklariert werden, weshalb ich sagen würde, dass es eigentlich stimmen müsste und funktieren tut es auch
Das vermaledeite "this" habe ich schon des öfteren verflucht. Nimm z.B. folgendes:
window.onload = function () {
var wrapper = {
allowOpen: true,
inputBox: document.getElementById("InpName"),
openButton: document.getElementById("BtnOpen"),
init: function () {
var that = this; // Sehr wichtig!!
this.openButton.onclick = function () { that.openIt(); };
this.closeButton.onclick = function () { that.closeIt(); };
this.inputBox.focus();
},
openIt: function () {
if (this.allowOpen) { // this muss das wrapper-Objekt sein!
// mach was z.B. mit this.inputBox.value;
} else {
this.inputBox.focus();
}
}
};
wrapper.init();
};
Das funktioniert. Aber wenn man nicht höllisch aufpasst (var that = this), zeigt "this" zur Laufzeit auf irgend etwas, was man nicht haben will...
Gruß, Don P
Das funktioniert. Aber wenn man nicht höllisch aufpasst (var that = this), zeigt "this" zur Laufzeit auf irgend etwas, was man nicht haben will...
Das stimmt, und es macht auch Sinn, dass es auf etwas anderem verweist.
Wenn man this in dem Event-handler benutzen würde, würde es sich auf das Button beziehen, welches in dem Fall das Objekt wäre das die Funktion aufruft.
Durch das "that = this" setzt man eine Referenz zu der bestimmten Instanz von dem "wrapper" Objekt.
Nun habe ich auch mein Problem gelöst, mir ist wirklich nicht eingefallen, dass man den Event-handler auch als neue Funktion definieren kann, um eben die Parameter weiterzugeben.
... so einfach ist es und ich wäre schon total verzweifelt.
Vielen Dank für die Lösung :)
Ich hoffe dass die Diskussion, falls sie jemand liesen sollte, zum Verstehen der 'this' Referenz helfen wird.
Hallo,
... so einfach ist es und ich wäre schon total verzweifelt.
Vielen Dank für die Lösung :)
Gern geschehen. Das Verzweifeln habe ich in den letzten Tagen für dich übernommen, denn genau das Problem hat mich viele Stunden gekostet und mir vermutlich ein paar graue Haare mehr verschafft, bis ich es endlich durchschaut hatte und auf die gepostete Lösung kam.
Die separat aufzurufende init()-Funktion gefällt mir aber noch nicht ganz. Irgendwie habe ich eine dumpfe Ahnung, dass es auch irgendwie ohne den extra-Aufruf gehen müsste. Natürlich könnte man damit leben, aber ich bin zur Zeit auf einem Optimierungs-Trip ;-)
Ich hoffe dass die Diskussion, falls sie jemand lesen sollte, zum Verstehen der 'this' Referenz helfen wird.
So sei es. Möge das 'this' mit uns sein :-)
Gruß, Don P
Hallo Leute,
Habe jetzt das Ganze noch etwas vereinfacht *brustschwell*:
window.onload = { // Anonymes Objekt definieren:
inputField: document.getElementById("InputField"), // Eingabefeld für text (DOM-Objekt)
actionButton: document.getElementById("ActionButton"), // Button um Action zu machen (DOM-Objekt)
allowAction: true, // Eigenschaft dieses anonymen Objekts
init: function () { // Methode definieren, die Eventhandler einbaut
var that = this; // ! Muss sein für Eventhandler wie diesen:
this.actionButton.onclick = function () {that.action()}; // Eine Action, die wir machen wollen
},
action: function () { // Eventhandler definieren: Der kann jetzt
// über 'this' auf jede Eigenschaft und
if (this.allowAction) { // Methode des anonymen Objekts zugreifen,
// hier also z.B. etwas mit this.inputBox.value
// (dem Text im Eingabefeld) machen bzw. mit
// jeder beliebigen Eigenschaft und Methode
// des anonymen Objekts arbeiten.
} else {
this.inputBox.focus(); // z.B. mal den Cursor ins Eigabefeld setzen.
}
}
}.init(); // Eventhandler vollends einbauen.
Auf diese Weise braucht man keine einzige globale Variable, d.h. das window-Objekt bleibt absolut sauber und unsere Eventhandler haben immer alles nötige im Zugriff. Es braucht auch keine onclick-, onmouseover-, oder sonstige Eventhandler-Attribute im HTML-Code, so dass das HTML/XHML ebenfalls absolut sauber bleibt, ganz im Sinne der hier immer geforderten strikten Trennung von HTML, Script und CSS-Styles.
Gibt's da vielleicht irgendwelche Nachteile, die mir entgangen wären?
Oder will mir sogar jemand ein "fachlich hilfreich" geben? *bettel* ;-)
Don P
Hallo,
window.onload = { ... }.init();
Du meintest wohl eher `window.onload = { ... }.init;`{:.language-javascript}, sonst ist die Zuweisung unsinnig.
Und dann müsste es `window.onload = ({ ... }).init;`{:.language-javascript} sein, um keinen Syntaxfehler auszulösen.
Das völlige Verstecken macht natürlich solange Sinn, wie man ein Script mit einem onload-Handler hat. Im anderen Fall kann man ein Objekt aber trotzdem mit `(function () { var obj = {... }; addLoadEvent(obj.init); })();`{:.language-javascript} kapseln.
Mathias
--
[SELFHTML aktuell Weblog](http://aktuell.de.selfhtml.org/weblog/)
Hallo molily,
window.onload = { ... }.init();
> Du meintest wohl eher `window.onload = { ... }.init;`{:.language-javascript}, sonst ist die Zuweisung unsinnig.
Eigentlich meine es schon genau so wie es dasteht, denn das gibt keinen Sytaxfehler, obwohl die init()-Funktion undefined zurückgibt.
Ok, ein bisschen unsinnig scheint die Zuweisung zu sein, aber warum sollte man window.onload nicht undefined zuweisen? Zu diesem Zeitpunkt ist das anonyme Objekt ja bereits erzeugt und die Eventhandler sind an ihre DOM-Objekte gebunden (dank init()), ein späteres window.onload kann nicht mehr vorkommen, es sei denn, man lädt die Seite neu, aber dann geschieht ja wieder dasselbe: Das Objekt wird neu erzeugt und die Eventhandler zugewiesen.
Mit anderen Worten, der Code bedeutet nur: Wenn das Fenster fertig geladen ist (onload), soll eine Funktion ausgeführt werden, und zwar die init()-Methode des gerade erzeugten Objekts, wobei deren Rückgabewert (undefined) dann der window.onload-Handler wird. Den brauchen wir wie gesagt nachher auch nicht mehr. init() könnte auch pro forma null oder eine leere Funktion oder beliebige andere Funktion zurückgeben, wenn man meint, das zu brauchen.
> Und dann müsste es `window.onload = ({ ... }).init;`{:.language-javascript} sein, um keinen Syntaxfehler auszulösen.
Das verstehe ich jetzt nicht ganz, habe es auch nicht überprüft, meinen Code dagegen schon, und der ergibt definitiv keinen Syntax- und keinen sonstigen Fehler und funktioniert genau wie gewollt, auch mit weit umfangreicheren Objekten und Eventhandlern. Habe fast eine Woche daran herumgedoktert und ausgiebig getestet, bis es soweit war.
> Das völlige Verstecken macht natürlich solange Sinn, wie man ein Script mit einem onload-Handler hat.
Was spricht dagegen, immer einen zu verwenden?
Im anderen Fall kann man ein Objekt aber trotzdem mit `(function () { var obj = {... }; addLoadEvent(obj.init); })();`{:.language-javascript} kapseln.
Das mag sein. Vor kurzem gab es hier einen Thread über sowas wie "addLoadEvent", und das Einzige, was ich beim Überfliegen verstanden habe war, dass das Ganze recht kompliziert wird, wenn man es richtig machen will.
Was meinen Vorschlag hier angeht, dürfte es natürlich auch nicht einfach sein (oder gar unmöglich?), den onload-event dynamisch zu erweitern...
Ich muss zugeben, dass ich mir darüber noch keine Gedanken gemacht habe.
Gruß, Don P
Hallo,
warum sollte man window.onload nicht undefined zuweisen? Zu diesem Zeitpunkt ist das anonyme Objekt ja bereits erzeugt und die Eventhandler sind an ihre DOM-Objekte gebunden
Die DOM-Objekte sind erst onload verfügbar, also muss init ein onload-Handler sein, also muss man window.onload = init notieren, auf die eine oder andere Weise.
Mit anderen Worten, der Code bedeutet nur: Wenn das Fenster fertig geladen ist (onload), soll eine Funktion ausgeführt werden
Ja, eben.
und zwar die init()-Methode des gerade erzeugten Objekts, wobei deren Rückgabewert (undefined) dann der window.onload-Handler wird.
Nein.
window.onload = expression;
expression sollte sinnigerweise ein Funktionsobjekt zurückgeben.
Du notierst (letztlich, nachdem der Ausdruck aufgelöst wurde):
window.onload = funktion();
Wenn funktion keinen Rückgabewert hat, kann man sich das sparen und gleich funktion() schreiben. Die Funktion wird sofort aufgerufen.
Habe fast eine Woche daran herumgedoktert und ausgiebig getestet, bis es soweit war.
<head>
<script type="text/javascript">
[code lang=javascript]window.onload = {
init : function () {
document.getElementById("a").onclick = this.handler;
},
handler : function () {
alert("geklickt");
}
}.init();
</script>
</head>
<body>
<p id="a">Klick mich</p>
</body>
</html>[/code]
document.getElementById("a") has no properties
Mathias
Nochmal auf dein Beispiel angepasst:
window.onload = {
elem : document.getElementById("a"),
init : function () {
alert(this.elem);
}
}.init();
Deshalb wirst du dort this.elem auch keinen Event-Handler zuweisen können.
Mathias
Hallo,
Nochmal auf dein Beispiel angepasst:
window.onload = {
elem : document.getElementById("a"),
init : function () {
alert(this.elem);
}
}.init();
Nein! Das habe ich so auch nie behauptet. Es ist zum Haare raufen... Du hast meinen Code gar nicht richtig gelesen. Vor allem die Stelle nicht, die mit "! Wichtig" kommentiert ist. Und ausprobiert hast du ihn auch nicht, stimmt's?
Meine onload-Funktion lässt sich im <head> laden, bevor eines der DOM-Elemente existiert. Und alle Handler funktionieren!
Gruß, Don P
Hallo Mathias,
Nochmal auf dein Beispiel angepasst [...]
Mein Beispiel war zwar ein bisschen anders, aber nachdem man mir jetzt nachgewiesen hat, dass es so doch nicht klappt, ist mir die Sache ziemlich peinlich.
Nach euren Erklärungen wurde es für mich auch immer unlogischer, dass es überhaupt geht, aber wenn man's doch mehrfach probiert hat und alles ging super...
Ich glaube inzwischen fast, dass ich bei den letzten Tests vielleicht vergessen habe, den .js-Dateinamen im <script src=...>-tag anzupassen. Immer wenn ich etwas neues probiere nach einer bereits funktionierenden Version, speichere ich unter einem neuen Namen, um die "gute" Version nicht zu verlieren. Kein Wunder also, wenn alles vermeintlich funktioniert, wenn die neueste Version gar nicht wirklich geladen wird... peinlich, peinlich!
Morgen (bzw. heute) werde ich es dann definitiv wissen (kann gerade nicht darauf zugreifen).
Danke jedenfalls für deine Geduld.
Gruß, Don P
Hi,
window.onload = { ... }.init();
>
> > Du meintest wohl eher `window.onload = { ... }.init;`{:.language-javascript}, sonst ist die Zuweisung unsinnig.
>
> Eigentlich meine es schon genau so wie es dasteht, denn das gibt keinen Sytaxfehler, obwohl die init()-Funktion undefined zurückgibt.
Natuerlich gibt das keinen Fehler - \*Sinn\* ergibt es aber auch keinen.
> Ok, ein bisschen unsinnig scheint die Zuweisung zu sein, aber warum sollte man window.onload nicht undefined zuweisen?
Warum sollte man?
Wenn man nicht auf das Eintreten des Events window.onload warten will, dann braucht man diesem Eventhandler auch nichts zuweisen.
> Zu diesem Zeitpunkt ist das anonyme Objekt ja bereits erzeugt und die Eventhandler sind an ihre DOM-Objekte gebunden (dank init()),
Da betreibst du reines Gluecksspiel - denn du greifst in init auf HTML-Objekte ueber getElementById zu. Die muessen aber (je nachdem, wo du das Script platzierst) noch gar nicht vorhanden sein.
> Mit anderen Worten, der Code bedeutet nur: Wenn das Fenster fertig geladen ist (onload), soll eine Funktion ausgeführt werden,
Das bedeutet er in der Form, die Mathias dir gezeigt hat.
In deiner Form bedeutet er: Fuehre init \*sofort\* zu dem Zeitpunkt, wo du auf diese Anweisung triffst, aus, kuemmere dich dabei einen Furz darum, ob die HTML-Objekte, auf die zugegriffen werden soll, bereits vorhanden sind - und weise dann noch window.onload einen vollkommen ueberfluessigen Wert zu.
> und zwar die init()-Methode des gerade erzeugten Objekts, wobei deren Rückgabewert (undefined) dann der window.onload-Handler wird.
Das ist, wie gesagt, absolut sinnfrei.
> Den brauchen wir wie gesagt nachher auch nicht mehr.
Wenn du auf onload in keinster Weise reagieren willst, dann brauchst du dem Handler auch nichts zuweisen - deine "onload ist mir furzegal"-Attituede hat der Browser auch von selbst, wenn du onload nichts zuweist.
> Das verstehe ich jetzt nicht ganz, habe es auch nicht überprüft, meinen Code dagegen schon, und der ergibt definitiv keinen Syntax- und keinen sonstigen Fehler und funktioniert genau wie gewollt, auch mit weit umfangreicheren Objekten und Eventhandlern.
Nur, wenn es wirklich von dir gewollt ist, dass mit der Ausfuehrung \*nicht\* gewartet wird, bis das Dokument (und eingebundene Ressourcen) "fertig geladen" ist.
> > Das völlige Verstecken macht natürlich solange Sinn, wie man ein Script mit einem onload-Handler hat.
>
> Was spricht dagegen, immer einen zu verwenden?
Was spricht \*dafuer\*, eine Nicht-Aktion fuer Ereignis XY zuzuweisen, wenn man am Eintreten von Ereignis XY ueberhaupt kein Interesse zu zeigen beabsichtigt, und auch nichts davon abhaengig ausfuehren lassen moechte?
> Was meinen Vorschlag hier angeht, dürfte es natürlich auch nicht einfach sein (oder gar unmöglich?), den onload-event dynamisch zu erweitern...
Es waere ja nicht mal eine "Erweiterung", weil du bisher noch absolut nichts definiert hast, was bei onload geschehen sollte.
Dir scheint der Unterschied zwischen
window.onload = funktionsreferenz;
und
window.onload = funktionsaufruf();
noch nicht klar zu sein.
Die Klammern sind es, die hier den Unterschied machen, und zwar einen gewaltigen.
MfG ChrisB
Hallo,
Da betreibst du reines Gluecksspiel - denn du greifst in init auf HTML-Objekte ueber getElementById zu. Die muessen aber (je nachdem, wo du das Script platzierst) noch gar nicht vorhanden sein.
Wow, das wäre allerdings ein Totschlagargument, wenn es denn stimmte. Tatsächlich lade ich das Script im <head> mit src=, und natürlich sind die DOM-Elemente zu diesem Zeitpunkt noch nicht vorhanden. Die kommen ja erst im <body> vor. Trotzdem funktioniert es einwandfrei. Wie erklärst du dir das?
Zugegeben, für Tests habe ich nicht gerade besonders viel im <body>, nur 5 inputs (text, buttons und select) sowie drei <div> und eine Tabelle mit 39 Zellen (alles auf meien Art mit onclick-, onkeydown- usw. Handlern versehen).
Aber meines Wissens ist der Umfang des <body> doch auch völlig unwesentlich, weil immer der Reihe nach abgearbeitet wird, nach deiner Aussage also keiner der Handler funktionieren dürfte. Sie funktionieren aber alle.
Die Klammern sind es, die hier den Unterschied machen, und zwar einen gewaltigen.
Ja, sie stören die Übersicht, wenn man allzu verschwenderisch damit umgeht. Das ist gerade das "Schöne" an meinem Code: Es steht nur das Nötigste da, und es funktioniert.
Gruß, Don P
Wow, das wäre allerdings ein Totschlagargument, wenn es denn stimmte. Tatsächlich lade ich das Script im <head> mit src=, und natürlich sind die DOM-Elemente zu diesem Zeitpunkt noch nicht vorhanden. Die kommen ja erst im <body> vor. Trotzdem funktioniert es einwandfrei. Wie erklärst du dir das?
Nein, dein Code funktioniert nicht.
Er ist auch falsch. Du weist hier dem Handler nur eine Objektreferenz zu, damit passiert aber nicht das was du erwartest.
Struppi.
Gibt's da vielleicht irgendwelche Nachteile, die mir entgangen wären?
Ja, es funktioniert so nicht http://selfhtml.jstruebig.de/Parameter bei Event-handler.html
Struppi.
Hallo,
Gibt's da vielleicht irgendwelche Nachteile, die mir entgangen wären?
Ja, es funktioniert so nicht http://selfhtml.jstruebig.de/Parameter bei Event-handler.html
Jetzt bin ich platt. Heute Nachmittag hat alles bestens funktioniert. Es war nicht diese einfache, zusammengeschrumpfte Version, die ich hier gepostet habe, aber ansonsten genau so. Habe lediglich ein paar Handler und andere Objekte und Funktionen herausgenommen, um hier ein einfaches Beispiel zu bringen.
Ok, sorry für den Wind, den ich gemacht habe, mea culpa!
Werde morgen die andere (funktionierende) Version nochmal testen und wenn es sein muss, das ganze Ding (ca. 200 Zeilen) hier posten (auf das Zeug kann ich jetzt gerade nicht zugreifen).
Gruß, Don P
Hallo,
Also jetzt muss ich doch ein ganzes Stück zurückrudern:
Hatte tatsächlich den .js-Dateinamen im <script src=...>-tag nicht angepasst, und nach einigen Tests irrtümlich angenommen, dass der code window.onload = {...}.init();
funktionieren würde. Tut er aber nicht :-(
Was sicher funktioniert, ist meine ursprünglich gepostete Version
window.onload = function () {
var wrapper = { .... };
wrapper.init();
};
Und – das könnt ihr mir hoffentlich bestätigen – auch folgende Variante funktioniert:
window.onload = function () { ({...}).init(); };
Ist ja auch logisch, weil onload jetzt eine richtige Funktion ist.
Es kommt auch hier erwartungsgemäß zu einem Fehler, wenn man gleich danach (noch im <head> des Dokuments) z.B. ~~~javascript window.onload();
Was m.M.n tatsächlich funktionieren könnte, wäre gemäß Mathias:
> `window.onload = ({ ... }).init;`{:.language-javascript}
Aber das klappt nicht (Fehler: DOM-Element hat keine Eigenschaften). Kann mir jemand sagen, warum nicht?
Der von Mathias genannte Code
> `(function () { var obj = {... }; addLoadEvent(obj.init); })();`{:.language-javascript}
sieht auch interessant aus. Ich werde mich aber vorerst mit meinem
`window.onload = function () { ({...}).init(); };`{:.language-javascript}
begnügen, und die addLoadEvent-Chose erst mal außen vor lassen, bis sich eine Notwendigkeit dafür ergibt.
Mancher fragt sich jetzt vielleicht, warum ich überhaupt so viel Aufhebens darum mache. Naja, ich will halt javascript richtig verstehen, und möglichst einfachen und übersichtlichen Code produzieren. Jede Klammer oder Variable, auf die man eigentlich verzichten könnte, ist mir dann auch zuviel. Dabei ist es natürlich auch Geschmacksache, ob ein Salat von Klammern oder gar "komischer Zeichen" à la Perl wirklich einfacher und übersichtlicher ist, als wenn man die eine oder andere verzichtbare Variable einführt.
Gruß, Don P
Was sicher funktioniert, ist meine ursprünglich gepostete Version
window.onload = function () {
var wrapper = { .... };
wrapper.init();
};
Das ist ok, onload bekommt eine Funktionsreferenz.
> Und – das könnt ihr mir hoffentlich bestätigen – auch folgende Variante funktioniert:
>
> `window.onload = function () { ({...}).init(); };`{:.language-javascript}
>
> Ist ja auch logisch, weil onload jetzt eine richtige Funktion ist.
Nein das funktioniert nicht, neben der falschen Klammerung, weist du onload keine Funktionsreferenz zu, sondern den Rückgabewert der Funktion init()
> Es kommt auch hier erwartungsgemäß zu einem Fehler, wenn man gleich danach » Was m.M.n tatsächlich funktionieren könnte, wäre gemäß Mathias:
> > `window.onload = ({ ... }).init;`{:.language-javascript}
> Aber das klappt nicht (Fehler: DOM-Element hat keine Eigenschaften). Kann mir jemand sagen, warum nicht?
Weil du beim erzeugen des Objektes mit { ...} bereits die DOM Funktionen aufrufst, du hängst zu sehr an dem onload, was du du genau machst ist folgendes:
var objekt = {
ini: function() { .... },
inputField: document.getElementById("InputField"),
actionButton: document.getElementById("ActionButton")
};
window.onload = objekt.init;
ist im Prinzip richtig, nur das document.getElement.. zu früh auferufen wird.
> Der von Mathias genannte Code
>
> > `(function () { var obj = {... }; addLoadEvent(obj.init); })();`{:.language-javascript}
>
> sieht auch interessant aus.
Hier geht es nur um die Kapselung von obj, deshalb wird eine anonyme Funktion drumherum gebaut, die aber sofort aufgerufen wird.
> `window.onload = function () { ({...}).init(); };`{:.language-javascript}
Das ist falsch und funktioniert nicht zumindest nicht so wie du denkst.
>
> begnügen, und die addLoadEvent-Chose erst mal außen vor lassen, bis sich eine Notwendigkeit dafür ergibt.
>
> Mancher fragt sich jetzt vielleicht, warum ich überhaupt so viel Aufhebens darum mache. Naja, ich will halt javascript richtig verstehen, und möglichst einfachen und übersichtlichen Code produzieren. Jede Klammer oder Variable, auf die man eigentlich verzichten könnte, ist mir dann auch zuviel.
Du benutzt zuviele Klammern, d.h. dein Code funktioniert nicht, die Beispiele von Mathias sind richtig und funktionieren und enthalten mit Sicherheit keine überflüssigen Klammern. Die Frage ist nur wie unübersichtlich es man sich selber macht. Wenn ich einen Event mit einer komplexen Funktion aufrufen will, bau ich mir eine entsprechende Referenz und weise sie dem Handler getrennt zu, finde ich manchmal übersichtlicher
~~~javascript
var myHandler = function() {
/* tu viele Dinge, wenn die Funktion aufgerufen wird * /
};
object.onevent = myHandler;
Das Problem mit der gewünschten Kapselung erreichst du aber nicht anders.
(function() {
/* Diese Funktion wird sofort aufgerufen */
})()
und hier kannst du keine Klammern weglassen.
Struppi.
Hallo,
Hallo Struppi,
Und – das könnt ihr mir hoffentlich bestätigen – auch folgende Variante funktioniert:
window.onload = function () { ({...}).init(); };
Ist ja auch logisch, weil onload jetzt eine richtige Funktion ist.
Nein das funktioniert nicht, neben der falschen Klammerung, weist du onload keine Funktionsreferenz zu, sondern den Rückgabewert der Funktion init()
Ähem... kann es sein dass du dich jetzt irrst? Aufgedröselt ist es doch so:
window.onload = function () // => eine Funktionsreferenz wird zugewiesen,
{
({...}).init(); // => im Funktionskörper wird eine Funktion ausgeführt, die keinen Rückgabewert hat,
}; // => das war's. Es wird auch nichts zurückgegeben.
Keine der Funktionen gibt also etwas zurück, aber die Zuweisung ist eine Funktionsreferenz.
Das habe ich nun wirklich getestet, überzeuge dich selbst:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head><title>onload-Test</title>
<script type="text/javascript">
[code lang=javascript]
window.onerror = function(msg, file, line) {alert( msg + '\n' + line);}
window.onload = function () {
({
inputField: document.getElementById("InputField"),
actionButton: document.getElementById("ActionButton"),
init: function () {
var that = this;
this.actionButton.onclick = function () {that.action()};
},
action: function () {
if (this.inputField.value) {
alert(this.inputField.value);
this.inputField.value = "";
} else {
this.inputField.value = "Gib mir Text..."
this.inputField.focus();
}
}
}).init();
};
</script>
</head>
<body>
<div id="Ich_existiere_nur_feur_den_Validator">
<input id="InputField">
<input id="ActionButton" type="button" value="OK">
</div>
</body>
</html>[/code]
Ich widerspreche inzwischen nur noch sehr ungern, aber das ist doch wirklich der Beweis.
Du benutzt zuviele Klammern
Wo genau?
window.onload = function () { ({...}).init(); };
------------------------------^-----^-----^^
von diesen kann man keine weglassen (s.u.), die übrigen braucht es natürlich wie für jede Funktion und die geschweiften inneren für das Objekt-Literal.
var myHandler = function() {
/* tu viele Dinge, wenn die Funktion aufgerufen wird * /
};object.onevent = myHandler;
Das ist im Prinzip klar, aber wenn myHandler eine Methode irgend eines Objekts ist, dann hat sie, wenn der Event eintritt nicht ohne weiteres Zugriff auf die andere Eigenschaften und Methoden des Objekts, dessen Methode sie ist (this in myHandler ist ja üblicherweise das Objekt, das den Event ausgelöst hat, soviel ich weiß). Deshalb will ich über window.onload (also erst wenn alle DOM-Elemente existieren) sämtliche Eventhandler an ihre DOM-Elemente binden und zwar so, dass die Handler immer Zugriff auf die Eigenschaften und Methoden des Objekts haben (quasi an Stelle von Parametern), in dem die Handler definiert sind.
> Das Problem mit der gewünschten Kapselung erreichst du aber nicht anders.
>
> ~~~javascript
(function() {
> /* Diese Funktion wird sofort aufgerufen */
> })()
Das ist doch fast genau das, was ich oben mache, nur dass ich nicht "Diese Funktion" sofort aufrufe, sondern die Methode init "Dieses Objekts".
(
{
/* Die Methode init dieses Objekts wird sofort aufgerufen */
}
).init()
und hier kannst du keine Klammern weglassen.
Stimmt.
Gruß, Don P
Hi,
Und – das könnt ihr mir hoffentlich bestätigen – auch folgende Variante funktioniert:
window.onload = function () { ({...}).init(); };
Ist ja auch logisch, weil onload jetzt eine richtige Funktion ist.
Nein das funktioniert nicht, neben der falschen Klammerung, weist du onload keine Funktionsreferenz zu, sondern den Rückgabewert der Funktion init()
Ähem... kann es sein dass du dich jetzt irrst?
Ja, sieht so aus.
Aufgedröselt ist es doch so:
window.onload = function () // => eine Funktionsreferenz wird zugewiesen,
{
({...}).init(); // => im Funktionskörper wird eine Funktion ausgeführt, die keinen Rückgabewert hat,
}; // => das war's. Es wird auch nichts zurückgegeben.
>
> Keine der Funktionen gibt also etwas zurück, aber die Zuweisung ist eine Funktionsreferenz.
Ja, stimmt soweit (ich das sehe).
Wenn du es allerdings mit deinem Code jetzt schon schaffst, jemanden wie Struppi zu verwirren, der ebenso wie molily JavaScript schon zum Fruehstueck liest (und schreibt), wie andere ihre Tageszeitung - dann wuerde ich an deiner Stelle noch mal ueberdenken, ob du dein Ziel, den Code so verstaendlich und uebersichtlich wie moeglich zu halten, in dem du ihn jeder "ueberfluessigen" Klammer beraubst und auch sonst einiges an "fortgeschrittenen" Techniken zusammenmixt, wirklich erreichst ...
Nichts gegen ein "raffiniertes" Stueckchen Code hier und da, manches laesst sich wirklich kuerzer und effizienter notieren als in den "gaengigen" Formen - aber wenn man den eigenen Code vor lauter Raffinesse, gekuerztem "Ueberfluessigem" und Verschachtelung diverser Spezialnotationen in ein paar Wochen selber nicht mehr versteht (oder aber fuenf Zeilen Code mit 50 Zeilen Kommentierung versehen muss, um es doch noch zu tun), dann hat man nur wenig gewonnen.
Versuche da also eine ausgewogene Balance (weisser Schimmel, I know ...) zu finden - und nicht auf Teufel komm' raus von "Bewaehrtem" abzuweichen.
MfG ChrisB
Hallo,
Ja, sieht so aus.
[...]
Ja, stimmt soweit (ich das sehe).
Uff, da fällt mir aber ein Stein vom Herzen. So bin ich wieder halbwegs rehabilitiert bzw. kann wieder in den Spiegel sehen :-)
Wenn du es allerdings mit deinem Code jetzt schon schaffst, jemanden wie Struppi zu verwirren, der ebenso wie molily JavaScript schon zum Fruehstueck liest (und schreibt), wie andere ihre Tageszeitung -
Ich bin halt ein Genie ;-) Crockford würde vor Neid erblassen, wenn er den Code sähe... *wegduck*
dann wuerde ich an deiner Stelle noch mal ueberdenken, ob du dein Ziel, den Code so verstaendlich und uebersichtlich wie moeglich zu halten, in dem du ihn jeder "ueberfluessigen" Klammer beraubst und auch sonst einiges an "fortgeschrittenen" Techniken zusammenmixt, wirklich erreichst ...
Sicher überdenke ich das. Aber vielleicht reicht es ja manchmal, wenn *ich* meinen Code verstehe. Es wird ja oft genug nach Verschlüsselungstechniken und Ähnlichem gefragt. Manchmal ist es vielleicht ganz gut, wenn nicht jeder gleich dahinter steigt.
Nichts gegen ein "raffiniertes" Stueckchen Code hier und da, manches laesst sich wirklich kuerzer und effizienter notieren als in den "gaengigen" Formen - aber wenn man den eigenen Code vor lauter Raffinesse, gekuerztem "Ueberfluessigem" und Verschachtelung diverser Spezialnotationen in ein paar Wochen selber nicht mehr versteht (oder aber fuenf Zeilen Code mit 50 Zeilen Kommentierung versehen muss, um es doch noch zu tun), dann hat man nur wenig gewonnen.
Das ist sicher richtig. Bei mir ist es halt so, dass ich die "gängigen" Formen fast nicht kenne und mir das alles mehr oder weniger autodidaktisch, auch mit eurer Hilfe natürlich, aneigne. Ein Code, der "nur" funktioniert, den ich aber nicht wirklich durchschaue, nützt mir eher wenig, wie gängig er auch immer sein mag. Durch viel Grübeln und ausprobieren komme ich dann manchmal auf Sachen, die auf den ersten Blick ungewöhnlich oder gar falsch aussehen, aber dafür habe ich sie selber erarbeitet, und wenn sie knapp und effizient oder von mir aus auch raffiniert sind, umso besser.
Versuche da also eine ausgewogene Balance (weisser Schimmel, I know ...) zu finden - und nicht auf Teufel komm' raus von "Bewaehrtem" abzuweichen.
Meistens komme ich dann auf Umwegen doch wieder beim Bewährten an. Wie könnte es auch anders sein. Aber dann habe ich wenigstens ein tieferes Verständnis dafür, warum es sich bewährt hat.
Gruß, Don P
Ähem... kann es sein dass du dich jetzt irrst? Aufgedröselt ist es doch so:
Seltsam aber wahr ;-)
Ich meine mich zu erinnern, das mir Notepad++ falsche Klammmerpaare angezeigt hat, aber vielleicht hatte ich da schon am Code rumkopiert.
Ich widerspreche inzwischen nur noch sehr ungern, aber das ist doch wirklich der Beweis.
JaJa, du hast recht.
var myHandler = function() {
/* tu viele Dinge, wenn die Funktion aufgerufen wird * /
};object.onevent = myHandler;
>
> Das ist im Prinzip klar, aber wenn myHandler eine Methode irgend eines Objekts ist, dann hat sie, wenn der Event eintritt nicht ohne weiteres Zugriff auf die andere Eigenschaften und Methoden des Objekts, dessen Methode sie ist (this in myHandler ist ja üblicherweise das Objekt, das den Event ausgelöst hat, soviel ich weiß).
Du meinst sowas:
~~~javascript
var myHandler = {
attribut_1: 'attribut_1',
attribut_2: 'attribut_2',
methode: function() {
alert(this.attribut_1);
}
};
window.onload = myHandler.methode;
Das ist so auch nicht wirklich lösbar. Deshalb musst du um den Event eine Wrapper Funktion bauen.
window.onload = function() { myHandler.methode();};
Deshalb will ich über window.onload (also erst wenn alle DOM-Elemente existieren) sämtliche Eventhandler an ihre DOM-Elemente binden und zwar so, dass die Handler immer Zugriff auf die Eigenschaften und Methoden des Objekts haben (quasi an Stelle von Parametern), in dem die Handler definiert sind.
Das ist genau der Weg, das Problem an deinem ursprünglichen Code war, das du erst ein Objekt erzeugt hast. Und beim erzeugen schon versucht hast auf das unfertige DOM zu zugreifen. Das geht nicht.
Das Problem mit der gewünschten Kapselung erreichst du aber nicht anders.
(function() {
/* Diese Funktion wird sofort aufgerufen */
})()
>
> Das ist doch fast genau das, was ich oben mache, nur dass ich nicht "Diese Funktion" sofort aufrufe, sondern die Methode init "Dieses Objekts".
Stimmt, das hatte ich übersehen, da du Versionen hattest wo es nicht so war.
Struppi.