ek

Beiträge zum Thema JS, HTML, CSS & anderem Kram

(English version of this article @Codepen.)

Neulich hatte ich ein interessantes Problem: Ich musste ein <iframe>-Element in eine responsive Website einbetten, dessen Inhalt wiederum nicht responsiv war. Man kann sich leicht vorstellen, wo hier der Fallstrick liegt. Wird der Bildschirm kleiner, passt sich die umgebende Website zwar problemlos an, der Inhalt des iFrames jedoch bleibt starr in seinen Dimensionen. Zusätzlich erscheinen unschöne, horizontale Scroll-Balken am iFrame.

Glücklicherweise gibt es in modernen Browsern einen Weg, mit diesem Problem umzugehen. Mithilfe von CSS-Transforms kann der iFrame inklusive seines Inhalts runter skaliert werden wie ein Bild. Der folgende Codepen zeigt, wie das aussehen kann.

(Link zur Demo-Seite)

Um dieses Verhalten zu implementieren, benötigen wir als erstes einen Breakpoint, ab dem die Skalierung wirksam wird. Den Breakpoint kann man sehr leicht ermitteln, indem man das Browser-Fenster verkleinert. Sobald am iFrame ein horizontaler Scroll-Balken auftaucht, hat man den Breakpoint erreicht.

JavaScript

var BREAKPOINT = 1060; // (Willkuerlich gewaehlter Wert!)

Der Breakpoint dient außerdem als Referenz-Breite des iFrames: 100% entsprechen 1060px. Nun benötigen wir den Skalierungs-Faktor. In der Theorie ist das der Quotient aus gegenwärtiger Bildschirm-Breite und iFrame-Referenz-Breite. In der Praxis hat sich dieser Wert für mich jedoch ab einem bestimmten Punkt als zu groß erwiesen. Daher verringere ich den Wert geringfügig mithilfe der Math.pow-Methode und einem Exponenten von 1.2.

JavaScript

var scale = Math.pow(window.innerWidth / BREAKPOINT, 1.2);

Eigentlich habe wir damit auch schon alles, was wir benötigen: einen Breakpoint, ab dem das Skalieren einsetzt, und einen Skalierungs-Faktor. Setzten wir nun jedoch den CSS-Transforms-Wert mit dem jeweiligen Skalierungs-Faktor am iFrame-Element an, passiert etwas merkwürdiges: der Inhalt des iFrames skaliert unmerklich, wobei das iFrame-Element selbst rapide an Größe verliert und buchstäblich in sich zusammen fällt. Das ist leider nicht das gewünschte Resultat.

Glücklicherweise gibt es einen Weg, dieses Verhalten zu unterbinden. Die Lösung ist, die Dimensionen des iFrames in dem Maße zu erhöhen, in dem es skaliert wird. Die Auswirkungen der CSS-Transforms auf die Abmessungen werden somit vollständig kompensiert und der iFrame verhält sich wieder wie ein normales, responsives Element. Lediglich der Inhalt des iFrames schrumpft wie gewünscht.

JavaScript

var scale = 0.8; // (Willkürlich gewählt!)
var width = 100 / scale; // Breite in Prozent
var height = INITIAL_IFRAME_HEIGHT / scale; // Hoehe in Pixeln

Die Breite des iFrames in Prozent ist die Inverse des gegenwärtigen Skalierungs-Faktors multipliziert mit 100. Die angepasste Höhe in Pixel ist der Quotient aus initialer Höhe und Skalierungs-Faktor. Die initiale Höhe des iFrames muss dabei ganz zu Beginn einmal ermittelt und gespeichert werden.

Jetzt gibt es nur noch ein potenzielles Problem: ist das iFrame-Element mithilfe von margin: * auto; mittig auf der Seite positioniert, muss die transform-origin ebenfalls durch "center top" auf Zentrierung eingestellt werden.

Dies hat jedoch zur Folge, dass der iFrame während des Skalierens rechts aus dem Bild "läuft", da sich seine Breite sukzessive erhöht. Um dieses Verhalten zu kompensieren, muss der iFrame um die Hälfte seines Wachstums in der Breite nach links verschoben werden.

JavaScript

var width = 100 / scale; // Breite in Prozent
var offsetLeft = (width - 100) / -2; // Versatz nach links in Prozent

