Bernhard Häussner
Journal: Neueste Artikel erscheinen hier (Seite 9 von 21)

Friede den Hütten? Google fotografiert wieder...

29.06.2009, 17:58
Google Car in Würzburg
Google Car in Würzburg

Exhibitionisten aller Länder vereinigt euch! ☺ Heute ist in Würzburg wieder mal ein Streetview-Auto unterwegs. Was mich an der Sache allerdings noch etwas irritiert: Es hatte nicht das Google-Logo aufgeklebt. Verkauft Google etwa seine Spielzeugchen an den begüterten Datamining-Landschaftsfotografen? Oder fahren die Kamerawagen schon wieder nach Hause?

Nun kann sich der Würzburger zumindest darauf freuen, schon bald ein Foto von sich im Internet zu entdecken. Warum kann man sich da eigentlich nicht verlinken, wie bei StudiVZ? Oder wie bei iPhoto, sodass man dann gleich mitgeteilt bekommt, wo man noch alles abgelichtet wurde. Noch mal von hinten:

Google Car in Würzburg (von hinten)
Google Car in Würzburg (von hinten)

Angesichts der Tatsache, dass es nicht gerade in der City herum fuhr und ein Franzose am Steuer telefoniert hat (und französische Zulassung), könnte es auch sein, dass es nur eine kleine Durchreise war. Oder der Spaß beginnt erst noch.

Kommentare: 3 Einträge

MySQL-Queries sparen mit GROUP_CONCAT

22.06.2009, 20:10
Ein MySQL-Query mit GROUP_CONCAT

Ein MySQL-Query mit GROUP_CONCAT

Fast jede (Web-)Anwendung, die eine Datenbank wie MySQL benutzt, muss irgendwie das Problem lösen, zu einem Artikel mehrere Tags, zu einem Eintrag mehrere Kommentare, zu einem Wort mehrere Synonyme zu finden, die sich in einer weiteren Tabelle befinden. Jedes Mal, wenn mehrere Zeilen Abgefragt werden sollten, die wieder mehrere untergeordnete Zeilen haben, bedeutet das meistens für jedes Element einen weiteren Query zu starten, oder eine zweite Abfrage mit einer weiteren Liste zu starten, oder eine riesige Reihe Datensätze abzufragen und der Anwendung das Gruppieren zu überlassen. Mit GROUP_CONCAT (Dokumentation) lässt sich in MySQL (fast) alles in ein SQL-Query zusammenfassen.

Einführung

Nehmen wir eine einfache Datenbankstuktur an, wie die eines Blogs. Es gibt blogeintraege mit einem Titel, blogtags mit einem Namen. Dann gibt es noch eine Tabelle, die beides verbindet, z.B. blogtaglinks. Jeder Datensatz hat natürlich eine eindeutige ID. Wenn man jetzt alle Blogeinträge auflisten will, könnte man das wie folgt machen:

SELECT Titel FROM blogeintraege

Um die Anzahl der Verbundenen Tags herauszufinden, wäre ein solcher Join üblich:

[pr:sqle]SELECT Titel, COUNT(distinct(t.ID)) FROM blogeintraege b, blogtags t, blogtaglinks l WHERE b.ID = l.ID_entry and t.ID = l.ID_tag GROUP BY b.ID[/pre]
+-------------------------+-----------------------+
| Titel                   | COUNT(distinct(t.ID)) |
+-------------------------+-----------------------+
| Hello World!            |                     2 |
| The CMS                 |                     2 |
| To be Released Later... |                     1 |
+-------------------------+-----------------------+
3 rows in set (0.01 sec)

Hier werden jeweils alle Spalten, die den selben Blogeintrag repräsentieren gruppiert und dann aus diesen Gruppen die Anzahl der verschiedenen Tags abgefragt. Dabei ist COUNT die Gruppenfunktion, die die nun versteckten, „weg-gruppierten“, Datensätze verarbeitet - hier zählt.

Die GROUP_CONCAT-Funktion

MySQL und andere RDBMSs kennen für Zahlen einige Funktionen, die Summe, Durchschnitt etc. gruppierter Zahlen berechnen können. MySQL kennt darüber hinaus die GROUP_CONCAT()-Funktion, welche die „versteckten“ Werte (also auch CHAR) in einer Spalte zusammen zu hängen. Mit dieser Funktion wäre es also möglich, die Tags direkt aufzulisten:

