dWing — die Welt ist nicht gerecht

sign in

Warum ich Prototype.js hasse

... und jedem Webentwickler davon abrate es jemals zu benutzen.

Der Grund ist einfach: Prototype manipuliert fröhlich an internen JS Objekten herum und zerstört dadurch essentielle Funktionalität.

Ein Beispiel, dass mir bereits seit langem bekannt ist: Prototype zerstört Javascripts for ... in. Ein Beispiel um es zu verdeutlichen:

Mit normalem JS:
>>> a = [4,5]; for(var i in a) { console.log(i, a[i]); }
0 4
1 5
Mit Prototype:
>>> a = [4,5]; for(var i in a) { console.log(i, a[i]); }
0 4
1 5
each function()
eachSlice function()
all function()
any function()
[...]
uniq function()
clone function()
toJSON function()

Prototype fügt also eine Unmenge von Funktionen an ein Array an, welche bei einem for ... in auch durchlaufen werden. Dies ist ärgerlich, aber mit einem klassischen for i < .length geht es noch immer.


Etwas viel schlimmeres ist mir heute aufgefallen: Prototype zerstört die Funktionalität von JSON.stringify(), welches entweder nativ in Firefox 3.5 implementiert ist oder über die json2.js für ältere Browser zur Verfügung gestellt wird. Auch hier ein Codebeispiel:

Normales JS:
>>> JSON.stringify([1,"a"])
"[1,"a"]"
>>> JSON.parse(JSON.stringify([1,"a"]))
[1, "a"]
Und mit Prototype:
>>> JSON.stringify([1,"a"])
""[1, \"a\"]""
>>> JSON.parse(JSON.stringify([1,"a"]))
"[1, "a"]"

Wie es scheint wird wegen Prototype stringify() zwei mal ausgeführt. Ein weiteres parse() liefert dann den eigentlich gewünschten JSON String. Dieses Verhalten ist absolut inakzeptabel!


Aber was sollte man als Alternative verwenden falls man eine Browser unabhängige JS Bibliothek braucht? Ich rate zu jQuery. Bei jQuery wird sehr darauf geachtet eine klare Trennung zwischen jQuery und nativen Objekten zu gewährleisten. Es pfuscht nicht an nativen Arrays oder JSON herum.

Diese strikte Trennung kann aber zu einigen Unklarheiten führen, wenn man nicht genau weiß ob man nun ein jQuery Array oder ein natives Array in der Hand hat. Ein solches Problem hatte ich kürzlich mit filter(), da sich die native Implementierung deutlich von der in jQuery unterscheidet. Auch hier ein Codebeispiel:

Mit normalen JS:
>>> [1,'a'].filter(function(aArg) {console.log(aArg);});
1
a
Mit jQuery:
>>> $([1,'a']).filter(function(aArg) {console.log(aArg);});
0
1

Wie man sieht übergibt die native Funktion als ersten Parameter das Objekt selbst, während in jQuery nur der Index übergeben wird. Es ist somit darauf zu achten ob man nun ein echtes Array oder ein jQuery Array vor sich hat, in diesem Fall wird aus einem echten Array per $(array) ein jQuery Array. Das echte Array bleibt in jedem Fall unangetastet.


Was mir Heute auch noch unangenehm aufgefallen ist an Prototype: Es kapselt EventListener nicht gut genug, vor allem im Bezug auf Events abbrechen. Im normalen DOM bricht man der event.preventDefault() ein Event ab. Fängt man ein Formular vor dem Abschicken ab, so wird es nach dem Funktionsaufruf nicht abgeschickt.

Leider unterstützt der IE keine echten EventListener und es gibt diese Methode gar nicht. Stattdessen muss die EventHandler Methode einfach false zurückliefern.

In Prototype muss man beide Methoden verwenden um das gewünschte Ergebnis zu erzielen. jQuery hingegen sorgt selbst dafür, dass bei einem return false; das Event nicht weiter ausgeführt wird.


Das war es vorerst mit meiner Abneigung gegen Prototype, vielleicht kommt noch etwas dazu falls ich noch einmal kostbare Arbeitszeit wegen solchen Bugs verliere. Ich werde mir auch ernsthaft überlegen vielleicht das gesamte Projekt an dem ich zur Zeit arbeiten muss von Prototype auf jQuery umzustellen.

Fehler 401 oder doch lieber 403?

Ich finde es sowieso komisch das Opera die cursor: pointer; Regel nicht anerkennt und den Mauszeiger nicht ändert wenn man über die Bewertungsanzeige geht. Als der Opera dann auch nicht mit einer Du bist nicht angemeldet Fehlermeldung reagiert hat auf den Versuch etwas zu bewerten habe ich gedacht das er ein größeres Problem mit dem CSS der Bewertungsanzeige hat.

Es lag aber man Javascript Teil der Seite. Denn wenn ein Benutzer nicht angemeldet ist schicke ich einen 401 Unauthorized Fehler Code zurück. Wikipedia hilft wie immer weiter wenn es um eine Übersicht der HTTP Codes geht. Ich sende also einen 401 Fehler. Laut Beschreibung ist das auch OK, denn eine Anmeldung kann noch erfolgen. Allerdings ist in diesem Kontext nur von HTTP Authentifizierung die Rede, und diese verträgt sich mit OpenID bisher noch nicht. In der Beschreibung des 403 Forbidden Fehlers steht, das bei dieser Art von Fehler eine Anmeldung keinen Unterschied machen würde. Das ist in meinem Fall nicht der Fall. Denn meldet sich der Benutzer an, wird kein Fehler mehr gesendet sondern die Aktion wird ausgeführt.

Allerdings trifft hier wieder die Einschränkung, dass dies alles nur auf HTTP Authentifizierung zutrifft. Für diese sendet der Server normalerweise einen WWW-Authenticate Header mit. Bei mir ist das nicht der Fall.

Nunja, auf jeden Fall denkt sich Opera es könnte einfach meinen gesendeten Status Code überschreiben und statt einem 401 ein 403 an das Script senden, was in diesem Fall ein JavaScript alert() öffnet um den Benutzer darüber zu informieren, dass dieser sich anmelden soll.

Den Fehler zu beheben ist einfach. Dennoch sollte Opera nicht einfach die Fehlercodes ändern wenn mit XmlHttpRequest eine Seite geöffnet wird.

Form submission und EventListener

Ich bin ja interessiert an richtigem dem Standard entsprechendem Javascript. Demzufolge bin ich auch Verfechter der Meinung das man in Javascript Ereignisse mit EventHandlern behandelt, die man über element.addEventListener(eventType, eventHandler, bubbling) hinzufügt. Durch diese Methode hat man Zugriff auf die Ereigniseigenschaften und man hat auch die Möglichkeit mehrere EventHandler anzufügen und diese der Reihe nach abzuarbeiten, bzw die weitere Abarbeitung durch Ereignismethoden zu unterbinden. So Zeugs wie element.onEvent = eventHandler; oder sogar onclick="javascript: eventHandler();" lehne ich grundsätzlich ab. Ich halte es einfach für unsauberen Stil, und in der DOM Spezifikation ist so etwas auch nicht enthalten. Der richtige Weg ist es addEventListener zu benutzen.

Jetzt habe ich allerdings über zwei Stunden damit verbracht den FCKeditor in meine Testversion von dWing einzubinden. Ich habe mir also einen EventListener für das submit Ereignis des Formulars registriert um das Formular im Hintergrund per XHR zu übertragen. Hat auch super funktioniert solange bis der FCKeditor nicht eingebaut war. Außerdem wollte ich den speichern Button des FCKeditors benutzen um mich nicht selbst um einen submit Button kümmern zu müssen. Nur leider ging danach nichts mehr.

Ich dachte zuerst der FCKeditor wäre an allem schuld denn er spielt selbst mit dem submit Event umeinander um vor dem Abschicken des Formulars die Eingaben des FCKeditors in eine versteckte textarea zu kopieren. An sich sehr gut. Somit kann ich direkt den Inhalt des textarea verarbeiten ohne direkt auf FCKeditor Methoden zugreifen zu müssen.

Es funktionierte aber nicht. Wie sich aber heraus gestellt hat war nicht der FCKeditor schuld sondern Firefox. Mit einem Klick auf den speichern Button im FCKeditor ruft dieser die form.submit() Methode auf. An sich müsste das ja kein Problem sein. Ein submit Ereignis wird ausgelöst und es erscheint logisch das jeder EventHandler darüber informiert wird. Dank eines Bugs in Mozilla ist dies aber nicht der Fall.

Noch schlimmer ist, dass die Leute bei Mozilla den Fehler gar nicht beseitigen wollen. Gründe dafür sind, dass sie die Abwärtskompatibilität zu 8 Jahre alten Browsern nicht stören wollen und weil jemand der über Script die form.submit() Methode aufruft auch gleich den EventHandler aufrufen könnte. Na wunderbar.

Ich bin mal wieder sehr angepisst das ich über zwei Stunden einem Fehler nachgejagt bin den niemand ausbessern will. Ich hoffe mal dass z.b. die WHAT Arbeitsgruppe etwas dagegen unternimmt und ein einheitliches Verhalten festlegt. Dann sollte dieses Verhalten noch in einem möglichen Acid4 Test überprüft werden und vielleicht kann ich dann in 3 Jahren die ganzen Hacks aus meinem Javascript Code rauslöschen.

Wieder da

Vor einigen Wochen habe ich meinen Desktop Rechner komplett formatiert und dort nur Ubuntu, ohne Windows, installiert. Weil ich den Rechner sowieso kaum mehr eingeschaltet hatte, weil ich eher mit meinem Laptop online war, und weil ich zu faul war auf diesem einen Webserver zu installieren, war dWing lange Zeit nicht erreichbar.

Aber damit ist jetzt Schluss. Ich habe mich dazu entschieden einen vServer zu mieten. Und jetzt habe ich ihn erfolgreich eingerichtet. :) Ich werde noch ein wenig rumspielen mit dem vServer und hoffentlich auch mal wieder öfter bloggen.