Setzt man diesen Versatz mithilfe von transform: translateX(…);, bleibt der iFrame in der Mitte der Seite.

Das ist auch schon alles. Solltest du Fragen dazu haben, schreib einen Kommentar oder frag mich via Twitter: @Herschel_R.

GitHub Random Favorite

»GH Random Favorite« im Google Chrome Store

»GH Random Favorite« auf GitHub

Interessante GitHub-Repos zu favorisieren ist fast schon ein Reflex. Ich denke, niemand spart dabei mit Sternen. Allerdings gilt für die meisten favorisierten Repos wohl der Leitspruch: aus den Augen, aus dem Sinn.

Das ist schade und oftmals ärgerlich. Wie oft sucht man nach einer Lösung (aka Library, PlugIn, Framework, …) für ein bestimmtes Problem und stellt dann — nachdem man auf GitHub etwas gefunden hat — fest, dass man das Repo vor einem halben Jahr schon einmal favorisiert hat.

Um dieser Form der digitalen Demenz entgegen zu treten, habe ich eine Chrome-Extension — »GH Random Favorite« — geschrieben, die auf der GitHub-Startseite ein zufällig gewähltes Repo aus der Favoritenliste anzeigt. Dabei treten wahre Schätze wieder zutage.

Probierts mal aus!

Sollte es Probleme geben, kann im zugehörigen GitHub-Repo ein Ticket angelegt werden.

PS: Das ganze läuft über die stark reglementierte Public API von GitHub. Es können also nur 30 zufällige Favoriten pro Stunden angezeigt werden (es braucht zwei Requests pro zufälligem Favorit). Sollte sich herausstellen, dass das zu wenig ist, könnte man über die Integration von OAuth nachdenken. Das spielt für mich momentan jedoch noch keine Rolle.

This is a rather unusual post as it is written in english. So don't be irritated by the fact that everything around here on this blog is in german. I switched the language for this post because I wanted to write a response to an article on Smashing Magazine about Full-stack JavaScript. Normally I would have just written a comment over at Smashing Magazine's, but this comment would have gotten way too long.

Also I want to shortly clarify my relationship to JavaScript. I'm working as a frontend developer for an agency in Hanover and I actually love writing JavaScript. In fact I have to love writing JavaScript as it is my main programming-/scripting-language. I'm quite familiar with PHP too and I wrote a little Python some time ago, but most of the time I'm writing JavaScript. And therefor I would get in real trouble if I started to hate writing it.

So what's wrong with this article on Full-stack JavaScript? Actually it's overall a good post and provides a wide overview on the topic. But beside the fact, that I disagree with the author when he states that asynchronous, callback-based code is easier to read and understand than synchronous (linearly written) code (e.g. PHP), I don't like the announcement of the "Full-stack-JacaScript-developer-dude/-dudess" he implicitly makes.

It's correct that it is easily possible to to have an application (client and server) fully running on JavaScript. But in my perception Alejandro Hernandez negates the fact that there still exist two different environments, though they both run the same language. And these environments provide different requirements to the developer. On the client-side it's about creating a user experience, dealing with user interaction and device deficiencies. Sure, with client-side-rendering and -routing formerly unusual tasks arrived on the client-side, but IMHO that doesn't equate client- and server-side programming.

On the server-side again there are tasks that could easily be located on the client-side (e. g. SQLite is implemented on both sides of the wire), but that's only a small part of the tasks a common server-side progammer is confronted with. There are way more things to do with data than fetching it from/writing it to a database. This is the kind of computer-sience-stuff a frontend developer has few to nothing to do with with when she's juggling HTML, CSS and JavaScript in the presence of various browser quirks.

That's why I think it's risky to suggest that there could prospectively be one kind of developer that is writing the full stack in JavaScript. And I think Alejandro Hernandez is exactly doing this in his article.

TL;DR

Though client and server are nowadays able to run the same programming language, I think there won't arise a new species of full-stack developer that addresses all the requirements issued by these two environments. Though there is an overlap in tasks and some people out there have a really broad understanding of various JavaScript-driven environments, the common case will be a specialized developer that does reliable work either on either the client- or the server-side.

Sizzle & Angular