SELECT Titel, GROUP_CONCAT(distinct(t.name))
FROM blogeintraege b, blogtags t, blogtaglinks l
WHERE b.ID = l.ID_entry and t.ID = l.ID_tag
GROUP BY b.ID
+-------------------------+---------------------------------+
| Titel                   | GROUP_CONCAT(distinct(t.name))  |
+-------------------------+---------------------------------+
| Hello World!            | Projekt: Mein Blog,Persönliches |
| The CMS                 | PHP,Projekt: Mein Blog          |
| To be Released Later... | Projekt: Mein Blog              |
+-------------------------+---------------------------------+
3 rows in set (0.00 sec)

Jetzt wird anstatt der Anzahl der Tags eine Komma-getrennte Liste der Tags angezeigt. Das ist insofern ein recht großer Fortschritt, da wir nicht nochmal eine zweite Abfrage machen müssen, die uns jeweils die Namen der Tags heraus sucht.

Weiter Ausbauen

Jetzt wird es Zeit ein bisschen zu Optimieren. In diesem Beispiel macht es wenig Sinn, aber theoretisch könnten zwei Tags den gleichen Namen haben. In diesem Fall würde uns MySQL aber den Namen nur einmal anzeigen, da distinct() doppelte Werte aussortiert. Wir würden also weniger Tags bekommen, als ein COUNT(distinct(t.ID)) anzeigt. Das können wir lösen, indem wir die distinct()-Funktion auf die IDs der Tags anwenden:

SELECT Titel, GROUP_CONCAT(distinct(t.ID),'-', t.name)
FROM blogeintraege b, blogtags t, blogtaglinks l
WHERE b.ID = l.ID_entry and t.ID = l.ID_tag
GROUP BY b.ID

Das würde uns für jeden Blogeintrag eine Liste erstellen, wie 42-Webbrowser,28-Productivity,27-Design. Die eigentlich überflüssigen IDs werden später noch entfernt, z.B. mit PHP.

Wenn wir zusätzlich noch die Erstellungsdaten z.B. der letzten drei Kommentare abfragen wollen, zeigt sich, warum wir distinct() verwenden müssen: Sonst wird jeder Tag so oft angezeigt, je nach dem, wie viele Kommentare ein Eintrag hat. Wenn wir also noch eine Tabelle benutzen, wie blogkommentare mit creation_time, und uns jeweils die letzten Erstellungszeiten anzeigen lassen wollen, benutzen wir:

SELECT Titel, GROUP_CONCAT(distinct(t.ID),'-', t.name), 
GROUP_CONCAT(distinct(c.ID),'-',c.creation_time ORDER BY creation_time DESC)
FROM blogeintraege b, blogtags t, blogtaglinks l, blogkommentare c
WHERE b.ID = l.ID_entry and t.ID = l.ID_tag and c.entry_id = b.id
GROUP BY b.ID

Jetzt haben wir noch eine weitere Spalte mit einem Wert, wie 34-2008-10-20 18:28:41,33-2008-10-19 23:22:37. Doch je mehr Spalten wir abfragen, desto unübersichtlicher werden die Listen. Außerdem hängt ja immer noch die ungewollte, aber doch notwendige id davor.

Verarbeitung mit PHP

Diese minderen kosmetischen Makel lassen sich dann in der Anwendung noch beheben. Hier zeige ich, wie ich mit PHP die Listen in Arrays lade und sortiere:

function arrayifyGrpConcats($q,$resultset) {
  $regexp='/\\bgroup_concat\\((.*?) separator \'(?<sep>.*?)\'\\) +as +(?<name>\w+)\\b/i';
  if ( preg_match_all($regexp,$q,$matches,PREG_SET_ORDER) ) {
    foreach ($matches as $grpCoClause) {
      if (isset($grpCoClause["name"],$grpCoClause["sep"])) {
        $name=$grpCoClause["name"];
        if (strpos($name,'_',1)===false) {
          $foldername=$name;
          $subname=false;
        } else {
          $foldername=substr($name,0,strpos($name,'_',1));
          $subname=substr($name,strpos($name,'_',1)+1);
        }
        $s=$grpCoClause["sep"];
        foreach ($resultset as $id=>$row) {
          $val=$row[$name];
          unset($resultset[$id][$name]);
          if ($subname) {
            if (!isset($resultset[$id][$foldername])) {
              $resultset[$id][$foldername]=array();
            }
            $vals=explode($s,$val);
            for ($i=0, $len=count($vals);$i<$len;$i++) {
              if (!isset($resultset[$id][$foldername][$i])) {
                $resultset[$id][$foldername][$i]=array();
              }
              $resultset[$id][$foldername][$i][$subname]=preg_replace('/\\d+-/','',$vals[$i],1);
            }
          } else {
             $resultset[$id][$foldername]=explode($s,$val);
          }
        }
      }
    }
  }
  return $resultset;
}

Diese Funktion kann direkt nach der Abfrage auf die Ergebnisse angewendet werden. Dazu übergibt man ihr das Result als durchnummeriertes Array mit je einem assoziativen Unterarray (Dictionary) pro Datensatz, der als Schlüssel die Spaltennamen und als Werte den jeweiligen Spaltenwert enthält. Die Funktion sucht dann im Query nach GROUP_CONCAT-Aufrufen der From group_concat(... separator ',') as iwas. Das Trennsymbol wird explizit angegeben, damit es hier keine Missverständnisse gibt.

Man sollte bedenken, dass MySQL mit dem Separator im Wert nichts macht. Wie man im SQL sieht habe ich das Problem mit dem Trennzeichen im Wert „gelöst“, indem ich es aus dem Kommentartext einfach gelöscht habe. Eine ausgefeiltere Methode habe ich bei stackoverflow gefunden.

Als Alias nach as kann man fast alles angeben. Die Funktion wird diesen Namen als Spaltennamen im Result-Array verwenden.

Als kleines Extra habe ich noch eingebaut, dass die Funktion optional mehrere Spalten zusammen nimmt, wenn sie ein Alias der Form gruppe_name erkennt: Sie wird dann im Resultset-Array unter gruppe ein nummeriertes Array anlegen, das ein assoziatives Arrays enthält mit den verschieden name. Wenn wir also zu einem Kommentar nicht nur das Datum, sondern auch den Autor wissen wollen, könnten wir group_by... as comm_time, group_by... as comm_name verwenden, um die Daten für jeden Kommentar zusammen zu halten.

Im Endeffekt kann das dann so aussehen:

SELECT b.id, b.Titel AS Titel,
group_concat(DISTINCT t.ID,'-', t.name separator ', ') as tags_Name, 
group_concat(DISTINCT t.id separator ', ') as tags_id, 
group_concat(DISTINCT c.ID,'-',c.creation_time separator ', ') as comm_time, 
group_concat(DISTINCT c.ID,'-',REPLACE(LEFT(c.Text,20),',','') 
     separator ', ') as comm_text 
FROM blogeintraege b, blogtaglinks l, blogtags t, blogkommentare c
WHERE b.ID = l.ID_entry and t.ID = l.ID_tag and c.entry_id=b.id
GROUP BY b.ID LIMIT 1,2

Was uns dann dieses Ergebnis liefert: (print_r)

Array
(
    [0] => Array
        (
            [id] => 2
            [Titel] => The CMS
            [tags] => Array
                (
                    [0] => Array
                        (
                            [Name] => PHP
                            [id] => 3
                        )

                    [1] => Array
                        (
                            [Name] => Projekt: Mein Blog
                            [id] => 2
                        )

                )

            [comm] => Array
                (
                    [0] => Array
                        (
                            [time] => 2008-10-19 23:22:37
                            [name] => Bernhard H.
                            [text] => Also das Kommentarsy
                        )

                    [1] => Array
                        (
                            [time] => 2008-10-20 18:28:41
                            [name] => Bernhard H.
                            [text] => So ich hab es jetz 
                        )

                )

        )

    [1] => Array
        (
            [id] => 10
            [Titel] => To be Released Later...
            [tags] => Array
                (
                    [0] => Array
                        (
                            [Name] => Projekt: Mein Blog
                            [id] => 2
                        )

                )

            [comm] => Array
                (
                    [0] => Array
                        (
                            [time] => 2008-10-20 19:48:50
                            [name] => Bernhard H.
                            [text] => Cool das klappt auc
                        )

                )

        )

)

