Tooltips.. die kleinen Text-Popups die einem helfen sich zu orientieren. Immer wieder gerne gesehen und wurde auch schon zig-mal programmiert.

Ich hab mir schon so einige fertige Scripts angesehen, aber die meisten machen einfach viel zu viel und nutzen teilweise sogar selber Prototype, nur leider nicht effizient genug.

Meistens werden die DIVs beim Aufruf der Seite generiert und irgendwo unterhalb des BODYs "versteckt". Bei sehr vielen Tooltips/Links kann das den Seitenaufbau extrem verlangsamen. Andere wiederum berechnen gott-weiss-was und haben einen mehrseitigen Umfang. Da oft gleich auch eine Klasse geschrieben wird muss man meist eine weitere Scriptdatei laden, was nochmals den Aufbau verzögert.

Ich schreibe daher meine Tooltips immer selber und bin mittlerweile runter auf 10 Zeilen. Könnte sogar in einer passen, aber man will ja nicht den Überblick verlieren.

JAVASCRIPT:
  1. $$('a[title]').invoke('observe', 'mouseover', function(evt){
  2.     var element = evt.findElement('a'), text = element.getAttribute('title');
  3.     var tooltip = new Element('div', {'class':'tooltip'}).update(text);
  4.     element.removeAttribute('title'); element.setAttribute('_title', text);
  5.     element.insert({'after': tooltip.setStyle({'left':element.positionedOffset().first()+'px'})});
  6. }).invoke('observe', 'mouseout', function(evt){
  7.     var element = evt.findElement('a'), text = element.getAttribute('_title');
  8.     element.removeAttribute('_title'); element.setAttribute('title', text);
  9.     if (element.next().hasClassName('tooltip')) element.next().remove();
  10. });

  • Es wird OnMouseOver ein DIV kreiert mit dem Inhalt des TITLE Attributs eines Links. OnMouseOut wird das DIV einfach wieder entfernt.
  • Damit das Browser Tooltip nicht zusätzlich angezeitg wird, verschiebe ich bis zum OnMouseOut den Inhalt des TITLE Attributs kurz in ein Fake-Attribut namens _TITLE.
  • Der Selector a[title] wählt nur Anker die auch wirklich ein TITLE Attribut haben. Somit brauchen wir auch nicht mehr zu kontrollieren ob eines vorhanden ist.
  • Vom Javascript aus wird nur LEFT gesetzt, TOP braucht man nicht zwingend.
  • Am Ende von OnMouseOut prüfe ich ob das nächste Element wirklich die tooltip Klasse hat. Das ist nur eine reine Vorsichtsmaßnahme. Wer dennoch Probleme hat könnte etwas radikaler einfach alle DIVs mit dieser Klasse löschen via getElementsByClassName (ist am schnellsten).

Das nachfolgende CSS kümmert sich um den z-Index und den Abstand zum Cursor.

div.tooltip {
    color: #000;
    background-color: #FAFAFA;
    position: absolute;
    z-index: 9999;
    padding: 5px;
    margin: 5px;
    margin-left: 15px; /* Abstand zum Zeiger */
}

So, und das war's. Wenn man nun irgendwo auf der Seite ein <a ... title="mein tooltip"> platziert hat man einen Link mit Tooltip. Zero config, wenn man so will.

Man kann natürlich auch ohne Probleme auf andere Elemente erweitern. Beispiel: $$('a[title], img[title]')...

Falls jemand Verbesserungsvorschläge hat: Immer her damit!
Script wurde in IE, FF und Safari getestet.

Hallo zusammen. Es ist einige Zeit her dass ich mal was geschrieben habe. Ich werde versuchen nun wieder öfters eine halbe Stunde opfern zu können.

Es gibt nun eine neue Lightbox Version. Da die bisherige 0.7 eigentlich in seiner Grundfunktion immernoch kompatibel ist gab es hier nicht viel zu tun. Daher trägt die aktuelle Version den Titel 0.7a.

Was gibt's neues?
- Settings Seite funktioniert endlich richtig.
- Neuste Lightbox 2 in der Version 2.0.4 enthalten
- Komprimiertes Javascript (nur noch 48kb, dank gzip)
- Nur noch 1 DB Feld wird für die Einstellungen verwendet (statt 8)
- Repository auf GitHub angelegt. Dort gibt's immer die neuste Version.
- Admin Panel hübsch gemacht für WP 2.6+

Die gute alte Projekt Seite (zip)
Die neue Seite auf GitHub