Wie weithin bekannt ist, benutzt Angular für DOM-Angelegenheiten intern eine abgespeckte jQuery-Variante namens jqLite, auf die über angular.element zugegriffen werden kann. Diese bietet ein paar Methoden aus dem Bereich DOM-Manipulation und -Traversing, hat jedoch keine Selektor-Engine. Bindet man parallel zu Angular die jQuery-Library ein, wird jqLite durch jQuery ersetzt.

Angesichts der Tatsache, dass Angular eigene Services für AJAX und Animationen (Version >= 1.1.3 in Verbindung mit CSS3) bietet, ist der Einsatz von jQuery jedoch stark übertrieben. Zum Glück gibt es die von jQuery verwandte und von John Resig geschriebene Selektor-Engine Sizzle auch als Stand-alone-Version. Diese kann man alternativ einbinden. Man muss angular.element dann nur noch beibringen, sie auch zu verwenden.

Das wiederum ist schnell erledigt.

JavaScript

// Anlegen einer Referenz auf die eigentliche angular.element-Methode
angular._element = angular.element;

// Ueberschreiben der angular.element-Methode
angular.element = function (selector) {
    // ist das uebergebene Argument bereits eine
    // Instanz von angular._element, wird es ohne
    // Umschweife zurueck gegeben.
    if ( selector instanceof angular._element ) {
        return selector;
    }

    // Andernfalls wird eine Sizzle-Instanz erstellt
    // und an die angular._element-Methode uebergeben.
    return angular._element(Sizzle(selector));
};

Fertig ist die Laube!

Das ganze gibt es auch als Angular-Modul "Angular-Sizzle" auf Github.

Zum Schluss noch der Hinweis, dass der Einsatz von Angular-Sizzle gut überlegt sein will. Das Konzept von Angular sieht vor, dass tiefgreifendere Arbeiten am DOM über Direktiven erledigt werden. Diese bieten abstrahierte Element-, Klassen- und Attribut-Selektoren. Innerhalb der Direktiven gibt es automatisch eine jqLite-Instanz der relevanten DOM-Elemente, mit der man arbeiten kann.

Der Einsatz einer ausgewachsenen Selektor-Engine könnte also übertrieben sein. Das hängt jedoch davon ab, was man vor hat.

Burp.js - Screenshot

Seit kurzer Zeit gibt es ein JavaScript-API, mit welchem man Vibrationen von mobilen Geräten im Browser steuern kann. Um den Umgang mit dem API intuitiver zu gestalten, habe ich einen Navigator.Vibrate-Wrapper namens Burp.js geschrieben. Dieser funktioniert folgendermaßen:

JavaScript

Burp('*·*·*·**-**', 200);

Die Burp-Funktion nimmt zwei Parameter entgegen: Einen String, welcher den Rhythmus der Vibration steuert. Sternchen bilden dabei Vibrationen, Punkte (Middle Dot) sind kurze Unterbrechungen und Bindestriche Pausen, welche die gleiche Länge wie die Vibrationen haben. Mehrere Vibrationen bzw. Pausen hintereinander werden addiert.

Der zweite, optionale Parameter gibt in Millisekunden an, wie lang die Vibrationen und Pausen sein sollen. Der Default-Wert ist 300ms.

Hier gibt es eine Demo dazu …

Das ganze wird theoretisch vom mobilen Firefox, dem Android-Browser und dem mobilen Chrome unterstützt. Praktisch zum Laufen bekommen habe ich es jedoch nur im Mobile Firefox. Man wird sehen, wie es sich entwickelt. Für mehr Informationen empfehle ich den Beitrag auf MDN dazu.

Require.js

Angenommen man lädt mit seine JavaScript-Dateien mit Require.js, wobei man über das data-main-Attribut die Require.js-Konfiguration anspricht und innerhalb dieser mithilfe der deps-Eigenschaft die weiteren benötigten Dateien anmeldet. Hat man nun keine klassiche “Single-page App”, sondern mehrere Unterseiten, auf welchen man unterschiedliche Funktionalitäten anbieten möchte, hat man drei Möglichkeiten:

  1. Man verpackt alles in eine JavaScript-Datei und lädt diese auf allen Unterseiten
    Das ist ziemlich unelegant und läuft der Idee von Require.js, JavaScript zu modularisieren, zuwider.
  2. Man legt für jeden Unterseite eine Config-Datei an, die das gewünschte, zur Unterseite gehörige Script lädt
    Auch dies ist unelegant, da es Redundanz und unnötigen Overhead erzeugt.
  3. Man definiert am Script-Tag, welches die Require.js-Library einbindet, seitenspezifische Umgebungsvariablen
    Dazu im folgenden mehr …

