<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Der Interaktionsdesigner - PHP, jQuery und CSS &#187; jQuery</title>
	<atom:link href="http://www.interaktionsdesigner.de/category/jquery/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.interaktionsdesigner.de</link>
	<description>Pauls Blog beschäftigt sich mit Webentwicklungsthemen, im Focus stehen jQuery, TYPO3, CSS und PHP.</description>
	<lastBuildDate>Sat, 05 Jun 2010 21:13:53 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>TYPO3 Klickvergrößern durch eine jQuery Lightbox ersetzen</title>
		<link>http://www.interaktionsdesigner.de/2009/12/04/typo3-klickvergrosern-durch-eine-jquery-lightbox-ersetzen/</link>
		<comments>http://www.interaktionsdesigner.de/2009/12/04/typo3-klickvergrosern-durch-eine-jquery-lightbox-ersetzen/#comments</comments>
		<pubDate>Fri, 04 Dec 2009 13:07:44 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[TYPO3]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=629</guid>
		<description><![CDATA[Wenn jQuery auf einer Seite eingebunden ist (z.B. durch die coole Extension Kiwi Slider) dann beißt sich das Javascriptframework mit anderen wie MooTools oder Prototype. Insgesamt sollte man bevorzugt ein Framework einsetzen, wenn man unbedingt beide braucht, dann lohnt sich die Suche nach dem NoConflict Mode von jQuery.
Wenn aber das unbedingt nur von einer Lightbox [...]]]></description>
			<content:encoded><![CDATA[<p>Wenn jQuery auf einer Seite eingebunden ist (z.B. durch die coole Extension <a title="Kiwi Slider" href="http://www.interaktionsdesigner.de/2008/09/09/typo3-extension-kiwi-slider-2/">Kiwi Slider</a>) dann beißt sich das Javascriptframework mit anderen wie MooTools oder Prototype. Insgesamt sollte man bevorzugt ein Framework einsetzen, wenn man unbedingt beide braucht, dann lohnt sich die Suche nach dem <a title="jQuery noConflict" href="http://docs.jquery.com/Core/jQuery.noConflict" target="_blank">NoConflict Mode</a> von jQuery.</p>
<p>Wenn aber das <em>unbedingt</em> nur von einer Lightbox kommt, dann sollte man sich kurz hinsetzen, einen geeigneten Klon aussuchen (<a title="Lightboxclone Matrix" href="http://planetozh.com/projects/lightbox-clones/" target="_blank">hier ist die große Lightbox Matrix</a>) und die <strong>Klickvergrößerung von TYPO3</strong> damit ersetzen. Das ist schnell, effektiv und in diesem Beitrag erklärt.<span id="more-629"></span></p>
<h2>Vorbereitungen</h2>
<p>Für mein aktuelles Projekt habe ich mich für den Lightbox Clone <a title="TopUp" href="http://gettopup.com/" target="_blank">TopUp</a> entschieden. Sieht sehr vielversprechend aus. Also welcher Klone auch immer: <strong>Daten herunterladen</strong> und <strong>ins Projekt einbinden</strong>. Dazu gehören mind. jQuery und das entsprechende Plugin, in vielen Fällen auch noch eine CSS Datei. Firebug verrät im <em>Script</em> Reiter ob es funktioniert hat.</p>
<p>TopUp benötigt keine CSS Datei, dafür aber <strong>einen Bilder- und einen Players-Ordner</strong> in dem SWF Dateien gespeichert sind um verschiedene Medien wiederzugeben. Per JavaScript können die Pfade angepasst werden:</p>
<pre><code class=''>TopUp.host = 'http://www.kiwi-service.de/';<br />
TopUp.images_path = 'fileadmin/template/img/topup/';<br />
TopUp.players_path = 'fileadmin/template/flash/';</code></pre>
<h2>JavaScript einbinden</h2>
<p>Eine Besonderheit bringt TopUp mit: Es lädt dynamisch die benötigten JavaScriptdateien nach. Dafür braucht er den <strong>kompletten javascripts/jquery Ordner</strong>. Dieser muss im gleichen Ordner liegen wie <strong>top_up-min.js</strong>.</p>
<pre><code class=''>fileadmin/template/js/top_up-min.js<br />
fileadmin/template/js/jquery/...</code></pre>
<h2>Test</h2>
<p>Alle benötigten Dateien sind eingebunden und in Firebug sichtbar. Um zu testen ob es funktioniert, füge ich ein Inhaltselement vom Typ HTML ein und schreibe mein eigenes Bild rein. Testbilder sind im <em>Images</em> Ordner vorhanden.</p>
<pre><code class=''>&lt;a href="fileadmin/template/img/topup/photos/1.jpg" class="top_up"&gt;<br />
&nbsp;&nbsp;&lt;img src="fileadmin/template/img/topup/thumbnails/1.jpg"&gt;<br />
&lt;/a&gt;</code></pre>
<p>Funktioniert, wunderbar! Wenn nicht, dann die eigene Seite mit den <a title="TopUp Beispiele" href="http://gettopup.com/examples" target="_blank">Beispielen</a> vergleichen.</p>
<h2>Typoscript</h2>
<p>Nun muss man <strong>TYPO3</strong> noch das hässliche JavaScript PopUp austreiben. Hier mein Konfiguration für <strong>Version 4.3</strong></p>
<pre><code class=''>temp.imageLinkWrap = 1<br />
temp.imageLinkWrap {<br />
enable = 1<br />
typolink {</p>
<p>parameter.cObject = IMG_RESOURCE<br />
parameter.cObject.file.import.data = TSFE:lastImageInfo|origFile<br />
parameter.cObject.file.maxW = {$styles.content.imgtext.maxW}<br />
parameter.override.listNum.stdWrap.data = register : IMAGE_NUM_CURRENT</p>
<p>title.field = imagecaption // title<br />
title.split.token.char = 10<br />
title.if.isTrue.field = imagecaption // header<br />
title.split.token.char = 10<br />
title.split.returnKey.data = register : IMAGE_NUM_CURRENT<br />
parameter.cObject = IMG_RESOURCE<br />
parameter.cObject.file.import.data = TSFE:lastImageInfo|origFile<br />
ATagParams = target="_blank"<br />
}<br />
}</p>
<p>tt_content.image.20.1.imageLinkWrap &gt;<br />
tt_content.image.20.1.imageLinkWrap &lt; temp.imageLinkWrap<span> </span></code></pre>
<p>Als erstes wird das temporäre Objekt <strong>temp.imageLinkWrap</strong> erzeugt und konfiguriert. Interessant ist die letzte Angabe <strong>ATagParams</strong> - darüber gibt man jedem Link die Klasse <em>top_up</em> mit, damit das Plugin sich um die Bilder kümmert.</p>
<p>Anschließend wird die Standardvergrößerung in <strong>tt_content.image.20.1.imageLinkWrap</strong> überschrieben. <strong>Cache leeren</strong> und Seite neuladen, fertig!</p>
<h2>TopUp Spezialitäten</h2>
<p>Dieses TopUp ist wirklich ein schönes Teil. Mit der Funktion <strong>addPresets()</strong> lassen sich für Links in DOM Bereichen Voreinstellungen treffen. Zum Beispiel alle Bilder die über <strong>ein Inhaltselement</strong> eingestellt sind, zu einer Gruppe zusammenfassen:</p>
<pre><code class=''>TopUp.addPresets({<br />
".csc-textpic-imagewrap a": {<br />
group: "images"<br />
}<br />
});</code></pre>
<p>Das bedeutet alle TopUp-Links im Div <strong>.csc-textpic-imagewrap</strong> werden der Gruppe <em>images</em> zugewiesen. Im Overlay gibts dann Vor- und Zurückpfeile.</p>
<h2>Fazit</h2>
<p>Die Initialisierung und Einstellungen einer Lightbox variieren natürlich. Es gibt aber ein paar schöne und elegante Tools deren Einsatz sich lohnen. Vorallem im Zusammenspiel mit der brand neuen TYPO3 Version 4.3 - ein großartiger Schritt mit schönen Verbesserungen. Unbedingt ansehen. Und TopUp einbinden!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/12/04/typo3-klickvergrosern-durch-eine-jquery-lightbox-ersetzen/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Ajax in Webapplikationen mit CakePHP und jQuery</title>
		<link>http://www.interaktionsdesigner.de/2009/11/21/ajax-in-webapplikationen-mit-cakephp-und-jquery/</link>
		<comments>http://www.interaktionsdesigner.de/2009/11/21/ajax-in-webapplikationen-mit-cakephp-und-jquery/#comments</comments>
		<pubDate>Sat, 21 Nov 2009 14:38:09 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=618</guid>
		<description><![CDATA[Jeder Webentwickler weiß, dass kein Benutzer mehr auf das Neuladen einer Seite warten will. Vorallem nicht, wenn es in Webapplikationen darum geht Elemente hinzuzufügen, zu bearbeiten oder zu entfernen.
Zum Beispiel in einem Tool welches umfangreiche Exportmöglichkeiten besitzt und gerade von einer jungen, dynamischen Agentur entwickelt wird. In dem Programm legt man ein Exportscript an und [...]]]></description>
			<content:encoded><![CDATA[<p>Jeder Webentwickler weiß, dass kein Benutzer mehr auf das Neuladen einer Seite warten will. Vorallem nicht, wenn es in Webapplikationen darum geht Elemente hinzuzufügen, zu bearbeiten oder zu entfernen.</p>
<p>Zum Beispiel in einem Tool welches umfangreiche Exportmöglichkeiten besitzt und gerade von einer <a title="Meine junge dynamische Agentur!" href="http://www.kiwi-service.de" target="_blank">jungen, dynamischen Agentur</a> entwickelt wird. In dem Programm legt man ein Exportscript an und kann diesem verschiedene Exportdateien zuweisen die beim durchlaufen dynamisch erstellt werden.</p>
<p>Diese einfache 1:n Beziehung ist mit Cake schnell gebacken, mit ein paar Zeilen erweitert und läuft. Allerdings verteilt auf mehreren Seiten. Zum Glück gibt uns Cake einfache Möglichkeiten an die Hand um das zu verhindern.</p>
<p>In diesem Artikel will ich aufschreiben wie man praktisch jedes Cake Projekt zusammen mit dem Lieblings-Javascriptframework jQuery in eine coole Web 2.0 Anwendung verwandeln kann.</p>
<p><span id="more-618"></span></p>
<h2>Grundlagen</h2>
<p>Ich gehe mal von einer laufenden Cakeanwendung aus. Da gibt es die Tabelle Export und die Tabelle Exportfiles. Die beiden sind über eine 1:n Beziehung verknüpft, die Anwendung wurde gebacken und man verfügt über die normalen CRUD Funktionen.</p>
<p><em>Wenn Interesse an einem Tutorial dazu besteht, wäre ein kurzer Hinweis in den Kommentaren oder per Mail nett.</em></p>
<h2>Der AppController</h2>
<p>Den schönsten Teil bringt Cake schon mit: den <strong>RequestHandler</strong>. Mit dessen Hilfe kann man auf Ajaxanfragen reagieren. Als erstes muss er in der Datei <strong>app/app_controller.php</strong> eingebunden werden:</p>
<pre><code class=''>$components = array('RequestHandler');</code></pre>
<p>Anschließend stehen in jedem Controller über <strong>$this-&gt;RequestHandler</strong> umfangreiche Möglichkeiten zur Verfügung. Besonders toll wirds im Callback <strong>beforRender()</strong>. Dieser wird im AppController definiert und von Cake automatisch aufgerufen, wenn die Logik im entsprechenden Controller abgearbeitet wurde.</p>
<p>So sieht die Killerfunktion aus:</p>
<pre><code class=''>function beforeRender() {<br />
&nbsp;&nbsp;if($this-&gt;RequestHandler-&gt;isAjax()) {<br />
&nbsp;&nbsp;   Configure::write('debug', 0);<br />
&nbsp;&nbsp;   if($this-&gt;RequestHandler-&gt;prefers() == 'json') {<br />
&nbsp;&nbsp;      die(json_encode($this-&gt;viewVars));<br />
&nbsp;&nbsp;   }<br />
&nbsp;&nbsp;   else {<br />
&nbsp;&nbsp;      $this-&gt;layout = 'ajax';<br />
&nbsp;&nbsp;   }<br />
&nbsp;&nbsp;}<br />
}</code></pre>
<p>Was hier passiert ist so gut wie selbsterklärend: Wenn es sich um eine asynchrone Anfrage handelt <strong>if($this-&gt;RequestHandler-&gt;isAjax())</strong>, dann wird als erstes jegliche Debugausgabe verboten. Das muss man sich unbedingt merken, sonst besteht die Gefahr auszuflippen weil <strong>debug()</strong> "aufeinmal" nichts mehr ausgibt. Auf der anderen Seite kann man aber nicht mit einem JSON String in Javascript weiterarbeiten, wenn dem ein Datenbanklog <span style="text-decoration: line-through;">am Hintern</span> an der schließenden Klammer klebt. <em>Also nicht vergessen!</em></p>
<p>Nach dem Cake weiß, das es eine Ajaxanfrage bearbeitet, wird geprüft, welche Rückantwort erwartet wird <strong>$this-&gt;RequestHandler-&gt;prefers()</strong>. Wenn diese JSON ist, werden alle <strong>viewVars</strong>, dass sind alle Variablen die über <strong>$this-&gt;set() </strong>im Controller gesetzt wurden, als JSON Objekt zurück gegeben. Damit stehen dem Javascript alle Informationen zur Verfügung die sonst im View verarbeitet werden. Toll oder?</p>
<p>Dieses <strong>die(json_encode()) </strong>finde ich noch nicht ganz so elegant wie der ganze Rest von Cake, tut aber seine Arbeit. Vielleicht hat jemand einen schöneren Ansatz?!</p>
<p>Wenn die Anfrage kein JSON erwartet, wird nicht das Standardlayout genutzt, sondern auf das Ajax umgeschaltet. Das befindet sich in der Datei <strong>app/views/layouts/ajax.ctp</strong> und besteht nur aus einer Zeile:</p>
<pre><code class=''>&lt;?php echo $content_for_layout; ?&gt;</code></pre>
<h2>Standardlayout</h2>
<p>Um Probleme beim ändern der BaseURL zu vermeiden habe ich mir angewöhnt im Standardlayout das Tag <strong>base</strong> zu verwenden um mit jQuery schnell auf die URL zugreifen zu können. In der Datei <strong>app/views/layouts/default.ctp</strong> muss dafür folgende Zeile hinzugefügt werden:</p>
<pre><code class=''>&lt;base href="http://&lt;?=$_SERVER['HTTP_HOST']?&gt;&lt;?=$this-&gt;base?&gt;/" host="http://&lt;?=$_SERVER['HTTP_HOST']?&gt;" /&gt;</code></pre>
<p>Ich weiß, das Attribut <strong>host</strong> verhindert eine komplette Validierung, allerdings brauche ich in einigen Fällen nur den Host und in anderen die komplette BaseURL, da zum Beispiel die erstellen Formulare mit <strong>$form-&gt;create(...) </strong>schon die BaseURL enthalten.</p>
<p>Das wird man gleich sehen.</p>
<h2>jQuery Action</h2>
<p>Da sind jetzt in wenigen Zeilen mächtige Grundlagen gelegt worden, die man mit jQuery an seiner Seite ausnutzen möchte. Im Formular zur Erstellung eines neuen Exports habe ich ein Button "Neue Exportdatei", die bei Klick in den Container <strong>div#new</strong> das Formular lädt um eine neue Datei zu speichern. Nichts leichter als das:</p>
<pre><code class=''>$ajax({<br />
&nbsp;&nbsp;url: $('base').attr('href')+'exportfiles/add/export:'+$('#ExportEditForm').attr('rel'),<br />
&nbsp;&nbsp;dataType: 'html',<br />
&nbsp;&nbsp;success: function(form) {<br />
&nbsp;&nbsp;    $('#new').html(form);<br />
&nbsp;&nbsp;}<br />
};</code></pre>
<p>Als benannten Parameter wird die ID des aktuellen Exports übergeben. Im Controller steht diese Information im Array <strong>$this-&gt;params['named']</strong> zur Verfügung. Von da wird sie an den View übergeben und hier wird, sofern gesetzt ein verstecktes Formularfeld erzeugt, anstatt der gebackenen Liste der vorhandenen Exports.</p>
<p>Mit der Verwendung des Ajax-Layouts und der Angabe <strong>dataType: 'html'</strong> steht in der Variable <strong>form</strong> der Success Funktion genau das Formular zur Verfügung, welches im View definiert wurde.</p>
<h2>JSON benutzen</h2>
<p>Wenn das Formular ausgefüllt wurde, muss man das Abschicken abfangen. Leider unterstützt die <strong>Live</strong> Funktion noch nicht den Event <strong>submit</strong>, deshalb muss man sich mit einem Klick auf den Submitbutton helfen.</p>
<pre><code class=''>$('#new .submit input').live('click', function() { ... });</code></pre>
<p>Das wirklick spannende ist natürlich auch hier die Ajaxanfrage und das Geheimnis liegt im <strong>dataType: 'json'</strong>.</p>
<pre><code class=''>$.ajax({<br />
&nbsp;&nbsp;url: $('base').attr('host')+$form.attr('action'),<br />
&nbsp;&nbsp;data: $form.serialize(),<br />
&nbsp;&nbsp;type: 'POST',<br />
&nbsp;&nbsp;dataType: 'json',<br />
&nbsp;&nbsp;success: function(content) {<br />
&nbsp;&nbsp;  if(content.saved) {<br />
&nbsp;&nbsp;      $form.closest('fieldset').prev().find('ul')<br />
&nbsp;&nbsp;          .find('li.empty').remove().end()<br />
&nbsp;&nbsp;          .append('&lt;li&gt;Neuer Eintrag &lt;b&gt;'+$form.find('input[type=text]:first').val()+'&lt;/b&gt; angelegt.&lt;/li&gt;');<br />
&nbsp;&nbsp;  }<br />
&nbsp;&nbsp;  else {<br />
&nbsp;&nbsp;      alert("Leider ist ein Fehler beim speichern aufgetreten.");<br />
&nbsp;&nbsp;  }<br />
&nbsp;&nbsp;}<br />
});</code></pre>
<p>In der Variable <strong>$form</strong> ist das jQuery Objekt gespeichert, welches das Formular widerspiegelt, dass gerade abgeschickt wurde. Da man immer wieder darauf zugreifen muss, macht es Sinn das in eine Variable zu legen um Zeit bei der Abarbeitung zu sparen.</p>
<p>In der Variable <strong>content</strong> stehen jetzt dank <em>AppController</em> und <em>RequestHandler</em> alle View-Variablen zur Verfügung. Über das Auslesen des Attributs <strong>Action</strong> des Formulars muss man sich nicht mal mehr Gedanken machen wo das Formular überhaupt abgearbeitet wird. Wichtig ist nur das in der entsprechenden Funktion ein Status gesetzt wird:</p>
<pre><code class=''>$this-&gt;set('saved', true);</code></pre>
<h2>Fazit</h2>
<p>Nach genau dem gleichen Prinzip funktioniert auch das Löschen oder jeder andere Anwendungsfall der einem einfällt. Ich bin im Moment mal wieder begeistert von diesen beiden tollen Frameworks und den schier unerschöpflichen Möglichkeiten.</p>
<p>Demnächst gibt es dann hoffentlich mal die Vorstellung von einem fertigen Tool. Bis dahin freue ich mich auf Fragen und Verbesserungsvorschläge. Vielen Dank fürs lesen!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/11/21/ajax-in-webapplikationen-mit-cakephp-und-jquery/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Select Boxes dynamisch verändern</title>
		<link>http://www.interaktionsdesigner.de/2009/09/03/select-boxes-dynamisch-verandern/</link>
		<comments>http://www.interaktionsdesigner.de/2009/09/03/select-boxes-dynamisch-verandern/#comments</comments>
		<pubDate>Thu, 03 Sep 2009 08:00:00 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=591</guid>
		<description><![CDATA[Die praktische Auswahl von Daten über eine Selectbox ist immer wieder ein Thema. Vorallem bei den ganzen neuen Beta-Ajax-Web-Applications kommen sie einem immer wieder unter.
Das Problem mit den Dingern, ist die dynamische Veränderung. Ein einfaches $("select").find("option:first").remove(); funktioniert zwar zum entfernen, aber das hinzufügen von Options ist schon schwieriger (-böser Blick zum Internet Explorer-). Und wenn [...]]]></description>
			<content:encoded><![CDATA[<p>Die praktische Auswahl von Daten über eine Selectbox ist immer wieder ein Thema. Vorallem bei den ganzen neuen <strong>Beta-Ajax-Web-Applications</strong> kommen sie einem immer wieder unter.</p>
<p><strong>Das Problem </strong>mit den Dingern, ist die dynamische Veränderung. Ein einfaches <em>$("select").find("option:first").remove();</em> funktioniert zwar zum entfernen, aber das hinzufügen von Options ist schon schwieriger (<strong>-böser Blick zum Internet Explorer-</strong>). Und wenn die Selectbox sich dann an einer weiteren orientieren soll, dann wirds haarig.</p>
<p><strong>Die Lösung</strong> ist das Klonen und verstecken der "originalen" Box. Aus dem Klon kann man dann die überflüssigen Auswahlmöglichkeiten entfernen.</p>
<p>Und jetzt folgt der Artikel wie man das effizient anstellen könnte. Natürlich Hand in Hand mit <strong>jQuery</strong>.</p>
<p><span id="more-591"></span></p>
<h2>Das Problem</h2>
<p>Folgende Anforderung bestand im aktuellen Projekt: In der ersten Selectbox wird die Frequenz gewählt (monatlich, vierteljährlich usw.). Die zweite Box bietet eine Auswahl an Monaten zur Angabe der Dauer.</p>
<pre><code class=''>&lt;select name="frequency" class="frequency"&gt;<br />
&nbsp;&nbsp;&lt;option value="1"&gt;monatlich&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="3"&gt;vierteljährlich&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="6"&gt;halbjährlich&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="12"&gt;jährlich&lt;/option&gt;<br />
&lt;/select&gt;</code></pre>
<pre><code class=''>&lt;select name="months" class="months"&gt;<br />
&nbsp;&nbsp;&lt;option value="0"&gt;0&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="1"&gt;1&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="2"&gt;2&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="3"&gt;3&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="4"&gt;4&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="5"&gt;5&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="6"&gt;6&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="7"&gt;7&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="8"&gt;8&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="9"&gt;9&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="10"&gt;10&lt;/option&gt;<br />
&nbsp;&nbsp;&lt;option value="11"&gt;11&lt;/option&gt;<br />
&lt;/select&gt;</code></pre>
<h2>Theoretisch</h2>
<p>Sehr einfach zu erklären: Der Anwender wählt in der ersten Auswahl "<em>halbjährlich</em>" - darauf hin stehen in der Monatsauswahl nur noch "<em>0</em>" und "<em>6</em>" zur Verfügung. Welcher Monat angezeigt wird, kann ganz einfach mit der <strong>Modulo-Operation</strong> berechnet werden. <a title="Pauls Blog über die Modulo Operation" href="http://www.interaktionsdesigner.de/2009/01/27/modulo-verstehen-und-benutzen/" target="_blank">Darüber hatte ich mich auch schon im Blog ausgelassen.</a></p>
<p><span>Der Klon wird mit der Klasse <em>active</em> gekennzeichnet und muss vor der nächsten Veränderung entfernt werden.<br />
</span></p>
<h2>Praktisch</h2>
<p>Jetzt wirds nett. jQuery ist natürlich im Projekt eingebunden. Sobald das Dokument geladen wurde, muss ein <strong>Eventhandler</strong> auf die Frequenzauswahl gelegt werden.</p>
<pre><code class=''>jQuery(function($) {<br />
&nbsp;&nbsp;$("select.frequency").onChange(function() {<br />
&nbsp;&nbsp;//hier gehts weiter!!<br />
&nbsp;&nbsp;});<br />
});</code></pre>
<p><span>Diese Form ist besser als das überall gesehene <em>$(document).ready(function() {})</em>, denn die <em>$</em> Funktion muss nicht mehr zwangsläufig von jQuery besetzt sein, sondern wird dem eigenen Code als Parameter übergeben.</span></p>
<p><span>Als nächstes beschreibe ich alle Schritte zum Ändern der Selectbox <em>$("select.months")</em> einzelnd. Anschließend gibts nochmal die komplette Funktion unkommentiert im Ganzen.</span></p>
<p><span><br />
</span></p>
<pre><code class=''>new_frequency = $(this).val();<br />
$form = $(this).parents("form:first");</code></pre>
<p>Die Auswahl wird in <em>new_frequency</em> zwischengespeichert und das umfassende Formular in der Variable <strong>$form</strong> abgelegt, um später schneller darauf zugreifen zu können.</p>
<p>Die Variable <em>$form</em> beginnt mit einem Dollarzeichen. Damit kennzeichne ich Variablen, die ein jQuery-Objekt enthalten. So ist es leicher, diese im weiteren Verlauf zu erkennen. Dieses Vorgehensweise kann ich <strong>wärmstens empfehlen</strong>.</p>
<pre><code class=''>if($("select.months.active", $form).length &gt; 0) {<br />
&nbsp;&nbsp;$("select.months", $form).filter(".active").remove().end().show();<br />
}</code></pre>
<p>Mit der Anweisung <em>$("select.months.active", $form).length</em> werden alle Selectboxen mit der Klasse <em>active</em> gesucht. Sind diese vorhanden, werden <strong>beide Selectboxen</strong> (die ggf. versteckte und der Klon) selektiert.</p>
<p>Mit der Funktion <em>filter(".active")</em> wird davon die aktive Selectbox ausgewählt und entfernt. Das <em>end() </em>setzt die Auswahl von <em>filter()</em> zurück und<em> show() </em>zeigt die übrig gebliebene Selectbox an.</p>
<p><strong>jQuery Power</strong> in einer einzigen Zeile! Aber es wird noch besser...</p>
<p>Die Selectbox mit der Klasse <em>.months</em> muss geklont werden, verändert, angezeigt und die ursprüngliche Box versteckt werden. Zum Glück braucht man dazu auch nicht mehr als <strong>eine Zeile</strong>.</p>
<pre><code class=''>$("select.months", $form).clone().hide().insertBefore("select.months").end().addClass("active").find("option").filter(function(i) { return i%new_frequency == 0 ? false : true; }).remove();</code></pre>
<p>Sieht komplizierter aus als es ist. Man kann es praktisch Stück für Stück lesen:</p>
<ol>
<li><strong>$("select.months", $form)</strong> sucht die Selectbox im aktuellen Formular mit der Klasse <em>months</em>.</li>
<li><strong>clone()</strong> kopiert die aktuelle Auswahl und speichert sie <em>irgendwo</em> zwischen. Die Auswahl besteht <strong>nicht</strong> aus dem Klon, sondern der ursprünglichen Auswahl.</li>
<li><strong>hide() -</strong> deshalb kann die Auswahl, also die <strong>erste</strong> Selectbox, auch einfach versteckt werden. Das passiert nach dem klonen, damit der Klon nicht extra wieder eingeblendet werden muss.</li>
<li><strong>insertBefore("select.months")</strong> fügt den Klon vor der Selectbox ein.</li>
<li><strong>end()</strong> setzt die Auswahl zurück. Damit besteht die Auswahl aus dem Klon und nicht mehr aus der ursprünglichen Selectbox.</li>
<li><strong>addClass("active")</strong><em> </em>fügt die Klasse hinzu damit der Klon später erkannt werden kann.</li>
<li><strong>find("option")</strong> sucht alle Optiontags innerhalb der geklonten Selectbox.</li>
<li><strong>filter(function(i) { return i%new_frequency == 0 ? false : true; })</strong><br />
Ein großartiges Stück jQuery präsentiert hier die ganze Power der <em>filter()</em> Funktion. Dieser kann man nicht nur einen <strong>Tagnamen</strong>, eine <strong>ID</strong> oder eine <strong>Klasse</strong> übergeben, sondern auch eine <strong>anonyme Funktion</strong>! Anhand dieser wird jedes enthaltene Element überprüft. Gibt die Funktion <em>true</em> zurück, bleibt das Element in der Auswahl; ansonsten fliegt es raus.<br />
Diese Funktion besteht nur aus einer simplen Berechnung und schmeißt alle Monate raus, welche zur gespeicherten Frequenz passen.<br />
Schön, oder?</li>
<li><strong>remove()</strong> entfernt dann die übrig gebliebenen Auswahlmöglichkeiten.</li>
</ol>
<p><strong>Fertig!</strong> Eine einzige Zeile klont und bearbeitet die Auswahlmöglichkeit. Und das sooft der Benutzer möchte. So sieht die Funktion im ganzen aus:</p>
<pre><code class=''>$(".frequency").change(function() {<br />
&nbsp;&nbsp;new_frequency = $(this).val();<br />
&nbsp;&nbsp;$form = $(this).parents("form:first");<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;if($("select.months.active", $form).length &gt; 0) {<br />
&nbsp;&nbsp;  $("select.months", $form).filter(".active").remove().end().show();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;$("select.months", $form)<br />
&nbsp;&nbsp;    .clone().hide().insertBefore(".plan:visible select.months").end().addClass("active")<br />
&nbsp;&nbsp;    .find("option").filter(function(i) { return i%new_frequency == 0 ? false : true; }).remove();<br />
});</code></pre>
<h2>Fazit</h2>
<p>Ich mag jQuery. Kennst du eine besser Lösung? Mal abgesehen von Plugins die das ganze mit einem unüberschaubaren Overhead erledigen?</p>
<p>Ansonten viel Spaß mit <em>abhängigen</em> Selectboxen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/09/03/select-boxes-dynamisch-verandern/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>CakePHP HABTM mit automatischer Vervollständigung</title>
		<link>http://www.interaktionsdesigner.de/2009/08/10/cakephp-habtm-mit-automatischer-vervollstandigung/</link>
		<comments>http://www.interaktionsdesigner.de/2009/08/10/cakephp-habtm-mit-automatischer-vervollstandigung/#comments</comments>
		<pubDate>Mon, 10 Aug 2009 09:38:33 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=578</guid>
		<description><![CDATA[Wenn man für ein beliebiges Model Posts speichern und eingeben kann, vielleicht weil man dieser Anleitung gefolgt ist, dann ist das schon ein schöner Fortschritt. Allerdings passieren immer wieder Tippfehler, und ähnlich klingende Begriffe finden sich in der Tagcloud.
Da hilft nur eins: während der Eingabe dem Benutzer direkt passende Tags vorschlagen. Eine automatische Vervollständigung wie [...]]]></description>
			<content:encoded><![CDATA[<p>Wenn man für ein beliebiges Model Posts speichern und eingeben kann, vielleicht weil man <a title="Pauls Blog erklärt HABTM" href="http://www.interaktionsdesigner.de/2009/04/17/mit-cakephp-in-30-minuten-tags-zu-einer-tabelle-hinzufugen/#comment-375" target="_blank">dieser Anleitung</a> gefolgt ist, dann ist das schon ein schöner Fortschritt. Allerdings passieren immer wieder Tippfehler, und ähnlich klingende Begriffe finden sich in der Tagcloud.</p>
<p>Da hilft nur eins: während der Eingabe dem Benutzer <strong>direkt passende Tags vorschlagen</strong>. Eine automatische Vervollständigung wie beim Mailen in den  großen sozialen Netzwerken oder beim suchen bei Google.</p>
<p>Zum Glück haben sehr schlaue Leute schon diverse Plugins erstellt, welche einen JSON String oder eine simple Textliste verarbeiten können. Zum Beispiel <a title="jQuery Autobox2" href="http://www.bigredswitch.com/blog/2008/12/autobox2/" target="_blank">Autobox2</a>. Gleich mal die <a title="jQuery Autobox2 Demo" href="http://www.bigredswitch.com/blog/wp-content/uploads/2008/12/autobox.html" target="_blank">Demo</a> angucken, runterladen und dann ins eigene Projekt integrieren.</p>
<p><strong>So funktionierts...</strong><span id="more-578"></span></p>
<h2>Vorbereitung</h2>
<p>Bestimmt ist <strong>jQuery</strong> schon im gesamten Projekt verfügbar, wenn nicht, muss es unter <em>views/layouts/default.ctp</em> mit Hilfe des <a title="Der Javascript Helper von CakePHP" href="http://book.cakephp.org/view/207/Javascript" target="_blank">Javascript Helpers</a> eingebunden werden:</p>
<pre><code class=''>echo $javascript-&gt;link('jquery.min');<br />
echo $javascript-&gt;link('jquery.action');<br />
echo $scripts_for_layout;</code></pre>
<p>Der Javascript Helper sucht die Dateien <strong>automatisch</strong> im Ordner <em>webroot/js/</em> und bindet sie ein, sofern sie vorhanden sind. In der Datei <em>jquery.action.js</em> befindet sich mein eigenes Script in dem das Verhalten der Seite gesteuert wird.</p>
<p>Sehr wichtig für den nächsten Schritt ist die Ausgabe der Variable <em>$scripts_for_layout</em>. Diese kann von jedem View aus befüllt werden. Das brauchen wir im View der<strong> Edit/Add Methode</strong> vom Model:</p>
<pre><code class=''>$javascript-&gt;link(array(<br />
&nbsp;&nbsp;'jquery.templating',<br />
&nbsp;&nbsp;'jquery.ui.autobox.ext.js',<br />
&nbsp;&nbsp;'jquery.ui.autobox.js'<br />
), false);</code></pre>
<p>Damit werden die Javascript Dateien <strong>nicht überall</strong> eingebunden, sondern nur auf den Seiten auf denen sie auch benötigt werden.</p>
<p>Nach dem gleichen Prinzip, aber mit dem <a title="HTML Helper von CakePHP" href="http://book.cakephp.org/view/205/HTML" target="_blank">HTML Helper</a> wird die Autobox CSS Datei eingebunden.</p>
<pre><code class=''>$html-&gt;css('jquery.ui.autobox.css', null, array(), false);</code></pre>
<p>Und da wir gerade dabei sind, bietet es sich an diese Datei zu öffnen und in der <strong>Zeile 66</strong> den Pfad zum <em>close.gif</em> anzugeben. Wenn sich alles in den korrekten Ordnern befindet ist es einfach:</p>
<pre><code class=''>background: url('../img/close.gif');</code></pre>
<p>Aussehen wird es gut, aber im Moment weiß das Script nicht wo es sich befindet. Damit die <strong>Baseurl</strong> auch in Javascript zur Verfügung steht, habe ich mir angewöhnt im HTML Gerüst das Basetag zu erweitern:</p>
<pre><code class=''>&lt;base href="http://&lt;?=$_SERVER['HTTP_HOST']?&gt;&lt;?=$this-&gt;base?&gt;/" host="http://&lt;?=$_SERVER['HTTP_HOST']?&gt;" /&gt;</code></pre>
<p>Mit dieser Zeile im Layout wird später vieles einfacher. Leider validiert es mit dem Attribut host nicht mehr, wer darauf Wert legt, muss sich also eine andere Lösung überlegen.</p>
<h2>jQuery Action</h2>
<p>Jetzt gehts los und das Plugin wird initalisiert. Ich mache das gerne in der Datei <em>jquery.action.js</em>. Um es auf die schnelle zu testen, kopiere ich einfach das Beispiel aus der Demo. Gemäß des letzten Posts zu HABTM heißt mein Feld <strong>temp_tags</strong>. Das resultiert in der ID <strong>ModelTempTags</strong>:</p>
<pre><code class=''>jQuery(function($) {<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;var list1 = [{text: 'Curious George'}, {text: 'George of the Jungle'}, {text: 'Felix the Cat'}];<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;$('#ModelTempTags').autobox({<br />
&nbsp;&nbsp;[tab]list: list1,<br />
&nbsp;&nbsp;[tab]match: function(typed) { return this.text.match(new RegExp(typed, "i")); },<br />
&nbsp;&nbsp;[tab]insertText: function(obj) { return obj.text },<br />
&nbsp;&nbsp;[tab]templateText: "&lt;li&gt;Hey: &lt;%= text %&gt;&lt;/li&gt;"<br />
&nbsp;&nbsp;});<br />
});</code></pre>
<p>Seite neuladen, und... herrlich! <strong>Funktioniert</strong>. Nur eine Kleinigkeit: Die vorhandenen Tags werden nicht übernommen. Zum Glück hilft eine einzige Zeile um das Problem zubeheben. Innerhalb der Autobox Konfiguration wird die Vorbelegung (<strong>prevals</strong>) angegeben:</p>
<pre><code class=''>prevals: $('#LinkTempTags').val().split(', ')</code></pre>
<p>Mit dieser Zeile wird der Inhalt ausgelesen, beim Komma geteilt und der Autobox als Vorbelegung übergeben. Dann kanns jetzt losgehen mit der <strong>asynchronen Datenübertragung</strong>. Das Attribut <strong>list</strong> in der Autobox Konfiguration wird ausgetauscht mit einer URL:</p>
<pre><code class=''>ajax: $('base').attr('href')+'tags/index',</code></pre>
<p>Damit wird im Parameter <strong>val</strong> die Benutzereingabe an den <em>Tags Controller</em> geschickt. Und der muss jetzt noch reagieren.</p>
<h2>Antworten mit CakePHP</h2>
<p>Der <strong>Controller Tags</strong> ist dafür zuständig eine Liste zu genierieren. In der Variable <strong>$this-&gt;params['url']['val']</strong> steht die Benutzereingabe zur Verfügung. Mit der wird eine Datenbankabfrage gestartet:</p>
<pre><code class=''>$tags = $this-&gt;Tag-&gt;find('all', array('conditions' =&gt; array('title LIKE' =&gt; $this-&gt;params['url']['val'].'%'), 'order' =&gt; 'Tag.title ASC'));</code></pre>
<p>Mit Hilfe der <strong>RequestHandler Componente</strong> wird ein Ajaxaufruf erkannt. Dafür muss die Componente im <strong>Controller</strong> eingebunden werden.</p>
<pre><code class=''>class TagsController extends AppController {<br />
&nbsp;&nbsp;var $components = array('RequestHandler');</code></pre>
<p>In der<strong> Index Funktion</strong> wird bei einem Ajaxaufruf der Renderingprozess gestoppt und purer Text zurück gegeben.</p>
<pre><code class=''>if($this-&gt;RequestHandler-&gt;isAjax()) {<br />
&nbsp;&nbsp;Configure::write('debug', 0);<br />
&nbsp;&nbsp;$this-&gt;autoRender = false;<br />
&nbsp;&nbsp;return '['.implode(',', Set::format($tags, '{text: "{0}"}', array('{n}.Tag.title'))).']';<br />
}</code></pre>
<p>Besondere Aufmerksamkeit verdient die letzte Zeile. <em>Implode</em> bekommt ein <em>Array</em>, das aus dem Core Utility <strong>Set</strong> stammt: <a title="CakePHP Set::format()" href="http://book.cakephp.org/view/672/format" target="_blank">Format</a>. Eine großartige Funktion die ein <strong>verschachteltes Array umformatiert</strong>. In diesem Fall zu JSON Objekten, welche dann mit einem Komma verbunden werden.</p>
<p>Das wars! Kaum zu glauben, aber ein Neuladen macht die Einbindung komplett. Und den Webentwickler fröhlich!</p>
<h2>Speichern im Model</h2>
<p>Das Plugin erstellt für jeden Tag ein verstecktes Formularfeld, welches den gleichen Namen hat wie das Ursprungsfeld. Damit die eingegebenen Daten übertragen werden, muss der Name auf ein Array hinweise:</p>
<pre><code class=''>echo $form-&gt;input('temp_tags', array('name' =&gt; 'data[Model][temp_tags][]');</code></pre>
<p>Der Trick sind die <strong>beiden eckigen Klammern am Ende</strong>. Damit steht die Eingabe im Model als Array zur Verfügung:</p>
<pre><code class=''>debug($this-&gt;data['temp_tags']);</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/08/10/cakephp-habtm-mit-automatischer-vervollstandigung/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nette Erweiterung für jedes Menü mit jQuery</title>
		<link>http://www.interaktionsdesigner.de/2009/07/27/nette-erweiterung-fur-jedes-menu-mit-jquery/</link>
		<comments>http://www.interaktionsdesigner.de/2009/07/27/nette-erweiterung-fur-jedes-menu-mit-jquery/#comments</comments>
		<pubDate>Mon, 27 Jul 2009 12:09:29 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=554</guid>
		<description><![CDATA[Es gibt kaum noch ein Menü, welches den Nutzer nicht mit einem Mouseover-Status über seine aktuelle Position informiert. Soweit so gut. Meistens sieht es so aus: Der Nutzer klickt auf den Menüpunkt, der Mouseover-Status verschwindet, die Seite verschwindet, die Seite baut sich wieder auf und der eben geklickte Menüpunkt ist dauerhaft hervorgehoben.
Ganz normal. Aber: Mit [...]]]></description>
			<content:encoded><![CDATA[<p>Es gibt kaum noch ein Menü, welches den Nutzer nicht mit einem <strong>Mouseover-Status</strong> über seine aktuelle Position informiert. Soweit so gut. Meistens sieht es so aus: Der Nutzer klickt auf den Menüpunkt, der Mouseover-Status verschwindet, die Seite verschwindet, die Seite baut sich wieder auf und der eben geklickte Menüpunkt ist dauerhaft hervorgehoben.</p>
<p>Ganz normal. Aber: Mit ein bisschen jQuery lässt sich der Mouseover-Status schon <strong>bei einem Klick festsetzen</strong>. Das sieht gut aus und wirkt fast wie ein dynamisches nachladen der Seite.</p>
<p>Ansehen kann man sich diesen Effekt zum Beispiel hier im Hauptmenü oder auf der Seite von <a title="Isabel von Roon" href="http://www.freies-herz.de" target="_blank">Isabel von Roon</a>. Wie der Effekt eingebaut wird, was <strong>find()</strong>, <strong>filter()</strong> und <strong>siblings()</strong> bedeutet und wie man den aktiven Menüpunkt hervorhebt wird in diesem Artikel besprochen.</p>
<p><span id="more-554"></span></p>
<h2>Die HTML Grundage</h2>
<p>Das oben genannte Beispiel ist zu simpel, deshalb machen wir es etwas komplizierter undzwar mit einem verschachtelten Menü.</p>
<pre><code class='html'>&lt;div id="menu"&gt;<br />
&lt;ul&gt;<br />
&lt;li&gt;&lt;a href="#"&gt;Punkt 1&lt;/a&gt;&lt;/li&gt;<br />
&lt;li&gt;<br />
&lt;a href="#"&gt;Punkt 2&lt;/a&gt;<br />
&lt;ul&gt;<br />
&lt;li&gt;&lt;a href="#"&gt;Punkt 2.1&lt;/a&gt;&lt;/li&gt;<br />
&lt;li&gt;&lt;a href="#"&gt;Punkt 2.2&lt;/a&gt;&lt;/li&gt;<br />
&lt;li&gt;&lt;a href="#"&gt;Punkt 2.3&lt;/a&gt;&lt;/li&gt;<br />
&lt;/ul&gt;<br />
&lt;/li&gt;<br />
&lt;li&gt;<br />
&lt;a href="#"&gt;Punkt 3&lt;/a&gt;<br />
&lt;ul&gt;<br />
&lt;li&gt;&lt;a href="#"&gt;Punkt 3.1&lt;/a&gt;&lt;/li&gt;<br />
&lt;li&gt;&lt;a href="#"&gt;Punkt 3.2&lt;/a&gt;&lt;/li&gt;<br />
&lt;li&gt;&lt;a href="#"&gt;Punkt 3.3&lt;/a&gt;&lt;/li&gt;<br />
&lt;li&gt;&lt;a href="#"&gt;Punkt 3.4&lt;/a&gt;&lt;/li&gt;<br />
&lt;/ul&gt;<br />
&lt;/li&gt;<br />
&lt;li&gt;&lt;a href="#"&gt;Punkt 4&lt;/a&gt;&lt;/li&gt;<br />
&lt;/ul&gt;<br />
&lt;/div&gt;</code></pre>
<p>Die Funktionsweise ist klar: Bei einem Klick ins Menü darf sich nur das jeweilige Menü verändern (die Nachbarn). Einfach die Aktiv-Klasse generell zu entfernen und dem angeklickten Menüpunkt zuweisen hätte zur Folge, dass beim Klick ins Submenü der darüber liegende Punkt seine Aktiv-Klasse verlieren würde.</p>
<p><a title="Pauls Blog jQuery Demo Menü" href="http://interaktionsdesigner.de/stuff/mainmenu.php" target="_blank">Demo: Hier ansehen was ich meine</a>.</p>
<h2>Lovley jQuery</h2>
<p>Wer jetzt an ein kompliziertes und umfangreiches Script denkt, der hat noch nicht genug mit jQuery gearbeitet. Denn die Anforderungen benötigen nur <strong>eine einzige Zeile</strong>:</p>
<pre><code class='javascript'>jQuery(function($) {<br />
&nbsp;&nbsp;$('#menu a').click(function() {<br />
&nbsp;&nbsp;  $(this).blur().parents('li').siblings('li:has(.active)').find('a').removeClass('active').end().end().end().addClass('active');<br />
&nbsp;&nbsp;  return false;<br />
&nbsp;&nbsp;});<br />
});</code></pre>
<p>Was hier passiert ist wird jetzt Stück für Stück durchleuchtet:</p>
<p><strong>jQuery(function($) {})</strong><br />
Diese Funktion wird aufgerufen, sobald das DOM fertig geladen wurde. Als Parameter wird das jQuery-Framework übergeben und in der Variable <strong>$</strong> gespeichert. Mit dieser Schreibweise hat man weniger Stress, wenn ein Kollege auf die Idee kommt ein zweites Javascript Framework einzubinden (<em>Grrr...</em>)</p>
<p><strong>$('#menu a').click(function() {})</strong><br />
Mit dieser Anweisung werden alle Links im Element mit der ID <em>Menü</em> selektiert und jeder einzelne bekommt einen Eventhandler zugewiesen: <em>click</em>.</p>
<p>In der Funktion ist der angeklickte Link in der Variable <em>this</em> gespeichert.</p>
<p><strong>$(this)</strong><br />
Mit diesem Konstrukt wird der angeklickte Menüpunkt in ein jQuery-Objekt verwandelt, und liegt als einziges auf dem Elementstack.</p>
<p><strong>.blur()</strong><br />
entfernt den gepunkteten Rahmen um den Link. Eine alternative Möglichkeit wäre <strong>outline:none</strong> im CSS anzugeben. Je nach belieben.</p>
<p><strong>.parents('li')</strong><br />
Jetzt wird es spannend! Diese Funktion wählt alle Elternelemente aus. Mit der Einschränkung, dass sie vom Typ <em>li</em> sind.</p>
<p><strong>.siblings('li:has(.active)')</strong><br />
Siblings steht für Nachbarn. Diese Funktion findet nur die benachbarten Elemente. Klickt man also auf ein Untermenü, befinden sich nach <strong>siblings()</strong> alle LI Elemente <em>dieser</em> Liste im Stack.</p>
<p>Mit der Übergabe eines Selectors kann man die Nachbarn auch gleich einschränken. <strong>li:has('.active')</strong> steht für alle LI Elemente welche ein Objekt der Klasse<em> .active</em> beinhalten.</p>
<p>Alternativ könnte man auch die Funktion <strong>filter()</strong> benutzen. Mit <strong>siblings('li')</strong> werden alle benachbarten LI Elemente ausgewählt. Anschließend mit <strong>filter(':has('.active')')</strong> auf das enthalten sein eines aktiven Links geprüft. Und genau hier liegt der Unterschied zwischen filter() und find():</p>
<p><strong>Filter()</strong> <em>filtert</em> die Elemente im Stack. <strong>Find()</strong> <em>sucht</em> innerhalb der Elemente im Stack. Logisch, oder?</p>
<p>Mit <strong>siblings('li:has(.active)') </strong>sparen wir uns eine Anweisung und im Stack befinden sich alle benachbarten Elemente, die einen aktiven Link beinhalten. Weiter gehts.</p>
<p><strong>.find('a')</strong><br />
Innerhalb des LI Elements wird jetzt nach dem aktiven Link gesucht und dieser ausgewählt.</p>
<p><strong>.removeClass('active')</strong><br />
Entfernt die Klasse von dem Link. (Bitte daran denken keinen Punkt vor der Klasse anzugeben!)</p>
<p><strong>.end()</strong><br />
Mit der Funktion <strong>end()</strong> wird eine Veränderung im Elementstack rückgängig gemacht. Dabei bezieht sich das <strong>end()</strong> immer auf die als <em>letztes getätigten Veränderungen</em>.</p>
<p>Das erste <strong>end()</strong> macht also das <strong>find('a')</strong> rückgängig, im Stack befinden sich jetzt wieder die LI Elemente mit einem (ehemals) aktiven Link.</p>
<p><strong>.end()</strong><br />
Als nächstes werden die Nachbarn aus dem Stack geworfen. (<em>siblings</em>)</p>
<p><strong>.end()</strong><br />
Und jetzt noch die Elternelemente raus und wieder zurück zum Kind: <strong>$(this)</strong>. Wir befinden uns jetzt wieder ganz am Anfang der Auswahl und es bleibt nur noch eine Funktion:</p>
<p><strong>.addClass('active')</strong><br />
Diese Funktion weißt dem angeklickten Link die passende Klasse zu, damit beim nächsten Klick das ganze Spiel wieder von vorne beginnen kann.</p>
<h2>Das wars!</h2>
<p>Im Beispiel habe ich noch ein <strong>return false;</strong> hinzugefügt. Diese Anweisung verhindert, dass der Browser sein Standardverhalten abspielt (bei Links die angeklickte Seite aufrufen). In einer wirklichen Umgebung darf das natürlich nicht vorkommen.</p>
<p>Ich hoffe ihr konntet etwas aus diesem Artikel mitnehmen. Weitere jQuery <strong>Fragen</strong> und <strong>Beispiellösungen</strong> stelle ich gerne auf Anfrage vor. Nur rein in die Kommentare.</p>
<p>Frohes verketten!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/07/27/nette-erweiterung-fur-jedes-menu-mit-jquery/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Viele Elemente dynamisch aktualisieren</title>
		<link>http://www.interaktionsdesigner.de/2009/07/24/viele-elemente-dynamisch-aktualisieren/</link>
		<comments>http://www.interaktionsdesigner.de/2009/07/24/viele-elemente-dynamisch-aktualisieren/#comments</comments>
		<pubDate>Fri, 24 Jul 2009 18:10:57 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=555</guid>
		<description><![CDATA[Ein aktuelles Projekt interagiert auf sehr starke Art und Weise mit dem Benutzer. Das bedeutet neben einer anpassbaren Oberfläche wird der Benutzer ständig über den Status diverser Objekte auf dem laufenden gehalten. Dabei können sich verschiedene Objekte untereinander beeinflussen.
jQuery bietet ja ein tolles Ajaxkonstrukt an mit dem es kein Problem ist Daten vom Server zu [...]]]></description>
			<content:encoded><![CDATA[<p>Ein aktuelles Projekt interagiert auf sehr starke Art und Weise mit dem Benutzer. Das bedeutet neben einer anpassbaren Oberfläche wird der Benutzer <strong>ständig über den Status</strong> diverser Objekte auf dem laufenden gehalten. Dabei können sich verschiedene Objekte untereinander beeinflussen.</p>
<p>jQuery bietet ja ein tolles Ajaxkonstrukt an mit dem es kein Problem ist Daten vom Server zu holen. Aber auf die Dauer wird es doch etwas nervig immer wieder zu tippen.</p>
<p>Und weil <strong>"objektorientiertes Javascript"</strong> und selber entwickeln so viel <strong>Spaß</strong> macht, habe ich mir eine alternative Lösung einfallen lassen: Im Projekt werden an geeigneter Stelle <em>Elemente</em> erzeugt, die alle benötigten Informationen enthalten.</p>
<p>Diese werden in einem <em>Updater</em> registriert, welcher sich um die regelmäßige Abfrage kümmert. Das war die Theorie und jetzt kommt die <strong>Praxis</strong>.</p>
<p><span id="more-555"></span></p>
<h2>Das Element</h2>
<p>Ein Element besteht aus <strong>einer URL</strong> auf dem Server, <strong>dem Typ</strong> der zurückerwartenden Daten und <strong>einer Funktion</strong> die aufgerufen wird, wenn die Aktion erfolg hatte. Im Quelltext sieht das so aus:</p>
<pre><code class='javascript'>var Paul = new element('count/paul', 'json', function(j) {<br />
&nbsp;&nbsp;alert("Paul hat "+j.count+" Einträge.");<br />
});</code></pre>
<p>Diese Elemente können überall im Projekt erzeugt werden. Alternativen zu <em>json</em> sind alle Formen die jQuerys Ajaxfunktion erkennt: <em>Html</em>, <em>Text</em> und <em>Jsonp</em>.</p>
<h2>Der Updater</h2>
<p>Ganz am Anfang (oder am Ende, je nach Geschmack) wird der Updater initalisiert:</p>
<pre><code class='javascript'>var Updater = new updater();</code></pre>
<p>Der Updater akzeptiert zwei Einstellungen:</p>
<pre><code class='javascript'>Updater.intervall = 1000; // Zeit bis zur nächsten Abfrage<br />
Updater.baseurl = "http://kleiner.test.com/"; // String der vor jede URL geschrieben wird</code></pre>
<p>Und hat eine handvoll Funktionen für den dynamischen Umgang mit dem Objekt:</p>
<pre><code class='javascript'>Updater.start(); // Startet den Vorgang sofern Elemente vorhanden sind<br />
Updater.stop(); // Stoppt die Aktualisierungen<br />
Updater.isRunning(); // Prüft ob sich der Updater in Aktion befindet<br />
Updater.log(); // Schreibt alle gespeicherten Elementen in die Konsole<br />
Updater.register(Element); // Fügt ein neues Element hinzu<br />
Updater.remove(Element); // Entfernt ein Element</code></pre>
<h2>Die Verwendung</h2>
<p>Dem Updater werden <strong>beliebig viele Elemente</strong>, auch zur Laufzeit, übergeben:</p>
<pre><code class='javascript'>Updater.register(Paul);<br />
Updater.start();</code></pre>
<p>Das wars! Die Funktion im übergebenen Element wird jetzt, im erfolgsfall, alle 1000 Millisekunden aufgerufen.</p>
<h2>Die technische Seite vom Element</h2>
<p>So sieht das Element aus:</p>
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><span style="color:#3f7bc6;">function</span> <span style="color:#4080ff;">element</span>(url, dataType, successFunction) {

  <span style="color:#e27600;">//einstellungen
</span>  <span style="color:#3f7bc6;">this</span>.url = url;
  <span style="color:#3f7bc6;">this</span>.data = {};
  <span style="color:#3f7bc6;">this</span>.type = <span style="color:#df2800;">'get'</span>
  <span style="color:#3f7bc6;">this</span>.dataType = dataType || <span style="color:#df2800;">'html'</span>;

  <span style="color:#e27600;">//callback um die daten zu bekommen
</span>  <span style="color:#3f7bc6;">this</span>.getData = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">this</span>.data;
  }

  <span style="color:#e27600;">//callback um an die URL zu kommen
</span>  <span style="color:#e27600;">//erster parameter ist die im Updater gespeicherte baseurl
</span>  <span style="color:#3f7bc6;">this</span>.getUrl = <span style="color:#3f7bc6;">function</span>(BASEURL) {
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">this</span>.url;
  }

  <span style="color:#e27600;">//callback wenn eine antwort vorliegt
</span>  <span style="color:#e27600;">//erster Parameter ist die Antwort vom Server, zweiter der Updater an sich
</span>  <span style="color:#3f7bc6;">this</span>.success = successFunction || <span style="color:#3f7bc6;">function</span>(html, Updater) {
    console.<span style="color:#4080ff;">log</span>(<span style="color:#df2800;">'Anfrage #'</span>+Updater.counter);
    console.<span style="color:#4080ff;">log</span>(html);
  }

  <span style="color:#e27600;">//callback wenn ein fehler aufgetreten ist
</span>  <span style="color:#e27600;">//erster parameter ist der Updater an sich um dieses Element daraus zu entfernen
</span>  <span style="color:#3f7bc6;">this</span>.error = <span style="color:#3f7bc6;">function</span>(Updater) {
    <span style="color:#e27600;">//bei einem fehler entfernt sich das objekt selbstständig aus dem updater
</span>    Updater.<span style="color:#4080ff;">remove</span>(<span style="color:#3f7bc6;">this</span>);
  }

}</pre>
<p>Zwei Funktionen sind wichtig: <strong>getData()</strong> wird aufgerufen, bevor die Anfrage an den Server gesendet wird. Der Rückgabewert der Funktion wird an den Server geschickt.</p>
<p>Damit ist es also möglich, auf <strong>Veränderungen im Frontend</strong> zu reagieren. Zum Beispiel:</p>
<pre><code class='javascript'>Paul.getData = function() {<br />
&nbsp;&nbsp;return {relation: $('body').attr('rel'), project: $('#project_selector').val()};<br />
}</code></pre>
<p>Schön, oder? Natürlich kann man sich noch viel einfallen lassen. Etwa mit der URL, welche mit Hilfe der Funktion <strong>getUrl()</strong> aus dem Element geholt wird. Es soll ja Fälle geben (<em>CakePHP</em>), bei denen es leichter ist Daten per URL zu übergeben:</p>
<pre><code class='javascript'>Paul.getUrl = function(BASEURL) {<br />
&nbsp;&nbsp;return BASEURL+this.url+'/'+$('#team .paul').attr('rel');<br />
}</code></pre>
<p>Die Funktionen <strong>success(return, Updater)</strong> und <strong>error(Updater)</strong> erklären sich von selbst und haben als Parameter immer das umfassende Element dabei. Deshalb kann sich das Objekt in einem Fehlerfall auch selbstständig entfernen. Es könnte auch das Intervall verändern, ein neues Objekt hinuzfügen, oder was auch immer!</p>
<h2>Die technische Seite vom Updater</h2>
<p>Als erstes der gesamte Block Quelltext:</p>
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><span style="color:#3f7bc6;">function</span> <span style="color:#4080ff;">updater</span>() {

  <span style="color:#e27600;">//einstellungen
</span>  <span style="color:#3f7bc6;">this</span>.intervall = <span style="color:#fe2d00;">1000</span>;
  <span style="color:#3f7bc6;">this</span>.elements = <span style="color:#3f7bc6;">new</span> <span style="color:#4080ff;">Array</span>();
  <span style="color:#3f7bc6;">this</span>.active_element = <span style="color:#fe2d00;">0</span>;
  <span style="color:#3f7bc6;">this</span>.baseurl = BASEURL;

  <span style="color:#3f7bc6;">this</span>.running = <span style="color:#3f7bc6;">false</span>;
  <span style="color:#3f7bc6;">this</span>.counter = <span style="color:#fe2d00;">0</span>;
  <span style="color:#3f7bc6;">this</span>.timeout = <span style="color:#df2800;">''</span>;

  <span style="color:#e27600;">//alle elemente anzeigen
</span>  <span style="color:#3f7bc6;">this</span>.log = <span style="color:#3f7bc6;">function</span>() {
    console.<span style="color:#4080ff;">log</span>(<span style="color:#3f7bc6;">this</span>.elements);
  }

  <span style="color:#e27600;">//neues element hinzufügen
</span>  <span style="color:#3f7bc6;">this</span>.register = <span style="color:#3f7bc6;">function</span>(element) {
    <span style="color:#3f7bc6;">this</span>.elements.<span style="color:#4080ff;">push</span>(element);
  };

  <span style="color:#e27600;">//ein element entfernen
</span>  <span style="color:#3f7bc6;">this</span>.remove = <span style="color:#3f7bc6;">function</span>(element) {
    <span style="color:#3f7bc6;">for</span>(i <span style="color:#3f7bc6;">in</span> <span style="color:#3f7bc6;">this</span>.elements) {
      <span style="color:#3f7bc6;">if</span>(<span style="color:#3f7bc6;">this</span>.elements[i] == element) {
        <span style="color:#e27600;">//delete this.elements[i];
</span>        <span style="color:#3f7bc6;">this</span>.elements.<span style="color:#4080ff;">splice</span>(i, <span style="color:#fe2d00;">1</span>);
        <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">true</span>;
      }
    }
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">false</span>;
  }

  <span style="color:#e27600;">//elemente durchgehen
</span>  <span style="color:#3f7bc6;">this</span>.process = <span style="color:#3f7bc6;">function</span>(active_element) {
    <span style="color:#3f7bc6;">this</span>.active_element = active_element || <span style="color:#fe2d00;">0</span>;

    <span style="color:#3f7bc6;">if</span>(<span style="color:#3f7bc6;">this</span>.elements[<span style="color:#3f7bc6;">this</span>.active_element]) {
      _this = <span style="color:#3f7bc6;">this</span>;
      _elem = <span style="color:#3f7bc6;">this</span>.elements[<span style="color:#3f7bc6;">this</span>.active_element];

      <span style="color:#e27600;">//ajaxaufruf
</span>      $.<span style="color:#4080ff;">ajax</span>({
        <span style="color:#e27600;">//einstellungen
</span>        url: _elem.<span style="color:#4080ff;">getUrl</span>(_this.baseurl),
        data: _elem.<span style="color:#4080ff;">getData</span>(),
        type: _elem.type,
        dataType: _elem.dataType,

        <span style="color:#e27600;">//hat funktioniert!
</span>        success: <span style="color:#3f7bc6;">function</span>(result) {
          _elem.<span style="color:#4080ff;">success</span>(result, _this);
        },

        <span style="color:#e27600;">//ein fehler
</span>        error: <span style="color:#3f7bc6;">function</span>(error) {
          <span style="color:#e27600;">//das object soll entscheiden wie es weitergeht
</span>          _elem.<span style="color:#4080ff;">error</span>(_this);
        },

        <span style="color:#e27600;">//wie auch immer soll das nächste element aufgerufen werden
</span>        complete: <span style="color:#3f7bc6;">function</span>() {
          _this.<span style="color:#4080ff;">next</span>();
        }
      });     

    }
    <span style="color:#3f7bc6;">else</span> {
      <span style="color:#3f7bc6;">this</span>.active_element = <span style="color:#fe2d00;">0</span>;
      <span style="color:#3f7bc6;">this</span>.<span style="color:#4080ff;">next</span>();
    }
  };

  <span style="color:#e27600;">//ruft process erneut auf, verzögert um intervall mit dem activen element
</span>  <span style="color:#3f7bc6;">this</span>.next = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">if</span>(<span style="color:#3f7bc6;">this</span>.<span style="color:#4080ff;">isRunning</span>()) {
      <span style="color:#e27600;">//nur so zum spaß
</span>      <span style="color:#3f7bc6;">this</span>.counter++;

      <span style="color:#e27600;">//zeit die bis zum nächsten aufruf vergehen soll
</span>      <span style="color:#3f7bc6;">var</span> _intervall = <span style="color:#fe2d00;">0</span>;

      <span style="color:#e27600;">//nächstes element suchen
</span>      <span style="color:#3f7bc6;">this</span>.active_element++;

      <span style="color:#e27600;">//schon am ende angelangt?
</span>      <span style="color:#3f7bc6;">if</span>(<span style="color:#3f7bc6;">this</span>.active_element &gt;= <span style="color:#3f7bc6;">this</span>.elements.length) {
        <span style="color:#3f7bc6;">this</span>.active_element = <span style="color:#fe2d00;">0</span>;

        <span style="color:#e27600;">//längerer intervall bis es wieder los geht
</span>        _intervall = <span style="color:#3f7bc6;">this</span>.intervall;
      }

      <span style="color:#e27600;">//erneut aufrufen
</span>      _this = <span style="color:#3f7bc6;">this</span>;
      _this.timeout = window.<span style="color:#4080ff;">setTimeout</span>(<span style="color:#3f7bc6;">function</span>() {
        _this.<span style="color:#4080ff;">process</span>(_this.active_element);
      }, _intervall);
      <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">true</span>;
    }
    <span style="color:#3f7bc6;">else</span> {
      <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">false</span>;
    }
  }

  <span style="color:#e27600;">//startet den vorgang
</span>  <span style="color:#3f7bc6;">this</span>.start = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">this</span>.active_element = -<span style="color:#fe2d00;">1</span>;
    <span style="color:#3f7bc6;">this</span>.running = <span style="color:#3f7bc6;">true</span>;
    <span style="color:#3f7bc6;">this</span>.counter = <span style="color:#fe2d00;">0</span>;
    <span style="color:#3f7bc6;">this</span>.<span style="color:#4080ff;">next</span>();
  }

  <span style="color:#e27600;">//stoppt das updaten
</span>  <span style="color:#3f7bc6;">this</span>.stop = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">this</span>.running = <span style="color:#3f7bc6;">false</span>;
    window.<span style="color:#4080ff;">clearTimeout</span>(<span style="color:#3f7bc6;">this</span>.timeout);
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">this</span>.counter;
  }

  <span style="color:#e27600;">//prüfen ob der Updater läuft
</span>  <span style="color:#3f7bc6;">this</span>.isRunning = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">this</span>.running;
  }
}</pre>
<p>Nett, oder? Was ist passiert? Die Einstellungen sollten klar sein. In der Variable <strong>timeout</strong> wird der aktuelle Timeout gespeichert. Damit kann man beim Abbrechen alle Verbindungen trennen, anstatt noch eine ausführen zu müssen.</p>
<p><strong>Log()</strong>, <strong>register()</strong> und <strong>remove()</strong> sind an sich uninteressant. Außer eine Erwähnung, dass der Operator <em>delete</em> ein Element zwar entfernt, aber danach nicht das Array neu durchnummeriert. Da liegen dann lauter "<em>undefinied</em>" rum. Deshalb habe ich <strong>splice()</strong> benutzt. Funktioniert super.</p>
<p>Spannend wird es in der Funktion <strong>process()</strong>. Diese wird für jedes Element aufgerufen. Als Parameter erwartet sie den <strong>Key</strong> vom aktiven Element. Wenn ein Element vorhanden ist, wird die Ajaxabfrage ausgeführt.</p>
<p>Innerhalb der Funktion Ajax() bekommt das <em>this</em> eine neue Bedeutung. Deshalb musste ich es in <em>_this</em> zwischenspeichern.</p>
<p>Die Funktion ruft also, je nach umstand, <strong>success()</strong> oder <strong>error()</strong> auf und übergibt die Kontrolle an das Element. In jedemfall wird aber <strong>_this.next()</strong> durch die Funktion <strong>Ajax.complete()</strong> aufgerufen. Diese wird, unabhängig vom Status, nach jeder Ajaxanfrage ausgeführt.</p>
<p>In der Funktion <strong>next()</strong> wird das nächste Objekt gesucht. Sofern der Updater läuft <strong>isRunning() </strong>wird der Counter erhöht und das Intervall auf Null gesetzt. Das passiert, weil alle registrierten Elemente sofort nacheinander aufgerufen werden sollen und erst anschließend die in <em>this.intervall</em> angegebene Zeit gewartet wird.</p>
<p>Wenn alle Elemente durchgearbeitet wurden, wird der <strong>Index auf Null</strong> und der <strong>Intervall auf die eingestellte Zeit</strong> gesetzt.</p>
<p>Anschließend gibt es ein klassisches <strong>window.setTimeout()</strong> um das ganze Spielchen wieder von vorne anzufangen.</p>
<p><strong>Start()</strong>, <strong>stop()</strong> und <strong>isRunning()</strong> erklären sich wieder von selbst, und damit ist die Vorstellung beendet!</p>
<h2>Fazit</h2>
<p>Der große nächste Erweiterungsschritt ist in dem Updater alle Anfragen zu sammeln und<strong> in einem Packet</strong> an den Server zu schicken. Die Antwort kann dann direkt vom Updater auseinander gefriemelt und an die verschiedenen Elemente verteilt werden.</p>
<p>Dazu muss der Server allerdings noch vorbereitet werden, und bisher sind es noch nicht viele Benutzer. Aber ich denke es ist eine <strong>solide Grundlage</strong> die gut erweitert werden kann.</p>
<p>Das war auch die Intention dieses Postings. Hast du einen <strong>Vorschlag</strong>? <strong>Kritik</strong>? <strong>Hinweis</strong>? Freue mich über Kommentare und Inspiration. Danke für die Aufmerksamkeit.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/07/24/viele-elemente-dynamisch-aktualisieren/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Das komplette HTML eines Elements mit jQuery auslesen</title>
		<link>http://www.interaktionsdesigner.de/2009/05/06/das-komplette-html-eines-elements-mit-jquery-auslesen/</link>
		<comments>http://www.interaktionsdesigner.de/2009/05/06/das-komplette-html-eines-elements-mit-jquery-auslesen/#comments</comments>
		<pubDate>Wed, 06 May 2009 12:42:27 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=454</guid>
		<description><![CDATA[Klingt komisch, ist aber ab und zu ganz nützlich. Worum geht es? Im Elementstack von jQuery liegt ein Element welches an einem anderen Ort wieder eingefügt werden soll. So sieht das HTML Element aus:
Natürlich wird das Element ausgewählt und das HTML mit der Funktion html ausgelesen.
Diese Abfrage liefert allerdings das hier:
Das ist nicht das gesamte [...]]]></description>
			<content:encoded><![CDATA[<p>Klingt komisch, ist aber ab und zu ganz nützlich. Worum geht es? Im <strong>Elementstack von jQuery</strong> liegt ein Element welches an einem <strong>anderen Ort</strong> wieder eingefügt werden soll. So sieht das <em>HTML Element</em> aus:</p>
<pre><code class='html'>&lt;a href="#" id="ein-interner-link"&gt;Ein &lt;span&gt;wichtiger&lt;/span&gt; Link&lt;/a&gt;</code></pre>
<p>Natürlich wird das Element ausgewählt und das HTML mit der Funktion <a title="jQuery Dokumentation zu html()" href="http://docs.jquery.com/Html" target="_blank">html</a> ausgelesen.</p>
<pre><code class='javascript'>$("#ein-interner-link").html();</code></pre>
<p>Diese Abfrage liefert allerdings das hier:</p>
<pre><code class='html'>Ein &lt;span&gt;wichtiger&lt;/span&gt; Link</code></pre>
<p>Das ist nicht das gesamte HTML, sondern nur das <strong>innerHTML</strong>. Im folgenden Artikel gibt es<strong> die Lösung</strong>. Oder direkt zur <a title="entireHtml 2.0" href="http://www.interaktionsdesigner.de/2009/09/23/entirehtml-2-0/">verbesserten Lösung</a>.<span id="more-454"></span></p>
<h2>Der komplette HTML Code</h2>
<p>Das zu selektierende Element wird <strong>mit einem Container umschlossen</strong>, dieser wird ausgewählt und dann befindet sich in der <em>innerHTML</em> <em>Eigenschaft</em> der komplette HTML Code des Elements.</p>
<p>So sieht es dann mit jQuery aus:</p>
<pre><code class='javascript'>$("#ein-interner-link").wrap("&lt;div&gt;&lt;/div&gt;").parent().html();</code></pre>
<p>Das liefert zwar den korrekten HTML Code, hat allerdings zwei Probleme:</p>
<p><strong>1. Der Ursprungslink befindet sich jetzt in einem DIV Container</strong>. Wenn diese Funktion mehrfach angewendet wird, hat man eine ganze Reihe DIVs um den Link.</p>
<p><strong>Die Lösung</strong> ist wieder einfach: Vor dem umschließen wird das Element kopiert:</p>
<pre><code class='javascript'>$("#ein-interner-link").clone().wrap("&lt;div&gt;&lt;/div&gt;").parent().html();</code></pre>
<p><strong>2. Die Lösung ist so unelegant.</strong> Und wenig jQuery-like. Wenn jemand einen besseren Weg kennt, dann her damit. Bis dahin habe ich die Funktionalität in das Plugin <strong>entireHtml</strong> ausgelagert:</p>
<pre><code class='javascript'>(function($) {<br />
&nbsp;&nbsp;jQuery.fn.entireHtml = function() {<br />
&nbsp;&nbsp;[tab]return this.clone().wrap('&lt;div&gt;&lt;/div&gt;').parent().html();<br />
&nbsp;&nbsp;}<br />
})(jQuery);</code></pre>
<p>Damit sieht es netter aus und wenn der rettende, alles erklärende Kommentar auftaucht, ist eine Veränderung nur noch an einer Stelle notwendig. So sieht die Anwendung dann aus:</p>
<pre><code class='javascript'>$("#ein-interner-link").entireHtml();</code></pre>
<p>Das ist doch wieder ganz nett. <strong>Was meinst du?</strong></p>
<p><strong><a title="entireHtml 2.0" href="http://www.interaktionsdesigner.de/2009/09/23/entirehtml-2-0/">Inzwischen gibt es eine verbesserte Lösung in Pauls Blog.</a><br />
</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/05/06/das-komplette-html-eines-elements-mit-jquery-auslesen/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Mit jQuery und CakePHP voneinander abhängige Selectboxen</title>
		<link>http://www.interaktionsdesigner.de/2009/04/11/mit-jquery-und-cakephp-voneinander-abhangige-selectboxen/</link>
		<comments>http://www.interaktionsdesigner.de/2009/04/11/mit-jquery-und-cakephp-voneinander-abhangige-selectboxen/#comments</comments>
		<pubDate>Sat, 11 Apr 2009 09:30:17 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=417</guid>
		<description><![CDATA[CakePHP ist genial, nicht nur was die Erstellung von Formularen angeht. Aber leider setzt der integrierte AjaxHelper Prototype voraus und als überzeugter jQuery-Nutzer kommt das für mich nicht in Frage.
Nun gibt es aber beim aktuellen Projekt folgendes Problem: Einem Projekt wird ein Kunde zugeordnet und jedes Projekt kriegt einen Ansprechpartner. Dieser Ansprechpartner ist in der [...]]]></description>
			<content:encoded><![CDATA[<p><strong>CakePHP ist genial</strong>, nicht nur was die Erstellung von Formularen angeht. Aber leider setzt der integrierte AjaxHelper Prototype voraus und als <strong>überzeugter jQuery-Nutzer</strong> kommt das für mich nicht in Frage.</p>
<p>Nun gibt es aber beim aktuellen Projekt folgendes Problem: Einem Projekt wird ein Kunde zugeordnet und jedes Projekt kriegt einen Ansprechpartner. Dieser Ansprechpartner ist in der User-Datenbank gespeichert und besitzt eine ID zu einem Kunden.</p>
<p>Legt man ein neues Projekt an und wählt einen Kunden aus, sollen nur noch die passenden Ansprechpartner angezeigt werden.</p>
<p><strong>Nichts leichter als das!</strong> Hier stelle ich meine sehr flexible Möglichkeit für die Umsetzung mit <strong>jQuery</strong> und <strong>CakePHP</strong> vor. <em>Grundwissen allerdings vorrausgesetzt.</em><span id="more-417"></span></p>
<h2>Vorbereitungen</h2>
<p><em>(Alle CakePHP Pfadangaben beziehen sich auf den </em><em>app/ Ordner)</em><br />
<strong>jQuery muss eingebunden werden</strong>. Dafür will ich den <strong>Ajaxhelper</strong> benutzen. Da das gesamte Projekt mit jQuery arbeiten soll, habe ich es für alle Controller auf einen Schlag eingebunden, und zwar in der Datei <em>app_controller.php</em>.</p>
<pre><code class='php'>class AppController extends Controller {<br />
&nbsp;&nbsp;var $helpers = array('Ajax');<br />
}<br />
</code></pre>
<p>Die Javascriptdateien kommen in den Ordner <em>/webroot/js/</em> und sind anschließend im<strong> Default Layout</strong> (oder welchem auch immer) in der Datei <em>views/layouts/default.ctp</em> schnell eingebunden:</p>
<pre><code class='php'>echo $javascript-&gt;link(<br />
&nbsp;&nbsp;array(<br />
&nbsp;&nbsp;[tab]'jquery.min.js',<br />
&nbsp;&nbsp;[tab]'jquery.action.js',<br />
&nbsp;&nbsp;)<br />
);</code></pre>
<p>Damit der absolute Pfad der Anwendung in der JavaScript Datei zur Verfügung steht, wird im Layout das Tag <strong>&lt;base&gt;</strong> gesetzt. Das wird dann ausgelesen:</p>
<pre><code class='html'>&lt;base href="http://&lt;?=$_SERVER['HTTP_HOST']?&gt;&lt;?=$this-&gt;base?&gt;/" /&gt;</code></pre>
<h2>Das Formular</h2>
<p>Um für das neue Anlegen und das Bearbeiten von Projekten nicht zwei Formulare pflegen zu müssen, habe ich das Formular in ein <strong>Element</strong> ausgelagert, es befindet sich jetzt in der Datei <em>views/elements/projectform.ctp</em> und wird im View <em>add.ctp</em> und <em>edit.ctp</em> mit dem Befehl</p>
<pre><code class='php'>echo $this-&gt;element("projectform");</code></pre>
<p>eingebunden. Die Datei selbst besteht, gekürzt, aus folgenden Zeilen:</p>
<pre><code class='php'>$c = array('') + $customers;<br />
echo $form-&gt;input('customer_id', array(<br />
&nbsp;&nbsp;'class' =&gt; 'relation',<br />
&nbsp;&nbsp;'rel' =&gt; 'ProjectContact',<br />
&nbsp;&nbsp;'data' =&gt; 'users|customer_id',<br />
&nbsp;&nbsp;'options' =&gt; $c<br />
));<br />
echo $form-&gt;input('contact', array(<br />
&nbsp;&nbsp;'options' =&gt; $contacts<br />
));</code></pre>
<p>In der ersten Zeile wird das Array <strong>$customers</strong>, welches im Controller gesetzt wurde, um ein leeres Element am Anfang erweitert. Als nächstes folgen ein paar Erweiterungen für das zu erzeugende HTML Element auf die mit jQuery dynamisch zugegriffen wird.</p>
<p>'class' =&gt; '<strong>relation</strong>' ist der grundlegende Hinweis für jQuery das hier was passieren soll.<br />
'rel' =&gt; '<strong>ProjectContact</strong>' bezeichnet die ID des Selectors welches die dynamischen Inhalte darstellt.<br />
'data' =&gt; '<strong>users|customer_id</strong>' enthält zwei Teile: <strong>users</strong> steht für das Model aus dem die Daten gezogen werden, <strong>customer_id</strong> ist die Spalte die mit dem Wert dieser Selectbox verglichen wird.<br />
'options' =&gt; <strong>$c</strong> weißt dann nur noch das neue Array dem Element hinzu.</p>
<h2>jQuery Action</h2>
<p>Weiter gehts in der Datei <em>webroot/js/jquery.action.js</em>. Als erstes muss die Baseurl ausgelesen werden.</p>
<pre><code class='javascript'>if($("base").length == 1)<br />
&nbsp;&nbsp;BASEURL = $("base").attr("href");<br />
else {<br />
&nbsp;&nbsp;alert("Keine BaseUrl gefunden!!!");<br />
}</code></pre>
<p>Als nächstes kommt die <strong>geballte Action für die Selectbox</strong>. Erstmal der ganze fette Block <strong>jQuery-Schönheit</strong> vorweg, anschließend Zeile für Zeile erklärt. Für weitere dynamische Selectboxen muss man diesen Code dann nicht mehr anpassen:</p>
<pre style="text-align:left;font-family:monospace; ">$(<span style="color:#fe2d00;">"select.relation"</span>).<span style="color:#4080ff;">each</span>(<span style="color:#3f7bc6;">function</span>() {
  <span style="color:#e27600;">//Vorhandene Elemente entfernen
</span>  target = <span style="color:#fe2d00;">"#"</span>+$(<span style="color:#3f7bc6;">this</span>).<span style="color:#4080ff;">attr</span>(<span style="color:#fe2d00;">"rel"</span>);
  $(target).<span style="color:#4080ff;">find</span>(<span style="color:#fe2d00;">"option"</span>).<span style="color:#4080ff;">remove</span>();

  <span style="color:#e27600;">//Eventhandler binden
</span>  $(<span style="color:#3f7bc6;">this</span>).<span style="color:#4080ff;">change</span>(<span style="color:#3f7bc6;">function</span>() {
    $(target).<span style="color:#4080ff;">find</span>(<span style="color:#fe2d00;">"option"</span>).<span style="color:#4080ff;">remove</span>();

    <span style="color:#e27600;">//Ajaxanfrage abschicken
</span>    data = $(<span style="color:#3f7bc6;">this</span>).<span style="color:#4080ff;">attr</span>(<span style="color:#fe2d00;">"data"</span>).<span style="color:#4080ff;">split</span>(<span style="color:#fe2d00;">"|"</span>);
    val = $(<span style="color:#3f7bc6;">this</span>).<span style="color:#4080ff;">val</span>();
    <span style="color:#3f7bc6;">if</span>(val &gt; <span style="color:#fe2d00;">0</span>) {
      $.<span style="color:#4080ff;">ajax</span>({
        url: BASEURL+data[<span style="color:#fe2d00;">0</span>]+<span style="color:#df2800;">'/get/'</span>,
        data: {field: data[<span style="color:#fe2d00;">1</span>], value: val},
        dataType: <span style="color:#df2800;">'json'</span>,

        success: <span style="color:#3f7bc6;">function</span>(j) {
          $.<span style="color:#4080ff;">each</span>(j, <span style="color:#3f7bc6;">function</span>(i) {
            $(target).<span style="color:#4080ff;">append</span>(<span style="color:#df2800;">'&lt;option value="'</span>+i+<span style="color:#df2800;">'"&gt;'</span>+<span style="color:#3f7bc6;">this</span>+<span style="color:#df2800;">'&lt;/option&gt;'</span>);
          });
        },
        error: <span style="color:#3f7bc6;">function</span>(e) {
          <span style="color:#4080ff;">alert</span>(<span style="color:#fe2d00;">"Fehler bei der Ajaxanfrage! Siehe Konsole."</span>);
          console.<span style="color:#4080ff;">log</span>(e);
        }
      });
    }
  })
});</pre>
<p><strong>$("select.relation").each(function() {</strong> Jede Selectbox mit der Klasse <strong>relation</strong> dient als Auslöser und braucht spezielle Behandlung.<br />
In der Variable <strong>target</strong> wird die ID der zu ändernde Selectbox, für die spätere Verwendung gespeichert <strong>"#"+$(this).attr("rel");<br />
$(target).find("option").remove();</strong> entfernt die vorhandenen Einträge.</p>
<p>Jetzt wird der Selectbox ein Eventhandler zugewiesen, der ausgeführt wird, sobald sich der Inhalt der Selectbox ändert: <strong>$(this).change(function() {</strong>.<br />
Falls es die zweite Änderung ist, müssen vorhandene Inhalte entfernt werden <strong>$(target).find("option").remove();</strong> und per Ajax neue Inhalte geladen. Die Variable <strong>val</strong> speichert die aktuelle Auswahl und <strong>data</strong> beinhaltet die Informationen von weiter oben. Wenn eine gültige ID ausgewählt wurde, dann wird die Ajaxanfrage gesendet.</p>
<p><strong>url: BASEURL+data[0]+'/get/'</strong>, gesucht wird die <em>Funktion get</em> im Controller <strong>data[0]</strong> (im Beispiel = <em>users</em>).<br />
<strong>data: {field: data[1], value: val}</strong>, für die Datenabfrage wird <strong>field</strong> und <strong>value</strong> standardmäßig per GET übergeben<br />
und als Rückgabe ein JSON String erwartet <strong>dataType: 'json'</strong>.</p>
<p>Wenn die Anfrage erfolgreich war, wird diese Funktion ausgeführt: <strong>success: function(j) {</strong><br />
<strong>j</strong> beinhaltet das erhaltene JSON Array und wird Schritt für Schritt durchgegangen <strong>$.each(j, function(i) {</strong> um dem Zielelement <strong>target</strong> eine neue Option hinzuzufügen <strong>$(target).append('&lt;option value="'+i+'"&gt;'+this+'&lt;/option&gt;')</strong>;</p>
<p>Danach folgt noch ein <em>Errorhandler</em>, der je nach belieben gestalltet werden kann, im Großen und Ganzen aber uninteressant ist.</p>
<p>Das wars schon. Jetzt gehts in den Controller User unter<em> controllers/users_controller.php</em>.</p>
<h2>Im Controller</h2>
<p>In der URL habe ich die Funktion <strong>get</strong> genannt, also muss sie hier definiert werden:</p>
<pre><code class='php'>class UsersController extends AppController {<br />
&nbsp;&nbsp;function get() {<br />
&nbsp;&nbsp;}<br />
}</code></pre>
<p>Als erstes wird wie gewohnt eine <strong>Liste von Daten</strong> aus dem Model gezogen:</p>
<pre><code class='php'>$list = $this-&gt;User-&gt;find('list', array(<br />
&nbsp;&nbsp;'conditions' =&gt; array(<br />
&nbsp;&nbsp;[tab]'User.'.$this-&gt;params['url']['field'] =&gt; $this-&gt;params['url']['value']<br />
&nbsp;&nbsp;)<br />
));</code></pre>
<p>Und jetzt wird es spannend, mit Hilfe der <strong>RequestHandler</strong> Komponente, die in der Datei <em>app_controller.php</em> oder <em>constrollers/users_controller.php</em> eingebunden wird.</p>
<pre><code class='php'>var $components = array('RequestHandler');</code></pre>
<p>Jetzt steht das Objekt<strong> $this-&gt;RequestHandler</strong> zur Verfügung, mit dessen Hilfe z.B. abgefragt werden kann ob die ankommende Anfrage per Ajax gesendet wurde. Wenn dies der Fall ist, darf es <strong>keine Debugausgaben</strong> mehr geben und die Liste soll als <strong>JSON String </strong>ausgegeben werden. Und so versteht das auch CakePHP:</p>
<pre><code class='php'>if($this-&gt;RequestHandler-&gt;isAjax()) {<br />
&nbsp;&nbsp;Configure::write('debug', 0);<br />
&nbsp;&nbsp;$this-&gt;autoRender = false;<br />
&nbsp;&nbsp;return json_encode($list);<br />
}</code></pre>
<p>Fertig! jQuery kriegt jetzt von der Funktion einen JSON String zurück und kann diesen, wie beschrieben, in die Selectbox einfügen.</p>
<h2>Fazit</h2>
<p><em>Diese Lösung ist nur ein Vorschlag</em> und ich bin mir sicher das es viele verschiedene Lösungswege gibt. Wahnsinnig wichtig ist die Tatsache, <strong>dass hier kein bisschen auf die Sicherheit geachtet wurde</strong>. Dieses Beispiel sollte man <strong>nicht unverändert im Livebetrieb einsetzen</strong>!</p>
<p>Ich bin mir auch nicht sicher, ob der gewählte Weg der beste ist. Deshalb freue ich mich diesmal <strong>besonders</strong> über Kommentare mit Anmerkungen und Erweiterungen.</p>
<p><strong>Frohes backen!</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/04/11/mit-jquery-und-cakephp-voneinander-abhangige-selectboxen/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Layout mit 100 Prozent Höhe</title>
		<link>http://www.interaktionsdesigner.de/2009/03/31/layout-mit-100-prozent-hohe/</link>
		<comments>http://www.interaktionsdesigner.de/2009/03/31/layout-mit-100-prozent-hohe/#comments</comments>
		<pubDate>Tue, 31 Mar 2009 07:13:13 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=370</guid>
		<description><![CDATA[Diesmal geht es die Höhe von Div-Containern. Findige Designer haben sich ausgedacht ein Layout zu basteln, welches eine Fußzeile besitzt die immer am unteren Bildschirmrand klebt. Es sei denn der Inhalt ist größer, dann muss die Fußzeile mit runterrutschen.
Soweit kein Problem; vor einiger Zeit habe ich ein schönes Beispiel gefunden welches genau diese Problem löst, [...]]]></description>
			<content:encoded><![CDATA[<p>Diesmal geht es die <strong>Höhe von Div-Containern</strong>. Findige Designer haben sich ausgedacht ein Layout zu basteln, welches eine Fußzeile besitzt die <strong>immer am unteren Bildschirmrand</strong> klebt. Es sei denn der Inhalt ist größer, dann muss die Fußzeile mit runterrutschen.</p>
<p>Soweit kein Problem; vor einiger Zeit habe ich ein <a title="100% Höhe mit CSS" href="http://www.xs4all.nl/~peterned/examples/csslayout1.html" target="_self">schönes Beispiel</a> gefunden welches genau diese Problem löst, in allen Browsern und nur mit CSS. Was aber, wenn die Seite mit dem <a title="960.gs CSS Framework" href="http://www.960.gs" target="_blank"><strong>960 Grid System</strong> CSS Framework</a> aufgebaut wurde und nicht nur ein Footer hat sondern <strong>zwei</strong>?</p>
<p>Um genau zu sein, ist die Breite vom CSS Framework auf 960 Pixel beschränkt, der Footer mit Inhalten befindet sich innerhalb des umfassenden Containers, aber die Hintergrundgrafik soll <strong>über die gesamte Breite gehen</strong>.</p>
<p>Unmöglich? Niemals! Es folgt eine <strong>Skizze</strong>, der <strong>Lösungsansatz</strong> und ein paar Worte über das <strong>960.gs</strong>.<span id="more-370"></span></p>
<h2>Die Anforderung</h2>
<p><img class="alignright size-full wp-image-379" title="100% Höhe mit CSS" src="http://www.interaktionsdesigner.de/wp-content/uploads/2009/03/100hoehe-1.jpg" alt="100% Höhe mit CSS" width="550" height="424" />Diese Skizze soll das Problem verdeutlichen. Der weiße Bereich ist der Inhalt welcher sich in der Höhe ausdehnt und den gesamten Platz ein nimmt.</p>
<p>Header und Footer sollen aber über die gesamte Breite gehen, wobei die Inhalte sich aber am Content orientieren müssen da die gesamte Seite zentriert ist.</p>
<p>"<em>Viel Spaß</em>" hat der Designer gesagt, hatte ich auch!</p>
<h2>Das 960.gs</h2>
<p><strong>CSS Frameworks</strong> sind in aller Munde und es lassen sich damit wirklich schnell und einfach Layouts aufbauen. Ich habe mich in diesem Projekt gegen das kompliziertere, aber dafür flexiblere <a title="YAML ist auch ein CSS Framework" href="http://www.yaml.de" target="_blank">YAML</a> entschieden weil es mir überdimensioniert erschien.</p>
<p>Im 960.gs entscheidet man sich für ein <strong>12-</strong> oder <strong>16-spaltiges</strong> Layout und benennt, je nach dem, den umfassenden Container:<br />
<pre><code class='html'>&lt;div class="container_16"&gt;&lt;/div&gt;</code></pre><br />
Anschließend hat man innerhalb mit den Klassen <strong>grid_1</strong> bis <strong>grid_16</strong> (bzw. grid_12) beliebige Möglichkeiten zur Steuerung der Inhalte. Auch die <strong>Integration in TYPO3</strong> ist natürlich überhaupt kein Problem, mein Template sieht für das oben beschriebene Projekt so aus:</p>
<pre><code class="html">&lt;div class="outerwrap"&gt;
&nbsp;&nbsp;&lt;div class="container_16 wrap png_fix"&gt;
&nbsp;&nbsp;[tab]
&nbsp;&nbsp;[tab]&lt;!-- KOPF --&gt;
&nbsp;&nbsp;[tab]&lt;div class="grid_11 top"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;ul class="menu sf-menu"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;[tab]###MAIN_MENU###
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;/ul&gt;
&nbsp;&nbsp;[tab]&lt;/div&gt;
&nbsp;&nbsp;[tab]
&nbsp;&nbsp;[tab]&lt;div class="grid_4 top logo"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;###LOGO###
&nbsp;&nbsp;[tab]&lt;/div&gt;
&nbsp;&nbsp;[tab]
&nbsp;&nbsp;[tab]&lt;!-- TEASER (Bild) --&gt;
&nbsp;&nbsp;[tab]&lt;div class="grid_11 teaser left"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;###TEASER###
&nbsp;&nbsp;[tab]&lt;/div&gt;
&nbsp;&nbsp;[tab]
&nbsp;&nbsp;[tab]&lt;div class="grid_4 teaser right png_fix"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;###LOGIN###
&nbsp;&nbsp;[tab]&lt;/div&gt;

&nbsp;&nbsp;[tab]
&nbsp;&nbsp;[tab]&lt;!-- CONTENT --&gt;
&nbsp;&nbsp;[tab]&lt;div class="content_wrap"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;!-- ganze spalte --&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;div class="grid_11 left"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;[tab]&lt;div class="content"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;[tab]&nbsp;&nbsp;###CONTENT_LEFT###
&nbsp;&nbsp;[tab]&nbsp;&nbsp;[tab]&lt;/div&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;!-- Rechts vom Inhalt --&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;div class="grid_4 right png_fix"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;[tab]###CONTENT_RIGHT###
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;div class="clear"&gt;&lt;/div&gt;
&nbsp;&nbsp;[tab]&lt;/div&gt;
&nbsp;&nbsp;[tab]
&nbsp;&nbsp;[tab]
&nbsp;&nbsp;[tab]&lt;!-- FOOTER --&gt;
&nbsp;&nbsp;[tab]&lt;div class="footer_wrap"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;div class="grid_11 footer"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;[tab]###COPYRIGHT###
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;div class="grid_4 footer"&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;[tab]&lt;ul&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;[tab]&nbsp;&nbsp;###FOOTER_MENU###
&nbsp;&nbsp;[tab]&nbsp;&nbsp;[tab]&lt;/ul&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;
&nbsp;&nbsp;[tab]&nbsp;&nbsp;&lt;div class="clear"&gt;&lt;/div&gt;
&nbsp;&nbsp;[tab]&lt;/div&gt;
&nbsp;&nbsp;&lt;/div&gt;
&lt;/div&gt;</code></pre>
<p>Ein allgemeines Problem von CSS Frameworks wird in diesem Template auch gleich sichtbar: <strong>CSS Frameworks neigen zur Divirites</strong>. Dafür wird einem aber eine große Menge CSS-Nerv abgenommen. Hier bleibt kritisch zu betrachten wie viel Traffic die Seite zu erwarten hat und wie viel Zeit für die Realisierung veranschlagt wurde.</p>
<h2>100% Höhe Nr. 1</h2>
<p>Die kleinere Aufgabe besteht darin den <strong>Footer an das untere Ende der Seite</strong> zu kleben. Das geht mit dem Tutorial von <a title="100% Höhe mit CSS" href="http://www.xs4all.nl/~peterned/examples/csslayout1.html" target="_blank">xs4all</a> ganz leicht: Der Container <em>wrap</em> bekommt eine relative Position zugewiesen und eine<strong> Mindesthöhe von 100%</strong><em>.</em><br />
Der <em>footer_wrap</em> wird <strong>absolut positioniert</strong> und mit <em>bottom:0</em> an den unteren Rand vom <em>wrap</em> gelegt. Damit bei längerem Inhalt oder kleinem Browserfenster, der Inhalt nicht über den Footer reicht kriegt der Container <em>content_wrap</em> einen Innenabstand (<em>padding</em>) nach unten, welcher der Höhe vom <strong>Footer plus Abstand</strong> enthält. Das ist der Trick!</p>
<h2>100% Höhe Nr. 2</h2>
<p><img class="alignright size-full wp-image-345" title="Problem mit 100% Höhe" src="http://www.interaktionsdesigner.de/wp-content/uploads/2009/03/100hoehe-2.jpg" alt="Problem mit 100% Höhe" width="550" height="600" />Jetzt hat der Footer noch kein Hintergrundbild. Damit es über die gesamte Breite geht kriegt es der Container <em>outerwrap</em>. Der hat allerdings ein Problem: Bei 100% Höhe hängt der Balken immer am <strong>unteren Browserrand</strong>. Wenn die Inhalte länger sind, bleibt der Balken einfach stehen. Im Bild repräsentiert der schwarze Rahmen das Browserfenster.</p>
<p>Da im guten <strong>Standard Typoscript</strong> sowieso <strong>jQuery</strong> eingebunden ist und für diverse Zwecke genutzt wird, kann das Lieblingsframework hier treue Dienste leisten:</p>
<p>Die <strong>Besucher ohne JavaScript</strong> kriegen einfach keinen Balken zu sehen. Dem Container <em>outerwrap</em> wird das Hintergrundbild wieder genommen und statt dessen der Klasse <em>balken</em> zugewiesen. Im jQueryscript wird eine Funktion hinzugefügt: <em>fix_outerwrap_bug()</em>. <pre><code class='javascript'>function fix_outerwrap_bug() {<br />
&nbsp;&nbsp;$(".outerwrap")<br />
&nbsp;&nbsp;[tab].addClass("balken")<br />
&nbsp;&nbsp;[tab].css("height", $(document).height());<br />
}</code></pre><br />
Damit erhält der Container <em>outerwrap</em> die Klasse <em>balken</em> (sofern noch nicht vorhanden; das checkt jQuery von alleine) und die <strong>Höhe des Dokuments</strong> zugewiesen. Die Funktion muss beim laden der Seite aufgerufen werden:<br />
<pre><code class='javascript'>jQuery(function($) {<br />
&nbsp;&nbsp;fix_outerwrap_bug();<br />
});</code></pre><br />
Funktioniert einwandfrei, allerdings nur beim ersten laden der Seite. Der findige Internetbenutzer der die <strong>Größe des Browserfensters</strong> verändert kriegt Blödsinn zu sehen. Deshalb noch eine Zeile hinzufügen:<br />
<pre><code class='javascript'>$(window).resize(function() { fix_outerwrap_bug(); });</code></pre><br />
Eigentlich selbsterklärend: Bei Größenänderung (<em>resize()</em>) vom Fenster (<em>$(window)</em>) wird die Funktion <em>fix_outerwrap_bug()</em> aufgerufen. Das wars!</p>
<h2>Fazit</h2>
<p>Das es ohne JavaScript nicht funktioniert ist natürlich nicht ganz so schön, aber für eine Kleinigkeit im Design zu verkraften. Selber Schuld wer JavaScript deaktivert. <strong>Dank jQuery</strong> ist es ganz einfach und validieren tut es auch, warum also nicht?</p>
<p>Wer die vorgestellte <strong>Lösung in Aktion</strong> sehen will, dem sei ein Besuch auf der Seite <a title="ArminiusFunds" href="http://www.arminiusfunds.com" target="_blank">http://www.arminiusfunds.com</a> ans Herz gelegt. <strong>Viel Spaß </strong>und frohes investieren!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/03/31/layout-mit-100-prozent-hohe/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Animierte Header mit TYPO3 und jQuery</title>
		<link>http://www.interaktionsdesigner.de/2009/03/03/animierte-header-mit-typo3-und-jquery/</link>
		<comments>http://www.interaktionsdesigner.de/2009/03/03/animierte-header-mit-typo3-und-jquery/#comments</comments>
		<pubDate>Tue, 03 Mar 2009 11:23:45 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[TYPO3]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=296</guid>
		<description><![CDATA[Viele schöne Seiten präsentieren großflächige und hochauflösende Bilder. Natürlich wollen unsere Designer das auch und damit der geneigte Besucher Abwechslung bekommt und die Bilder mit dem Inhalt der Seite harmonieren können, wird die Seite mit TYPO3 aufgebaut und die Kopfbilder über die Seiteneigenschaften (Ressourcen / Dateien) selbst gepflegt.
Soweit kein Problem und schon tausendmal implementiert. Aber [...]]]></description>
			<content:encoded><![CDATA[<p>Viele schöne Seiten präsentieren großflächige und hochauflösende Bilder. Natürlich wollen unsere Designer das auch und damit der geneigte Besucher Abwechslung bekommt und die Bilder mit dem Inhalt der Seite harmonieren können, wird die <strong>Seite mit TYPO3 aufgebaut</strong> und die Kopfbilder über die <strong>Seiteneigenschaften</strong> (Ressourcen / Dateien) selbst gepflegt.</p>
<p>Soweit kein Problem und schon tausendmal implementiert. Aber wie sieht es aus, wenn der Redakteur <strong>mehrere Bilder</strong> auswählt? Na logisch: auf der Seite werden sie sehr sanft übergelendet (fadeIn, fadeOut). Und klar, das passiert natürlich mit <strong>jQuery</strong> und dem genialen <strong>Plugin <a title="Innerfade Plugin für jQuery" href="http://medienfreunde.com/deutsch/weblog/aus_der_praxis.html?nid=162" target="_blank">innerfade</a> </strong>von den <a title="Die Medienfreunde" href="http://medienfreunde.com/">medienfreunden</a>.</p>
<p>Diese Technik eingesetzt findet sich zum Beispiel auf der Seite zur Ausstellung über <a title="Audrey Hepburn Ausstellung in Berlin" href="http://www.timeless-audrey.de" target="_blank">Audrey Hepburn</a>.<span id="more-296"></span></p>
<h2>TYPO3 Template</h2>
<p>Nachdem eine Seite mit mehreren Dateien über <em>Seiteneigenschaften</em> / <em>Ressourcen</em> / <em>Dateien</em> ausgestattet wurde, wird ins <strong>HTML Template</strong> gewechselt und <strong>ein neuer Marker</strong> eingebunden. Ich nenne ihn mal <strong>rubrikBild</strong> mit der ID <em>head</em>.<pre><code class='html'>&lt;div id="head"&gt;###rubrikBild###&lt;/div&gt;</code></pre></p>
<p>Als nächstes wird der Marker per Typoscript definiert. Er ist vom <strong>Typ COA</strong> und soll aus einer Liste von Bildern bestehen.<pre><code class='javascript'>rubrikBild = COA<br />
rubrikBild {<br />
&nbsp;&nbsp;10 = IMAGE<br />
&nbsp;&nbsp;10.file {<br />
&nbsp;&nbsp;[tab]import = uploads/media/<br />
&nbsp;&nbsp;[tab]import.data = levelmedia : -1, slide<br />
&nbsp;&nbsp;[tab]import.listNum = 0<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;10.wrap = &lt;li&gt;|&lt;/li&gt;<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;wrap = &lt;ul&gt;|&lt;/ul&gt;<br />
}</code></pre></p>
<p>Das erste Objekt <strong>10</strong> wird definiert als Bild <strong>= IMAGE</strong>. Über die Angabe <strong>10.file</strong> wird eine Datei angesprochen. <strong>import</strong> gibt den Ursprungsort an, <strong>import.data</strong> bestimmt die <em>Vererbung auf Unterseiten</em>. Damit wird das Bild der übergeordneten Seite angezeigt, sollte kein eigenes bestehen. <strong>import.listNum</strong> wählt aus der Liste das erste Bild aus. Abschließend wird das generierte Img-Tag in einem Listenelement verpackt <strong>10.wrap = &lt;li&gt;|&lt;/li&gt;</strong> und das komplette Objekt als Liste <strong>wrap = &lt;ul&gt;|&lt;/ul&gt;</strong>.</p>
<p>Hm. Mit diesem Code wird jetzt nur ein einziges Bild in einer Liste dargestellt. Eindeutig zuwenig zum Überblenden. Deshalb muss dieser Code <strong>mit einer Schleife ersetzt werden</strong>. Dazu werden die ausgewählten Dateien in einen Text geschrieben und mit der Funktion <a title="Split in der Typoscript Referenz" href="http://www.typo3.net/tsref/functions/split/" target="_blank">split</a> auseinander genommen. Anschließend kann jedes Element einzeln behandelt werden. <pre><code class='javascript'>rubrikBild = COA<br />
rubrikBild {<br />
&nbsp;&nbsp;10 = TEXT<br />
&nbsp;&nbsp;10.data = levelmedia:-1, slide<br />
&nbsp;&nbsp;10.split {<br />
&nbsp;&nbsp;[tab]token = ,<br />
&nbsp;&nbsp;[tab]cObjNum = 1<br />
&nbsp;&nbsp;[tab]1 {<br />
&nbsp;&nbsp;[tab]&nbsp;&nbsp;10 = IMAGE<br />
&nbsp;&nbsp;[tab]&nbsp;&nbsp;10.file.import.current = 1<br />
&nbsp;&nbsp;[tab]&nbsp;&nbsp;10.file.import = uploads/media/<br />
&nbsp;&nbsp;[tab]&nbsp;&nbsp;10.wrap = &lt;li&gt;|&lt;/li&gt;<br />
&nbsp;&nbsp;[tab]}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;<br />
&nbsp;&nbsp;wrap = &lt;ul&gt;|&lt;/ul&gt;<br />
}</code></pre></p>
<p>Das Auslesen der Datei und die Wraps sind wie im oberen Beispiel, aber hier werden jetzt <strong>alle angegebenen Dateien </strong>ausgelesen!</p>
<h2>CSS</h2>
<p>Die Darstellung der Liste richtet sich natürlich nach dem Seitenlayout. Auf jeden Fall sollte man es mit <em>position:absolute</em> so einrichten, das alle Bilder übereinander liegen damit <em>Benutzer ohne JavaScript</em> nicht von einem Haufen sinnloser Bilder erschlagen werden.</p>
<h2>JavaScript</h2>
<p>Jetzt fehlen noch <strong>ein paar Zeilen jQuery</strong> und fertig ist die Funktion! Als erstes muss das <strong>Plugin innerfade eingebunden</strong> werden. Natürlich über <strong>Typoscript</strong> im <strong>Roottemplate</strong> und dort entweder per <em>page.10.headerData</em> oder <em>page.includeJS</em>, je nach Geschmack würde ich mal behaupten.</p>
<p>Dann wird eine Datei für eigene Scripte benötigt, ich nenne sie gerne <em>action.js</em> (eingebunden NACH dem jQuery Core und allen benötigten Plugins), die eine Funktion beinhaltet, welche aufgerufen wird sobald alles fertig geladen wurde:<pre><code class='javascript'>jQuery(function($) {<br />
&nbsp;&nbsp;// ... hier gehts los ...<br />
});</code></pre>Als erstes muss geprüft werden, <strong>ob überhaupt mehr als ein Bild vorhanden</strong> ist:<pre><code class='javascript'>if($("#head ul li").length &gt; 1) {<br />
&nbsp;&nbsp;// ...hier gehts weiter ...<br />
}</code></pre>Wenn das der Fall ist, dann muss nur noch das <strong>innerfade Plugin aktiviert</strong> werden:<pre><code class='javascript'>$("#head ul").innerfade({<br />
&nbsp;&nbsp;speed: 2000,<br />
&nbsp;&nbsp;timeout: 4000,<br />
&nbsp;&nbsp;containerheight: '250px'<br />
});</code></pre></p>
<p>Zu beachten ist, dass die Funktion <em>innerfade</em> auf den <strong>umfassenden Container</strong>, also auf das <em>&lt;ul&gt;</em>, angewendet wird und nicht auf die einzelnen Elemente. Wenn man die Angabe <strong>containerheight</strong> <span style="text-decoration: line-through;">vergisst</span> weglässt, dann funktioniert es nicht im Internet Explorer. <strong>Daran denken! </strong>Noch mehr mögliche Einstellungen sind im Quelltext von <em>jquery.innerfade.js</em> dokumentiert.</p>
<h2>Fertig</h2>
<p>Jetzt in der Schulung die <strong>Freude der Auftraggeber</strong> genießen und viele schöne Überblendungen auf den eigenen Seiten beobachten. Sobald unsere neue Seite veröffentlich wurde, wird sie hier als Beispiel dienen. <span style="text-decoration: line-through;">Bitte noch ein bisschen Geduld.</span> Die Ausstellungsseite <a title="Audrey Hepburn Ausstellung in Berlin" href="http://www.timeless-audrey.de" target="_blank">timeless-audrey</a> benutzt diese Technik.</p>
<p>PS: <em>Ein Linktipp; </em>die Firefox Erweiterung <a title="Wappalyer für Firefox" href="https://addons.mozilla.org/de/firefox/addon/10229" target="_blank">Wappalyzer</a> verrät auf einen Blick welche Systeme eine Internetseite einsetzt. Sehr praktisch! Danke <a title="t3n" href="http://t3n.yeebase.com/" target="_blank">t3n</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/03/03/animierte-header-mit-typo3-und-jquery/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