Readme liegt bei, alles andere kennt ihr bestimmt ja. Upgrade: Ich empfehle den Uninstall Button im Adminmenü einmal zu drücken nachdem ihr die neue Version hochgeladen habt. Diese räumt den alten Mist aus der Datenbank. Danach geht ihr einfach nochmal auf die Lightbox Settings Seite und somit wird automatisch neu installiert. :)

Viel Spass!

Grad aus meinen Blogs gefischt: Version 1.1 des FancyZoom Scripts von Carbel wurde vorgestern veröffentlicht. FancyZoom 1.1 ist eine Art Lightbox, allerdings mit einer netten Zoom Animation und ohne den Dunkel-Effekt.

Picture 1.png

Also anstatt dass einfach der Bildschirm sich verdunkelt, eine Ladeanimation kommt und dann das Bild zentriert angezeigt wird, wird bei FancyZoom - vom Thumbnail ausgehend - das Bild auf die richtige Grösse gebracht und dann geladen.

Vom Style her erinnert es mich an MacOS X.

Sieht sehr chique aus und Bilduntertitel wie bei der altbekannte LightBox sind natürlich auch möglich.

Demo und Download auf carbel's blog

Justin Palmer hat ein Snippet für Prototype gepostet welches das Date Object um strftime() erweitert.

JAVASCRIPT:
  1. Object.extend(Date.prototype, {
  2.   strftime: function(format) {
  3.     var day = this.getDay(), month = this.getMonth();
  4.     var hours = this.getHours(), minutes = this.getMinutes();
  5.     function pad(num) { return num.toPaddedString(2); };
  6.  
  7.     return format.gsub(/\%([aAbBcdHImMpSwyY])/, function(part) {
  8.       switch(part[1]) {
  9.         case 'a': return $w("Sun Mon Tue Wed Thu Fri Sat")[day]; break;
  10.         case 'A': return $w("Sunday Monday Tuesday Wednesday Thursday Friday Saturday")[day]; break;
  11.         case 'b': return $w("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")[month]; break;
  12.         case 'B': return $w("January February March April May June July August September October November December")[month]; break;
  13.         case 'c': return this.toString(); break;
  14.         case 'd': return pad(this.getDate()); break;
  15.         case 'H': return pad(hours); break;
  16.         case 'I': return pad((hours + 12) % 12); break;
  17.         case 'm': return pad(month + 1); break;
  18.         case 'M': return pad(minutes); break;
  19.         case 'p': return hours&gt; 12 ? 'PM' : 'AM'; break;
  20.         case 'S': return pad(this.getSeconds()); break;
  21.         case 'w': return day; break;
  22.         case 'y': return pad(this.getFullYear() % 100); break;
  23.         case 'Y': return this.getFullYear().toString(); break;
  24.       }
  25.     }.bind(this));
  26.   }
  27. });

[via Justin Palmer]

Schön gelöst. Weitere ähnliche Schnippsel von ihm gibt es im GitHub.

Prototype LogoJohn-David Dalton hat mal wieder das Protoculous Paket aktualisiert. Endlich ist das neue Prototype 1.6 um 60% kleiner. Diesmal hat er auch ein kleineres Protoculous (nur mit effects.js) erstellt, welches in der Packer Variante ohne GZIP nur 64k wiegt (mit GZIP nur 28k).

Natürlich gibt es auch wieder das normale Protoculous (Prototype 1.6 mit Scriptaculous 1.8) als Packer (92k) und Shrinkvars Version (152k).

Nun kann man wohl getrost umsatteln. Und wenn die "deprecated" Sektion aus Prototype rausfliegt wird es wohl noch schmaler.

Download

Happy coding!

Den Benutzer nicht gleich umzuleiten wenn er sicht registrieren möchte ist chique, modern und freundlich. Das geht aber nur mit Javascript, oder doppelten Code, bzw. zwei Formularen. Aber wenn der Benutzer kein Javascript hat, geht es entweder überhaupt nicht, oder wir müssen die Struktur so ändern, dass wir andere Links setzen.

Hier mal ein Vorschlag wie man das geschickt umgehen kann.

JAVASCRIPT:
  1. var AppRules = {
  2.   'a.swaplink:click': function(element) {
  3.     new Effect.toggle(element.getAttribute('href').split('#')[1], 'appear', {duration:0.4});
  4.       // brich den inhalt von href auseinander und verwende den hinteren teil.
  5.   }
  6. }
  7.  
  8. FastInit.addOnLoad(function(){
  9.     EventSelectors.start(AppRules);
  10. });

Unsere Links müssten dann so aussehen:

HTML:
  1. <a href="signup.php#signup" class="swaplink">Registrieren</a>

Unser Formular müsste dann eine versteckte Sektion beinhalten, welche die zur Registrierung zusätzlich notwendigen Felder beinhalten.

HTML:
  1. <div id="signup" style="display:none">
  2. ... wähle ein passwort, etc..
  3. </div>

Wenn Javascript aktiviert ist erhält der "swaplink" unser Event, welches "href" auseinanderbricht und nur das verwendet was hinter der Raute steht um ein Element mit der daraus resultierenden ID zu 'togglen'.

Wenn Javascript nicht aktiviert ist wird der Benutzer ganz einfach auf die "signup.php" umgeleitet. Zusätzlich hätte man auch die Sprungmarke auf der signup.php um es dem Benutzer noch angenehmer zu machen.

So einfach kann das sein! :)

Verwendete Bibliotheken: prototype.js, event-selectors.js, fastinit.js, effects.js

Ich bin grad so durch die Gegend gesurft und fand dabei folgende Slideshow von Chris Heilmann. Diese fand ich so genial das ich sie einfach bloggen musste. Von mir ein persönliches Dankeschön für diesen amüsanten Abriss der letzten Jahre im Javascript-Land.

Zu den ermutigenden Worte an die Flash-Crowd schliess ich mich an: "Ich will" auch haben :-)

So wurde bisher in Prototype.js eine Klasse erstellt:

JAVASCRIPT:
  1. var Message = Class.create();
  2. Message.prototype = {
  3.   initialize(title, message) {
  4.     this.title = title;
  5.     this.message = message;
  6.   },
  7.   format: function() {
  8.      return this.title + "\n" + this.message;
  9.   },
  10.   show: function() {
  11.      alert(this.format());
  12.   }
  13. }

Und so haben wir diese dann erweitert, komplett mit "Variable.prototype" und "Object.extend", aber ohne wirklichen Bezug zur abgeleiteten Klasse.

JAVASCRIPT:
  1. var ErrorMessage = Class.create();
  2. ErrorMessage.prototype = Object.extend(new Message(), { // überschreibe format()
  3.   format: function() {
  4.      return 'ERROR: ' + this.title + "\n" + this.message;
  5.   }
  6. });

In Prototype 1.6 ist dass nun alles ein wenig einfacher. Anstatt noch mal mit "Variable.prototype = {...}" zu beginnen, übergeben wir den "Hash" direkt als Parameter von Class.create().

JAVASCRIPT:
  1. var Message = Class.create({
  2.   initialize(title, message) {
  3.     this.title = title;
  4.     this.message = message;
  5.   },
  6.   format: function() {
  7.      return this.title + "\n" + this.message;
  8.   },
  9.   show: function() {
  10.      alert(this.format());
  11.   }
  12. });

Das Erweitern ist jetzt ähnlich PHP und liest sich fast ebenso natürlich wie "Klasse extends AndereKlasse". Wir nehmen auch hier direkt den Namen der Variable (bzw. Klassenname) anstatt "new Variable()". Hinzu kommt dass "Object.extend" komplett wegfällt. Man beachte das $super, welches die Kopie der originalen Methode ist.

JAVASCRIPT:
  1. var ErrorMessage= Class.create(Message, {
  2.   format: function($super) {
  3.      return 'ERROR: ' + $super(); // vergleichbar mit parent::format(); in PHP
  4.   }
  5. });

Die vorherige Klasse hatte kein Parameter in der Funktion, jedoch wird $super nun definiert. Warum?

Das ist nur der Fall bei Unterklassen und ist optional. Sollte die ursprünglich Methode bereits Paremeter erwarten, sind diese dann einfach als zweiter Parameter zu definieren. Die neue Methode lässt sich dann genau wie das Original verwenden.

Hier mal ein Beispiel:

JAVASCRIPT:
  1. var notice = new Message('Nur zur Info', 'Dies ist das normale Format');
  2. notice.show(); // wirft alert('Nur zur Info ...');
  3.  
  4. var error = new ErrorMessage('Es trat ein Fehler auf', 'Deshalb ein anderes Format');
  5. error.show(); // wirft alert('ERROR: Es trat ein ...');

Ich habe eine Testseite angelegt und auch ein paar weitere Beispiele angefangen. Vor allem ist addMethods interessant, da es die hinzugefügten Methoden anschliessend an alle Instanzen vererbt. So könnte man zum Beispiel durch eine erfüllte Bedingung allen Klassen "auf Knopfdruck" neue Möglichkeiten einräumen (mutieren).