Der Trick bei letzterer Herangehensweise ist, mithilfe des HTML5-data-Attributs weitere Informationen zu übergeben und dem Script-Tag eine ID zu verpassen, so dass es einfach angesprochen werden kann. Dadurch hat man die Möglichkeit, mit einer Konfigurationsdatei flexibel auf die jeweilige Unterseite zu reagieren.

Folgendermaßen sieht das Einbinden von Require.js aus:

HTML

<script src="path/to/require.js" id="requirejs" data-main="path/to/config" data-env="about" data-devmode="1"></script>

require.js und die config.js werden geladen. Soweit, so normal. Außerdem wird mithilfe von data-env definiert, dass es sich bei der aktuellen Seite um die “About”-Seite handelt, und mithilfe von data-devmode weiterhin festgelegt, dass der Entwicklungsmodus aktiv ist. Bei letzterem muss man auf “0” und “1” zurückgreifen, da die Attributwerte immer als String ausgelesen werden. “true” und “false” wären als nicht-leere Strings also beide in der Abfrage true.

“0” und “1” jedoch kann man über den Umweg Integer in Boolean umwandeln. Dies geht mithilfe von Bitwise-Operatoren folgendermaßen:

JavaScript

typeof ('1337' | 0) // => "number"

Die zu ladene config.js sieht dann folgendermaßen aus:

JavaScript

var requirejsElem = document.getElementById('requirejs');

require.config({

    deps : (function () {
        return [requirejsElem.getAttribute('data-env') + '.main']
    })(),

    urlArgs : (function () {
        return !!(requirejsElem.getAttribute('data-devmode')|0)
            ? 'bust=' + Date.now()
            : '';
    })(),

    paths : {
        jquery : 'libs/jquery-1.8.2.min'
    }

});

Als erstes wird das Script-Tag, welches Require.js lädt, in der Variable requirejsElem gespeichert, um bequem darauf zugreifen zu können. Dies ist zwar eine globale Variable, was es eigentlich zu vermeiden gilt, aber hier heiligt der Zweck die Mittel.

Als nächste folgt die eigentliche Require.js-Konfiguration. Die deps-Eigenschaft, welche die benötigte Applikationslogik lädt, ist nun dynamisch und spricht alle <prefix>.main.js-Dateien an. In diesen können dann die jeweils benötigten Module per require([…]) geladen werden.

Das devmode-Flag wird fürs Cache-Busting genutzt. Ist die Seite im Dev-Mode, wird an alle Script-Pfade der GET-Parameter bust angehängt und diesem der aktuelle Timecode übergeben.

Befindet sich die Seite nicht im Dev-Mode, wird der urlArgs-Eigenschaft ein leerer String übergeben und das Caching der Scripte findet statt.

Dies sind nur zwei Möglichkeiten, Require.js mithilfe des data-Attributs flexibler zu nutzen. Im täglichen Entwicklerleben ergeben sich bestimmt noch weitere praktische Anwendungsfälle. Kann man zudem sicher sein, nur für moderne Browser zu entwickeln, muss man nicht umständlich mit getAttribute hantieren, sondern kann auf die HTML5-data-Attribute-API zurückgreifen. Anstatt requirejsElem.getAttribute('data-env') reicht in dem Fall requirejsElem.dataset.env.

Require.js-Autoloader

Wenn aus dem “One-Pager” ein “Multi-One-Pager” wird, kann sich ein gewisser Overhead bezüglich der geladenen Skripte einstellen. Angenommen man baut eine Web-App, die aus vielen Einzelseiten besteht, und jede Einzelseite stellt für sich einen “One-Pager” dar, welcher nicht zwingend beim Besuch eines Nutzers aufgerufen wird. Dann werden – benutzt man Require.js zum Laden der Skripte – beim initialen Aufruf der Web-App alle Skripte geladen, auch wenn sie letztlich gar nicht benötigt werden.