Es ist auch an dWing noch sehr viel zu machen. Auch in Bezug auf Serverunterstützung. Ich hoffe das ich ein wenig mehr Motivation finde daran zu arbeiten. Es wäre auch an der Zeit mit dem neuen Design weiter zu machen. Außerdem habe ich einige Ideen bezüglich REST, ein neues Modewort was mehr oder weniger das selbe ist wie CRUD (create, read, update, delete), nur das CRUD eine Abstraktion ist zwischen Objekten in der Applikation und Datensätzen in einer Datenbank. REST ist eher eine Schicht zwischen der Client Applikation (ECMAScript) und dem Server, aber es werden genau die selben Methoden benutzt. create = POST, read = GET, update = PUT, delete = DELETE. Ach hätte ich doch bloß Motivation direkt daran zu arbeiten.

S6

Ich habe heute endlich meine Englisch Präsentation hinter mir, die ich mit S5, dem simplen, standardbasierten Slideshowsystem gemacht habe. Im Prinzip ist S5 super, es ist als HTML Kenner gut zu benutzen und sicherlich tausend mal einfacher als Powerpoint oder Impress oder wie auch immer das Präsentationsprogramm von OpenOffice heißt.
Aber es sind mir auch einige Probleme aufgefallen: S5 heißt zwar standardbasiert aber ist es nicht zu 100%. Sobald ich nämlich application/xhtml+xml benutze funktioniert gar nichts mehr mit S5. Und es hat auch einige Probleme mit der Skalierung. Es passt zwar die Schriftgröße je nach Fenstergröße an, es kann aber trotzdem vorkommen, dass wenn zu viele Punkte auf einer Folie sind, diese über den unteren Rand drübergeht und einige Punkte somit nicht mehr sichtbar sind. Ganz andere Probleme hat es mit Bildern. Diese werden gar nicht skaliert.
Also habe ich mir spontan überlegt: Wie wäre es mit einem S6? Ein simples, skalierbares und wirklich standardbasiertes Sliseshowsystem? Skalierbar würde noch einen Schritt weiter gehen, und zwar das SVG für das gesamte UI verwendet werden würde. Und jeder liebt eyecandy, also müsste es auch noch weiche Übergänge oder sonstige Animationen bieten.
Es wäre sicherlich eine interessante Arbeit soetwas zum Leben zu erwecken, aber schon jetzt so auf Anhieb wüsste ich nicht wie ich manche Dinge davon lösen sollte. Schon alleine wie ich ein halb-skalierbares SVG mache bei dem sich einige Teile wie in HTML anpassen, andere aber unabhängig vom Seitenverhältnis aber dennoch Abhängig von der Gesamtauflösung sind. Ich hoffe man kann mir dabei folgen.
Mal sehen ob ich genug Motivation aufbringen kann in diese Richtung etwas zu unternehmen.

older posts