Naja.. schaut euch einfach mal die Tests an und ansonsten wie gehabt:
Happy coding!

Ich hab mich mal ein wenig mit Adobe AIR befasst. Die Beta soll ja auch den Webentwicklern mit HTML/JS Hintergrund entgegenkommen. Ich habe mich also ans Werk gemacht und musste feststellen das dies wirklich noch zu sehr eine Beta ist.

Es ist möglich Prototype und sonstiges Javascript zu verwenden und auch CSS Frameworks with YUI Fonts-Reset-Grids einzusetzen. Also wirklich ganz normales HTML, mit und ohne Windows Fenster drumherum. Damit hat man schon mal eine ganze Menge Freiraum! Was die Client/Server Kommunikation betrifft, kann man beliebig Ajax verwenden und natürlich auch die von AIR gebotenen Schnittstellen (windows.runtime im DOM). Auch tief verschachtelte Ordner werden von CSS und HTML ohne Probleme übernommen.

Als ich so weit war dem ganzen Leben einzuhauchen traten aber schon die ersten Probleme auf: Es wurde mein "bitte warten" GIF nicht abgespielt. Ich war ja bereit ein wenig einzustecken und komplett auf HTML und Javascript zu setzen, aber keine animierten GIFs - um es halbwegs interessant aussehen zu lassen - ist absolut inakzeptabel. Davon habe ich mich aber wenig beeindrucken lassen. Geht ja auch anders ... dachte ich.

Ich habe dann die Animation einfach in Flash umgesetzt und das SWF in das HTML mit dem üblichen Object-Tag eingefügt. Man sollte meinen dass dies klappt, wo AIR doch eigentlich zusammen mit Flex sowieso den Schwerpunkt in Flash hat. Ergebnis: Fehlanzeige! SWF Dateien werden nicht innerhalb des HTML Bereichs angezeigt. Egal mit welchen Tricks. Im Forum habe ich dann erfahren dass dies bekannt ist und mit der nächsten Version kommen soll. :-(

Okay.. Also HTML Support ist drin und läuft auf WebKit (was mehr oder minder eine gute Wahl ist), aber Animationen lassen sich nicht mit den üblichen Mitteln realisieren. Damit ist das ganze zur Zeit ziemlich sinnlos, aber für mich nicht gänzlich gestorben. Ich werde nun mal versuchen mit Flash und Flex die Sache auszuprobieren, denn Lust meine ursprüngliche Idee umzusetzen habe ich immernoch.

Es gibt ein Beta Update für Flash womit Adobe AIR integriert wird, aber auch hier nur halb! Es gibt kein Code-Hinting, was häufige Blicke in die Referenz Doku zur Folge hat. Mit ein paar Tricks kann man aber sich bis zum Adobe Integrated Runtime 1.0 Release behelfen.

Schade das der Einstieg in die AIR Entwicklung nicht so leicht war, wie erhofft.

Wer es auch mal probieren möchte sollte sich Aptana IDE besorgen. Dort ist es super integriert.

AIR HTML QuickStart
AIR Javascript Language Reference

Auf Ajaxian entdeckte ich heute die sprinkle.js. Ein Skript welches das dynamische laden von Inhalten via eines Pseudoattributes "src" ermöglichen soll. Abgesehen davon dass dies nicht valide ist, da es kein "src"-Attribut für ein DIV gibt, waren die Kommentare im Gegenzug dazu doch recht erfrischend. :-)

Hier ein Beispiel wie sprinkle.js funktionieren soll:

HTML:
  1. <script src="sprinkle.js"></script>
  2. <div src="info.html"></div>
  3. <!-- info.html wird via ajax in das div geladen, on body load -->

Die sprinkle.js ist rund 300 Zeilen lang und enthält eine Menge Schönheitsfehler und ist eigentlich ein gutes Beispiel wie man es nicht machen sollte. Wem aber diese Lösung gefällt, und bereits eine Bibliothek wie Prototype oder jQuery verwendet, sollte sich mal die folgenden Beispiele ansehen.

jQuery - www.jquery.com

JAVASCRIPT:
  1. $('textarea[src], div[src], span[src]').each(function() {
  2. $(this).load(this.src);
  3. });

Prototype -www.prototypejs.org

JAVASCRIPT:
  1. $$('textarea[src], div[src], span[src]').each(function(el){
  2. new Ajax.Updater(el, el.src||'');
  3. });

Somit wäre das mit der sprinkle.js wohl dann auch geklärt ;)