In diesem Fall wäre es praktischer, mehrere Require.js-Instanzen anzulegen und durch einen zentralen Router gesteuert bei Bedarf aufzurufen. Dieses Szenario habe ich einmal mithilfe von Backbone.js nachgestellt. Die “Web-App” besteht aus diversen Einzelseiten, welche bestimmte jQuery-UI-Widgets beinhalten. Außerdem gibt es eine Startseite mit einem kurzen Erklärungstext. Ziel ist es, den Startseiteninhalt und die jQuery-UI-Widgets nur bei Bedarf zu laden.

Dafür wird beim Aufruf der Seite lediglich eine fundamentale Require.js-Instanz aufgerufen, die die global benötigten Skripte – jQuery, Underscore, Backbone, den jQuery-UI-Core und einen Observer – laden und einen Backbone-Router initialisieren, der die Autoloader-Funktionalität zur Verfügung stellt.

JavaScript

require(['jQuery', 'Underscore', 'Backbone', 'Observer'], function ($, _, Backbone, Observer) {

	var Router = Backbone.Router.extend({

		routes : {
			':site/*sub' : 'autoLoad'
		},

		initialize : function () {
			location.hash = location.hash || 'index/';

			Backbone.history.start();
		},

		autoLoad : function (site, sub) {
			var path = 'sites/' + site + '/app';

			require([path], function (App) {
				App.init({
					subPages : sub
				});
			});
		}
	}),

	router = new Router();

});

Der Router prüft als erstes, ob ein Location-Hash gesetzt ist. Ist dies nicht der Fall, wird der Hash “/index/” gesetzt, was die Initialisierung der Startseite einleitet. Weiterhin wartet der Router auf Änderungen des Location-Hash und führt im Bedarfsfall die “autoLoad”-Funktion aus, welche das gewünschte Modul/die gewünschte Seite lädt. Das “*sub” im “routes”-String des Routers stellt sicher, dass auch beim initialen Aufruf von Unter-Unterseiten die Autoload-Funktion tut, was sie soll.

Das vom Autoloader geladene Skript übergibt eine “init”-Funktion – dabei kann es sich, je nachdem ob die Seite lediglich statischen Inhalt hat oder tiefergreifende Funktionalität besitzt, entweder um eine Backbone-View- oder eine Backbone-Router-Instanz handeln.

JavaScript

var init = function () {
		return new View();
	};

return {
	init : init
};

JavaScript

var init = function (args) {
		return new Router(args);
	};

return {
	init : init
};

Das Datepicker-Modul habe ich als Beispiel für eine zusätzliche Router-Instanz gewählt. Inhaltlich zugegebenermaßen etwas mau, wird doch deutlich, dass auf diese Weise die Funktionalität der Web-App auch auf Unterebenen gewährleistet werden kann, ohne dass dies beim initialen Aufruf der Seite angemeldet bzw. geladen werden muss.

Beobachtet man den Inhalt des HTML-Head beim Durchklicken der Seite im Firebug bzw. Web Inspector, kann man sehr schön verfolgen, wie die jeweils benötigten Module und Widgets sukzessive nachgeladen werden.

Ich hoffe, mein Versuch, den Sachverhalt hier darzustellen, ist einigermaßen verständlich. Über Anregungen, Anmerkungen und Fragen freue ich mich immer. Sollte diesbezüglich Bedarf bestehen, kann ich nur dazu ermutigen, regen Gebrauch von der Kommentar-Funktion zu machen.

Außerdem sei darauf hingewiesen, dass der Code zur schnellen Inspektion auch auf GitHub zur Verfügung steht.

Clipboard – Digitales Notizbrett mit Node.js und Backbone.js

Um etwas Zerstreuung vom Uni-Lernstress zu bekommen, habe ich einen Ausflug in die bunte Node.js-Welt unternommen. Als Mittel zum Zweck diente mir dabei das digitale Notizbrett “Clipboard”. Serverseitig kommt das Application-Framework Express zum Einsatz. Gespeichert werden die Daten in einer MongoDB-Datenbank mithilfe von Mongoose.