Ich hoffe mit dieser Methode in Zukunft Datenbankabfragen zu vermindern und damit die Geschwindigkeit der Anwendungen weiter zu erhöhen. Zwei kleine Sachen noch:

SELECT @@group_concat_max_len

Dieser Wert ist die maximale Länger der mit GROUP_CONCAT gebildeten Spaltenwerte und lieget meist bei 1024. Außerdem kann es nötig sein den Spaltenwerte von BLOB zu konvertieren: CAST(... as CHAR).

Kommentare: keine

Processing und Blender

17.06.2009, 20:25

Processing ist gut geeignet um Daten und Berechnungen zu visualisieren, Blender übernimmt dafür mehr Material- und Rendering-Aufgaben. Dennoch gibt es Bereiche, in denen sich die Verwendungszwecke überschneiden, zum Beispiel die hier gezeigten Formen.

Das Bild oben ist eine runde zweifache Spirale - Auf der innersten Ebene jeweils zwei gestreckte und zu Spiralen verdrehte Kreise und Quadrate, deren Dehnprodukt sich dann spiralförmig schraubend um einen Kreis windet. Zwar kann Blender auch mit Python gescriptet werden, das habe ich mir aber bisher noch nicht angeschaut, weshalb alles eher in „Handarbeit“ entstanden ist. Die Farben habe ich nachträglich noch ein bisschen mit GIMP verändert. Das ist ein Rendering in Original-Farben und mit einer etwas angehobenen Ebene:

Wenn sich diese Doppelspiralkreise dann wiederum um einen Kreis legen, entsteht das:

Mit Processing geht dieses verdoppeln und drehen alles viel Einfacher. Hier mal ein Bild aus einem nicht wirklich fertigem Projekt:

Es ist ein Versuch eine Art Haarbüschel nach gewissen Expansionsregeln zu erstellen. Die „Haare“ können in der Sketch interaktiv nach oben und unten gebogen werden, was ein recht lustiges Bild ergibt. Vielleicht hier nochmal ein Bild mit besserem Überblick und anderer rendering-Methode:

Ich habe mich auch schon in Processing etwas mit Spiralen auseinandergesetzt, ist aber leider nicht in 3D, aber dank farblicher Überarbeitung doch noch recht interessant geworden:

spiral
spiral

Die Bildungsvorschrift ist übrigens eine Linie von einem Punkt aus im rechten Winkel zum Ursprung bis zu einem bestimmten Winkel und das sehr oft wiederholt. Der Sourcecode beschränkt sich auf wenige Zeilen:

// Initialisieren
size (1800,1100);
smooth();
background(255);
// Setzt den Ursprung des Koordinatensystems auf die Mitte
translate(width/2,height/2);

// 20 Spiralen
for (int k=0; k<20; k++) {
  // Weiterdrehung pro Spiralsegment
  float rotateStep=PI/11.465;
  // Anzahl der Spiralsegmente
  int rotateAnz=200;
  // Startposition
  float len=k; // wird ei jeder Spirale leicht vaiiert
  // Startposition 2
  float prelen=cos(rotateStep)*len;
  
  // Die Spiralensegmente erstellen
  for (int i=0; i<rotateAnz; i++) {
    // für jedes Segment ein Stück weiter drehen
    rotate(rotateStep);
    // Entfernung des Endes des letzten Segments zum Ursrung
    // Verwendet als Entfernung des Anfangs des aktuellen Segments zum U.
    len=prelen/cos(rotateStep);
    // eine Linie vom Ursprung zum Anfang des Segments
    stroke(0,40);
    line(0,0,len,0);
    /* Damit das letzte, nicht abgeschlossene Segment 
     * nicht so hässlich aus der Spirale hängt */
    if (i!=rotateAnz-1) { 
      // Eine Linie vom Anfang zum Ende des Segments
      // Das wird die Spirale
      stroke(0,200);
      line(len,0,len,len*tan(rotateStep));
    }
    prelen=len;
  }
}