Client-seitig kommt die bewährte Kombo aus Underscore, Backbone und jQuery zum Einsatz. Für Ordnung sorgt überdies Require.js, wobei ich auf folgenden Ansatz zurückgegriffen habe: Modular JavaScript & Backbone.js. Außerdem hat jQueryUI einen “Gast-Auftritt” und sorgt für die Draggability der Notizen.

Das Hosting übernimmt dankenswerterweise AppFog, die Daten werden bei MongoHQ abgelegt. Defenitiv zwei sehr praktische Services.

(Clipboard-Code bei GitHub)

Backbone.js – Logo

Wer hier gelegentlich mitliest, wird eventuell mitbekommen haben, dass ich mich gerne mit dem JavaScript-MVC-Framework Backbone.js beschäftige. Von daher war meine Freude recht groß, als ich gewahr wurde, dass Addy Osmani höchstselbst sich dran gemacht hat, eine unter CC-Lizenz stehende Wissenssammlung zum Thema unter dem Namen “Backbone Fundamentals” zu scheiben.

Diese behandelt die Basis-Themen und gibt einen kurzen Einblick in das Thema Backbone.js. Des weiteren wird die Entwicklung einer “RESTful Application” mithilfe von Node.js, Express, Mongoose und MongoDB beschrieben. Es folgt eine Einführung in das Thema “Modular JavaScript” im Kontext von Backbone.js, abgerundet durch konkrete Anwendungsszenarien. Anschließend wird das nicht uninteressante Thema Backbone.js in Kombination mit jQuery Mobile behandelt. Und zum Schluss steht Unit Testing mit dem Test-Framework Jasmine auf der Agenda.

Alle Themen werden anhand der Backbone-Todo-Application erläutert, so dass der praktische Einsatz der vorgestellten Techniken direkt ersichtlich wird. Meinem Empfinden nach entsteht mit “Backbone Fundamentals” eine sehr wertvolle, breit gefächerte Ressource, was das Thema Backbone.js anbelangt. Vielen Dank deshalb an Addy Osmani und allen weiteren, die mitgeholfen haben.

Zum Schluss möchte ich jedoch auch anmerken, dass es sich bei “Backbone Fundamentals” nicht gänzlich um Einstiegslektüre handelt. Grundsätzlich sollte man ein Verständnis von JavaScript, DOM-Libraries im allgemeinen und Application Development in Ansätzen mitbringen, um sich der Lektüre der Basics zu widmen. Bei den weiterführenden Themen ist ein Grundverständnis der verwandten Frameworks und Techniken, die im Kontext von Backbone.js beleuchtet werden, sicherlich auch hilfreich.

Obwohl meiner Meinung nach auch nichts dagegen spricht, bspw. das Kapitel “Modular JavaScript” zum Anlass zu nehmen, sich endlich einmal mit der Materie auseinander zu setzen. Allerdings wird man in diesem Fall wohl nicht umhin kommen, begleitende Lektüre zu konsultieren.

Rollstuhlsport – Android-App des DRS

Der Deutsche Rollstuhl-Sportverband hat neuerdings eine Android-App, welche ich in meiner Rolle als Frontend-Developer bei w3design. entwickeln durfte. Die App wird zeitnah außerdem auch für das iPhone erscheinen.

Was daran jedoch besonders interessant ist – abgesehen vom Erscheinen der App selbst –, ist die Tatsache, dass ich sowohl von Java, als auch von Objective-C nicht den blassesten Schimmer habe. Vielmehr habe ich mich beim Entwickeln der App auf meine HTML-, CSS- und JavaScript-Kenntnisse verlassen. Möglich ist das durch PhoneGap, ein Framework, das Web-Apps in native Apps umwandelt und dabei ein paar sinnvolle Hardware-APIs zur Verfügung stellt.

Für die App selbst, habe ich auf Backbone.js gesetzt, da es mir gute Möglichkeiten bot, einerseits die Fülle an unterschiedlichen Daten zu verarbeiten, und andererseits ein sehr umfangreichen “One-Pager” zu bauen, welcher aus einer index.html besteht und alle Unterseiten client-seitig rendert.

Ich kann Phonegap jedenfalls nur empfehlen und möchte noch auf LungoJS hinweisen, welches bei kleineren Apps sicher gut mit Phonegap Hand in Hand geht.