Mit den Kommentaren ist es doch ganz ordentlich länger geworden. Vom rechten Winkel sind übrigens nurnoch die Trigonometrischen Funktionen cos und tan übrig: Die Strecke Ursprung-Segmentbeginn ist die Hypotenuse des Segments und wird die (zu unserem Schritt-Rotationswinkel) Ankathede des nächsten.

Kommentare: 2 Einträge

Ein paar Code-Schnipsel

15.06.2009, 19:30
Javascript

Javascript

In diesem Post nur ein paar kleine Code-Auszüge und Techniken für Webanwendungen. Inhalt:

  • Data URI scheme für Favicons & CSS-Farbverläufe
  • HTML-Attribut contenteditable zum einfachen Editieren
  • Javascript: Globale Variablen einfach vermeiden
  • JSON in Script-Tags
  • JSON-Parser bei Bedarf laden
  • jQuery nach hinten verlegen
  • jQuery mouserest-Plugin

Data URI scheme für Favicons & CSS-Farbverläufe

Eine mir bisher unbekannte Funkion moderner Browser (ab IE8) sind data: URLs. Anders als normale http:// oder file:// URLs repräsentieren sie keinen Link, sondern die Daten sind komplett in der URL enthalten. Damit man auch Binärdaten verwenden kann (oder sich das escapen von HTML-Tags, „"“ etc. sparen), ist es möglich optional mit Base64 zu encodieren. Für ein Favicon sieht das dann so aus:

<link rel="shortcut icon" href="data:image/png;base64,iVBORw[...]RK5CYII="/>

Und für eine Graphik im CSS:

body {background:url(data:image/png;base64,iVBORw[...]ElFTkSuQmCC);}

Das lohnt sich bei kleinen Farbverläufen oder Symbolen, da ein zusätzliches Request zum Server mehr Bytes brauchen würde, als die Base64-Codierung produziert. Da bei base64 jeweils 3 Bytes zu 4 werden, sieht die Rechnung für die maximale PNG-Größe so aus:

PNG*(4/3) + 55 B = HTTP-HADER
mit HTTP-HADER:=600B PNG=1635 B

Da man im CSS mittels Sprites sowieso Requests sparen kann und durch Kombination größere PNGs bekommt, lohnt es sich hier also nur bei kleinen Projekten. (Oder bei riesigen Cookies, was die Request-Größe in die Höhe treibt. Da würde ich dann aber eine static-Subdomaine empfehlen. )

Als weiteren Vorteil kann man eine ganze Seite in eine HTML-Datei packen, jedoch kann man auch die Bilder nicht mehr so leicht bearbeiten.

HTML-Attribut contenteditable zum einfachen Editieren

Auch ein spaßiges Feature sind editierbare HTML-Elemente. Man kann das HTML-Attribut contenteditable für einzelne HTML-Elemente setzten oder gleich für die ganze Seite:

<body contenteditable="true">

Das Attribut ist zwar erst mit HTML5 valid, wird aber schon von einigen älteren Browsern unterstützt.

Javascript: Globale Variablen einfach vermeiden

Mit wachsender Zahl an Javascript-Bibliotheken wird die Wahrscheinlichkeit der Kollision globaler Variablen immer größer. Zwar ist es unwahrscheinlich, dass man z.B. $ überschreibt, aber manche Javascript Frameworks haben sich ja sogar zueignen gemacht, die Prototypen der Sprachfunktionen zu erweitern, was natürlich beim Einsatz eigener/anderer Prototypen-Erweiterungen nicht-überschaubare Probleme erzeugen kann. Das lässt sich aber vermeiden, indem man eine Art jQuery für andere Aufgaben erzeugt. Dazu kann man z.B. die ersten ~50 Zeilen von jQuery grob kopieren1, einen eigenen Framework-Namen einsetzen und dann eigene Funkionen einbauen, was man dann z.B. so verwenden kann:

var foo=Array(1,2,3,4);
alert(myFW(foo).contains(4));

Hier benutzt man dass die Funktion myFW einen Weiteren Konstruktor aus sich selbst aufrufen kann.

Es gilt zusätzlich globale Variablen zu vermeiden. Doch man kann für einzelne Komponenten Kürzel als Arrays verwenden, die dann die Globalen speichern. So kann man auch im DOM-Explorer von Firebug einen Überblick über globale Variablen behalten und die Komponenten können kommunizieren.

1 Ich vermute, wer den ganzen jQuery-Code aufmerksam gelesen hat, kann mehr als man in allen Javascript Büchern zusammen lernt.

Um den Speicherbedarf nicht unnötig in die Höhe zu treiben kann man die Variablen-Sichtbarkeit immer recht klein halten, damit die GC die Daten wieder fressen kann, wenn sie nicht mehr gebraucht werden. In JS sind übrigens auch Funktionen nicht global und löschbar. Funktionen, die nur beim Initialisieren gebraucht werden, kann man also mit delete funktionsName; wieder aus dem Speicher nehmen.

Ähnlich der main()-Funktion in anderen Programmiersprachen kann man in Javascript den Code zusätzlich in eine gleich aufgerufene, anonyme Funktion hüllen:

(function(){
  var nichtglobal;
  // code
})();

So kann man ganz normal „globale“ Variablen und Funktionen verwenden, aber ein anderes Script bemerkt davon nichts.

JSON in Script-Tags

Wenn man für Javascript Daten übertragen will, dann bietet sich JSON an, da es einfach Daten austauschen kann und in vielen Programmiersprachen implementiert ist. Da gibt es die eine Methode, JSONP, welche die Daten in einer Variable speichert, bzw. an ein Callback übergibt. Alternativ kann man auch die Daten in <script type="text/x-json" id="dafaultData">-Tags im DOM platzieren und dann mit Javascript auslesen und parsen

JSON-Parser bei Bedarf laden

Da moderne Browser jetzt native JSON-Parser bekommen, muss man den JSON-Parser nicht immer laden. Das könnte man so lösen:

<script>
  if(!this.JSON||typeof JSON.stringify !== 'function'||typeof JSON.parse !== 'function'){
    document.write("<scr"+"ipt type=\"text/javascript\" src=\"json2.js\"></scr"+"ipt>");
  }
</script>

jQuery nach hinten verlegen

Ganz praktisch hat es sich erwiesen, das Javascript nach dem meisten Markup zu laden. So wird durch CSS und HTML schonmal die Seite aufgebaut und dann erst werden die Javascript-Verbesserungen geladen.

Viele Webanwendungen, z.B. Google Mail, zeigen zunächst einen Ladebildschirm. Dadurch fühlt sich die Anwendung gleich viel langsamer an. Schneller wird die Anwendung wenn zunächst der grobe Aufbau steht, mit HTML und CSS zuerst geladen, dann ein kleines Skript erste Programmteile implementiert, sodass die Optik fertig erscheint. Dann läd man jQuery und dann erst weitere Javascripts (die auf jQuery basieren). So kann sich die Seite blitzschnell aufbauen, ohne dass erst jQuery geladen werden muss.

jQuery mouserest-Plugin

Nicht wirklich ein „Plugin“ aber eine kleine Hilfsfunktion. Sie führt ein Callback aus, nachdem die Maus einige Zeit auf dem Element war:

jQuery(function(){ // plugins
  jQuery.fn.mouserest=function (callback,time) {
    jQuery(this).mouseenter(function(e){
      this.dsthTo=setTimeout(function (obj){callback.apply(obj)},time,this);
    }).mouseleave(function(){
      clearTimeout(this.dsthTo);
    });
  }
});

Das ist ganz praktisch, denn wenn man die Zeit (in ms) auf 200 setzt, lässt sich verhindern, dass bestimmte Effekte beim bloßen Überfahren auftreten. Sondern der Event wird erst ausgelößt, wenn die Maus eine kurze Zeit auf dem Element war. Im callback kann man übrigens durch das apply() mit this auf das DOM-Element zugreifen, wie es in jQuery üblich ist.

Kommentare: 2 Einträge

jQuery Shortcuts (hoverfade Plugin)

01.06.2009, 15:53
jQuery hoverfade Plugin

jQuery hoverfade Plugin

Als ich zum Ferienbeginn ein paar Sachen an meinem Blog gerichtet habe, habe ich zum ersten mal die Plugin-Funktion von jQuery (aktiv) benutzt. Es gibt bestimmte Javascript-Effekte, die man immer wieder benutzt. In jQuery sind einige davon schon Enthalten. Doch man baut dann im Endeffekt doch immer einige jQuery-Funktionen zusammen. Ich z.B. „verstecke“ gerne Dinge auf Webseiten, indem ich sie leicht transparent mache und dann bei Mouseover wieder voll anzeigen lasse. Deshalb habe ich dafür ein jQuery „Plugin“ gebastelt:

jQuery.fn.hoverfade = function(hvspeed,hvopacity,uhspeed,uhopacity) {
  return this.each(function(){
    jQuery(this).css({
      opacity:uhopacity
    }).hover(function(){ 
      jQuery(this).stop().fadeTo(hvspeed,hvopacity);
    },function(){ 
      jQuery(this).stop().fadeTo(uhspeed,uhopacity);
    });
  });
};

Man kann es natürlich kaum Plugin nennen, sondern eher ein kleiner Shortcut, sodass man nicht immer schreiben muss:

$("selector").css({
   opacity:0.3
}).hover(function(){ 
   $(this).fadeTo("fast", 1);
},function(){ 
   $(this).fadeTo("slow", 0.3);
});

Sondern nur noch folgendes braucht:

$("selector").hoverfade("fast",1,"slow",0.3);

Wie man sieht ist das Erstellen von solch kleinen Plugins recht einfach: man hängt einfach in das jQuery.fn-Objekt die Funktion ein, die dann den häufig verwendeten Code enthält, und schon kann man sie als Makro auf alle jQuery-Elemente anwenden. Natürlich kann man auch Parameter an die Funktion übergeben, um eine gewisse Konfiguration zuzulassen.

Außerdem empfiehlt es sich in Plugins statt dem kurzen $ das explizite jQuery zu verwenden, damit man das Plugin auch verwenden kann, wenn $ z.B. ein anderes JS-Frameworks ist. In der Plugin-Funktion ist this das jQuery-Objekt auf das das Plugin angewendet wurde. Damit der Code immer klappt, sollte man noch mit each über die enthaltenten Elemente loopen. Im each-Closure zeigt this dann allerdings auf das DOM-Element, weshalb man es (meistens) mit $(this) wieder zu einem jQuery-Objekt machen muss. Damit das Plugin auch wirklich immer läuft, sollte man es vielleicht auch noch von JSLint checken lassen.

OT: andere Änderungen an der Seite

Ich habe übrigens neben den Icons für die Social Networks (Damit die Blogeinträge weiter oben stehen) auch noch ein paar andere Sachen verändert: Die automatischen ids, die nur die <input> mit den <label> verbinden, habe ich verkürzt, damit sie nicht immer so hässlich im Markup stehen. Dann habe ich im CSS alle Schriftgrößen auf Pixel umgestellt. Ich hoffe jetzt natürlich, dass meine Seite weiterhin in allen Browsern (fast) gleich aussieht.

Meine Editor-Toolbar wird auch immer voller. Jetzt habe ich mir noch Buttons dazu gemacht, um encodeierte <> und [] einzufügen. Außerdem habe ich endlich (halb) vimeo-Support im CMS. Ist ja grausam immer diesen ganzen <embed>-Kram erstmal in valides HTML umzuschreiben und dann hat man immer sehr viel HTML-Code zwischen dem Text. Außerdem kann ich so jetzt z.B. ganz einfach mal die Schriftfarbe von allen eingebetteten vimeo-Videos ändern.

Eigentlich sollte es ja so sein, dass im Content der Blogposts nichts ist, das die Darstellung beeinflusst, doch man braucht bei eingebetteten Videos offensichtlich immer Breite (ist fest) und Höhe (leider nicht fest), weshalb ich dann habe: [[vimeo:4767190|338|]]. Um alles 100%ig zu machen müsste ich natürlich aus der oEmbed API die genauen Video-Daten holen, aber das ist dann fast doch ein bisschen zu viel Aufwand. Was mich allerdings wundert: Während diese seltsame embed-Box auf der vimeo-Seite nicht-valides HTML ausspuckt, gibt die API, z.B. http://vimeo.com/api/oembed.xml?url=http%3A//vimeo.com/4767190&width=540 schönes valides HTML XHTML heraus.

Kommentare: keine
 
Χρόνογραφ
© 2008-2012 by Bernhard Häussner - Impressum - Login
Kurz-Link zu dieser Seite: http://1-co.de/bj