Fragen zu HTML5 und Co beantwortet 16 - Flexbox-Masonry, Block- und Inline-Elemente, File API, ES6-Klassen

Veröffentlicht am 15. September 2014

Ich würde ja mal wieder etwas anderes bloggen, aber so lange ihr mich weiter mit Fragen zu HTML5 und Co bombardiert, muss ich die ja schweren Herzens zuerst beantworten. Das soll nicht heißen, dass ich nicht noch viel mehr Fragen gebrauchen könnte! Wenn euch etwas zu HTML5, CSS3, JavaScript, Web Components oder ähnlichem auf dem Herzen liegt, schreibt mir eine Frage per E-Mail oder via Twitter.

Masonry-Layout mit Flexbox?

Kann man mit Flexbox Masonry Layout machen?

Es gibt tatsächlich die Möglichkeit, etwas in ungefähr dieser Richtung zu machen. Grundsätzlich dient Flexbox weiterhin dem einfachen Anordnen von Boxen und ist damit eher unfancy. Allerdings ist es so flexibel, dass das komplette Aufüllen eines vorhandenen Raumes unter Berücksichtigung des Inhalts machbar ist. Der Clou ist: der vorhandene Raum ist immer eine Zeile und Flexboxen können umbrechen. Um diese Umbrüche zu aktivieren, muss in einem Flex-Container die Eigenschaft flex-wrap auf wrap gesetzt werden:

.wrapper {
  display: flex;
  flex-wrap: wrap;
}

In diesem Container können nun allerlei Kindelemente mit unterschiedlichen Maß-Verhalten platziert werden. Die Maß-Verhalten ergeben sich auf Basisgröße auf der Flex-Achse (flex-basis) sowie den Wachs- und Schrumpffaktoren (flex-grow, flex-shrink). Provoziert man durch zusätzliche Mindestbreiten (min-width) der Kindelemente Umbrüche, so gilt die durch den Umbruch neu entstandene Zeile als komplett neuer Raum, auf dem sich die nächsten paar Flexboxen breit machen – bis zum nächsten Umbruch. Das „breit machen“ erfolgt dabei auf der Kreuzachse, so dass die Elemente auch jeweils auf der vertikalen Mitte der Achse zentriert werden bzw. sich über die komplette Höhe der Zeile erstrecken können. Das Ergebnis ist dann tatsächlich einigermaßen Masonry-Layout-Like.

Block- und Inline-Elemente in HTML5

Kann ich eigentlich beim W3C oder woanders schnell herausfinden, welches Element in HTML5 ein Inline- und welches ein Blockelement ist?

Die Unterteilung in die zwei Kategorien „Block“ und „Inline“ gibt es in HTML5 nicht mehr. Stattdessen gibt es sieben neue Kategorien: Metadata, Flow, Sectioning, Heading, Phrasing, Embedded und Interactive. Elemente können in mehrere dieser Kategorien fallen. Das <a>-Element ist z.B. gleichzeitig Bürger der Kategoien Flow, Phrasing, Interactive und Palpable, während das <nav>-Element in Flow, Sectioning und Palpable zuhause ist.

Die Kategorien werden in HTML5 vorrangig genutzt, um festzulegen, welches Element in welchem anderen Element vorkommen darf (content model). Vor HTML5 galt die einfache Regel „Block-Elemente dürfen Block- und Inline-Elemente, Inline-Elemente aber keine Block-ELemente enthalten“. In HTML5 sollen aber auch Konstruktionen wie die folgende erlaubt sein:

<a href="/foo"> <!-- "Inline-Element" -->
  <h1>Foo</h1>  <!-- "Block-Element" -->
</a>

Um (unter anderem) dies punktuell zu ermöglichen ohne ein generelles Alles-In-Alles-Verschachteln zu erlauben, hat man die Kategorien einfach etwas feiner aufgegliedert. Alles was vor HTML5 an Element-Verschachtelungen möglich war, geht auch weiterhin – und eben ein kleines bisschen mehr.

HTML5 File API: File- in Blob-Objekte verwandeln?

Wie kann man File-Objekte in Blob-Objekte konvertieren? Braucht man dazu den FileReader?

Eigentlich sollte diese Umwandlung nicht nötig sein, denn File-Objekte sind nur ein Spezialfall bzw. eine „Unterklasse“ von Blob. Das kann man der IDL-Box (IDL = Interface Definition Language) an der entsprechenden Stelle in den Spezifikationen entnehmen. Jede API die Blobs verarbeitet sollte auch mit Files klarkommen:

var blob = new Blob(['Hallo Welt'], { type: 'text/plain' });
var file = new File(['Hallo Welt'], 'dateiname.txt');

// FileReader schlucken Files und Blobs gleichermaßen

var reader1 = new FileReader();
reader1.readAsText(blob);
reader1.onload = function(evt){
  window.alert(evt.target.result); // > 'Hallo Welt'
};

var reader2 = new FileReader();
reader2.readAsText(file);
reader2.onload = function(evt){
  window.alert(evt.target.result); // > 'Hallo Welt'
};

Sollte eine API oder Library auf Biegen und Brechen Files nicht annehmen wollen, so kann man ein File-Objekt via slice()-Methode in einen Blob verwandeln:

var blob = file.slice();

Das sollte aber eigentlich wirklich niemals nötig sein.

Privates in ECMScript 6-Klassen

Wenn Klassen in ES6 nur syntaktischer Zucker für Prototypen sind, gibt es dann auch keine Encapsulation mit private usw?

Etwas ähnliches wie private Objekteigenschaften sind in ES6 möglich, aber nicht durch Klassen allein. Vielmehr kann man Klassen (oder auch normale Constructorfunktionen bzw. Objekte) mit Symbols kombinieren und somit so-gut-wie-private Eigenschaften schaffen. Ein Symbol ist ein spezielles Objekt, das als Property-Name fungieren kann; etwas, das in ES5 nur Strings machen konnten:

let foo = {};
let property = Symbol();
foo[property] = 42;

console.log(foo[property]); // > 42

Jedes Symbol-Objekt ist einzigartig, so dass Namenskollisionen prinzipbedingt ausgeschlossen sind. Das ist praktisch um ein Objekt automatisiert mit Eigenschaften zu befüllen, kann aber auch für einen gesteigerten Grad an Privatheit genutzt werden  denn wer keinen Zugriff auf ein Symbol hat, kann auf die damit identifizierte Eigenschaft nicht ohne weiteres zugreifen:

let foo = {};

(function(){
  let property = Symbol();
  foo[property] = 42;
})();
  
// Wie an die 42 kommen?

Wer es drauf anlegt, kommt mit Object.getOwnPropertySymbols() an die Liste der auf einem Objekt verwendeten Symbols. So gesehen sind diese Eigenschaften nicht im Wortsinne privat, doch die als privat und als öffentlich gedachten Eigenschaften sind klar getrennt. Ein versehentlicher (oder auch nur leichtfertiger) Zugriff auf als nicht für die Öffentlichkeit gedachte Eigenschaften ist ausgeschlossen – und das ist ja der eigentliche Use Case, den es zu lösen gilt.

Ob es in ES6 (oder später) auch richtige private Eigenschaften geben wird, ist unklar. De facto existieren private Symbols bereits in den Spezifikationen, doch sie allgemein verfügbar zu machen, hätte allerlei Komplikationen zur Folge. Aktuell werden normale (d.h. einzigartige, aber nicht wirklich private) Symbols als gut genug betrachtet.

Weitere Fragen?

Auch eure Fragen zu HTML5, JavaScript und anderen Webtechnologien beantworte ich gerne! Einfach eine E-Mail schreiben oder Twitter bemühen und ein bisschen Geduld mit der Antwort haben. Ansonsten kann man mich natürlich auch als Erklärbär zu sich kommen lassen.

Fragen zu HTML5 und Co beantwortet 15 - Web Components versus Performance und Async, CSS-Variablen, Data-URLs

Veröffentlicht am 20. August 2014

Neue Antworten auf neue Fragen zu Webtechnologien braucht das Land? Kein Problem! Falls ihr noch mehr Fragen zu HTML5, CSS3, JavaScript, Web Components oder ähnlichem beantwortet haben wollt, schreibt sie mir per E-Mail oder gebt die Frage per Twitter ab.

Sind Web Components schlecht für die Performance?

Sind Web Components nicht eine Katastrophe für die Performance? So spaltet sich doch die komplette Seite in hundert Einzel-Downloads auf, jede Komponente lädt jedes Mal neu jQuery … oder gibt es da einen Trick?

Heutzutage ist das möglicherweise tatsächlich ein Problem, das sich aber in Zukunft von selbst in Luft auflösen wird. Das Doppel-Download-Problem besteht in Browsern mit nativer Unterstützung für Web Components gar nicht erst, da hier Doppel-Requests automatisch dedupliziert werden (so liest man jedenfalls allerorten; in konkreten Specs habe ich nichts gefunden, das das verlangt). Im Polymer-Polyfill passiert das einfach anhand des Ressourcen-Pfades, die native Implementierung soll auch identische Ressourcen aus unterschiedlichen Quellen deduplizieren können.

Dass Web Components dann immer noch zu vielen Einzel-Requests führen, ist in nicht all zu ferner Zukunft ein Feature und kein Bug. Mit HTTP/2 bzw. SPDY als Netzwerkprotokoll hat man, anders als beim HTTP 1.1 von heute, keinen Vorteil mehr, wenn man Ressourcen zusammenfasst. Im Gegenteil: wenn man sein Frontend in viele kleine Teile aufsplittet, hat man den Vorteil, dass bei der Änderung einer einzigen Icongrafik der Nutzer nicht mehr das komplette Bild-Sprite, sondern wirklich nur eine einzige Winzdatei neu herunterladen muss. Anders gesagt übernimmt HTTP/2 das Zusammenfassen von Dateien auf Protokollebene und Webentwickler müssen es nicht mehr selbst machen. Und nur die üblichen Verdächtigen (d.h. IE und Safari) können das nicht bereits heute. Mehr Infos zum Thema gibt es in einem epischen Slide Deck aus der Feder von Performance-Papst Schepp.

Die Web-Component-Performance-Problematik löst sich also im Laufe der Zeit von selbst. Bis dahin kann man sich mit Vulcanize, einem Tool zum Zusammenfassen von HTML-Imports inklusive aller Ressourcen in eine einzige Datei, behelfen.

CSS-Variablen und JavaScript

Kann man native CSS-Variablen (Custom Properties) via JavaScript ändern?

Man wird das machen können, allerdings ist noch nicht klar wie genau das im endgültigen Standard funktionieren wird. Das was im aktuellen Working Draft steht, hat aktuell nicht viel mit dem zu tun, was man in heutigen Browsern vorfindet. Zur JavaScript-API wird sogar nur angesagt, dass es eine a custom-property API to be defined in the future geben wird – nichts Konkretes weit und breit.

Dass es aber zumindest im Prinzip eine solche API geben kann, zeigt diese simple Demo (läuft nur im Firefox mit aktiviertem CSS-Variablen-Flag), die noch eine alte API aus früheren Spezifikationen verwendet. Wie es in Zukunft gehen wird, ist noch unklar, aber irgendwie wird es wohl funktionieren.

Data-URLs mit JavaScript laden

Ajax-Requests auf Data URLs werden von der Same Origin Policy unterbunden. Kann man Data-URIs noch anders (also ohne XHR) laden?

Mit der File API geht das. Man nehme die Data-URL, packe sie in einen Blob, lese diesen mittels eines FileReaders aus und schon ist das Ergebnis da:

var dataUrl = 'data:text/plain,Hallo Welt';
var dataString = dataUrl.split(',')[1];
var mimeString = dataUrl.split(',')[0].split(':')[1].split(';')[0];
var blob = new Blob([dataString], {type: mimeString});
var reader = new FileReader();
reader.readAsText(blob);
reader.onload = function(evt){
  window.alert(evt.target.result);
};

Wenn man aber schon zur File API und zu Blobs greift, kann man auch weiterhin XHR verwenden. Es gibt Blob-URLs die aufgrund einer Ausnahme in der der Same Origin Policy auch von XHR abgerufen werden können. So klappt es dann auch ohne File Reader und mit XHR:

var dataUrl = 'data:text/plain,Hallo Welt';
var dataString = dataUrl.split(',')[1];
var mimeString = dataUrl.split(',')[0].split(':')[1].split(';')[0];
var blob = new Blob([dataString], {type: mimeString});
var blobUrl = window.URL.createObjectURL(blob);
$.get(blobUrl, function(result){
  window.alert(result);
});

Der einzige Browser in dem das nicht klappt, ist der Internet Explorer: der macht bekanntlich die Same Origin Policy falsch.

Asynchron ladende Web Components?

Wenn ich viele Web Components via HTML-Import in meine Seite lade, ist das nicht gut für die Performance. Kann ich hier auch das async-Attribut verwenden? Funktionieren die Komponenten dann noch?

Laut Spezifikation für HTML Imports wird es das async-Attribut für <link>-Elemente geben, so dass das Laden von externen Ressourcen nicht mehr blockiert. Die im Rahmen von Web Components selbst erstellten Elemente funktionieren dann auch problemlos. Es ist nicht erforderlich, den HTML-Parser vor Erreichen des noch anzumeldenden Elements „abzufangen“, da die Mechanik von document.registerElement() ohnehin nur ein nachträgliches Upgrade von schon durch den Parser als unbekannt verarbeiteten Elementen vornimmt.

Die Herausforderung hierbei ist natürlich die Zeitspanne bis ein Element geladen und einsatzbereit ist. Durch umsichtige Programmierung und passendes Styling gilt es, nerviges Geflacker zu unterbinden; entsprechende Callbacks und CSS-Pseudoklassen helfen dabei. Polymer feuert ein hilfreiches polymer-ready-Event auf window, sobald alle Elemente beim Browser angemeldet sind.

Weitere Fragen?

Auch eure Fragen zu HTML5, JavaScript und anderen Webtechnologien beantworte ich gerne! Einfach eine E-Mail schreiben oder Twitter bemühen und ein bisschen Geduld mit der Antwort haben. Ansonsten kann man mich natürlich auch als Erklärbär zu sich kommen lassen.

Web Components erklärt, Teil 3: native HTML-Elemente erweitern

Veröffentlicht am 12. August 2014

Die bisherigen Teile dieser Serie haben gezeigt, was Web Components sind und wie man mit ihnen eigene HTML-Elemente erfinden kann. Seither sind Web Components auch ganz gut im allgemeinen Bewusstsein der Frontend-Entwickler angekommen, was man vor allem an der auf Twitter aufkommenden Kritik merkt. Eine besonders häufig formulierte Befürchtung ist, dass Web Components jedes etablierte HTML-Element durch eine unsemantische Eigenkonstruktion ersetzen könnten. In diesem Szenario würde z.B. aus <a href="https://www.google.de"> ohne guten Grund ein Monstrum der Marke <polymer-anchor data-href="https://www.google.de">, das in wichtigen Details (z.B. Barrierefreiheit oder Browserunterstützung) nicht an das Original heranreicht.

Es ist in der Tat nicht auszuschließen, dass demnächst ohne komplett überzeugenden Grund zusätzliche Varianten von <a> erfunden werden, aber es ist definitiv nicht nötig, dabei das Rad stets komplett neu zu erfinden und auf die Fähigkeiten der Originale zu verzichten. Web Components beinhalten einen Mechanismus, der es erlaubt vorhandene Elemente mit wenig Aufwand um neuen Fähigkeiten zu ergänzen. Mit normalem DOM-Code ist das etwas knifflig, mit Polymer jedoch ein Kinderspiel.

Der Extend-Mechanismus

Wie wir im ersten Teil der Serie gelernt haben, ist eine der neuen Technologien rund um Web Components die Funktion document.registerElement(). Mit ihr kann man beim Browser komplett selbst erfundene Elemente anmelden:

document.registerElement('x-foo', {
  prototype: Object.create(HTMLElement.prototype, {
    methodBar:{
      value: function(){
        console.log('Hallo Welt!');
      }
    }
  })
});

An document.registerElement() übergibt man neben dem Namen des neuen Elements (hier x-foo) auch ein Konfigurationsobjekt, das unter anderem den Prototypen für das neue Element festlegt (hier ein neues Objekt, das seinerseits HTMLElement.prototype als Prototypen hat). Als Prototyp kann jedes HTML-Element fungieren, so dass man sein selbstgebautes Element zu z.B. einem Sonderfall von <a> könnte, einfach indem man HTMLAnchorElement.prototype statt HTMLElement.prototype verwendet. Außer einer veränderten Prototypen-Kette bringt das allein aber nicht viel und <x-foo> wird dadurch nicht zu einem anklickbaren Link.

Um wirklich (ohne das Rad neu zu erfinden) eine Abwandlung von <a> zu erstellen, muss man einen etwas anderen Weg gehen. Zum Einen muss man bei der Element-Anmeldung dem Konfigurationsobjekt eine extends-Eigenschaft mitgeben, die den Tag des Elements angibt, von dem wir einen Sonderfall anlegen möchten:

document.registerElement('x-foo', {
  prototype: Object.create(HTMLAnchorElement.prototype, {
    methodBar:{
      value: function(){
        console.log('Hallo Welt!');
      }
    }
  }),
  extends: 'a'
});

Wichtig ist zum Anderen, dass der Prototyp des Elements in extend auch der Prototyp des Objekts in prototype ist. Die Benutzung des neuen Elements erfolgt dann nicht über einen neuen Tag <x-foo>, sondern das Element wird im is-Attribut eines normalen <a>-Elements angegeben:

<a is="x-foo" href="https://www.google.de">Google</a>

Dieses Element ist ein fast ganz normaler Link, nur ergänzt durch unsere Erweiterung in Form der JavaScript-Funktion methodBar(). Die Spezifikationen für Custom Elements nennen solche Konstruktionen type extensions. An sich funktioniert unsere Erweiterung jetzt auch, aber richtig toll ist das Ergebnis noch nicht:

  1. Die rohe API von document.registerElement() ist äußerst unbequem, v.A. das Setup der Prototypen-Kette ist ausgesprochen mühsam
  2. Alles, was über den Einbau einer kleinen JS-Extramethode herausgeht (z.B. Shadow DOM) würde richtig anstrengend werden
  3. Mangels Polyfills funktioniert der bisher gezeigte Code nur in den allerneuesten Browser (d.h. aktuellem Chrome)

Zum Glück ist das Erweitern von Elementen mit Polymer ein Kinderspiel. Es ist sogar so einfach, dass wir uns für den nächsten Schritt direkt vornehmen können, eine durchaus realitäsnahe Erweiterung für <a>-Elemente zu schreiben.

Type Extensions mit Polymer

QR-Codes (nicht lachen!) sind nichts anderes als Links in Bildform. Also macht es Sinn, sie in HTML wie folgt einzubinden:

<a is="qr-code" href="https://www.google.de">Google</a>

Ein solcher Link sollte statt des Link-Texts einen QR-Code anzeigen. Ergänzend sollte man, da es sich ja um Bilder handelt, optionale height- und width-Attribute angeben können. Wenn sich eins der Größen-Attribute oder das href-Attribut ändert, sollte der Code neu berechnet werden. Davon abgesehen sollte sich das Element wie ein herkömmlicher Link verhalten: es muss anklickbar sein, sich per Tastatur bedienen lassen und generell keine schwerwiegenden Nachteile gegenüber einem herkömmlichen Link haben.

Eine gut funktionierende QR-Code-Library ist auf Github schnell gefunden und wird zusammen mit dem üblichen Boilerplate-Code für ein neues Polymer-Element in eine HTML-Datei geschrieben:

<script src="qrcode.js"></script>
<link rel="import" href="bower_components/polymer/polymer.html">

<polymer-element name="qr-code">

  <template>
    <span id="Code"></span>
  </template>

  <script>
    Polymer('qr-code', {
    });
  </script>

</polymer-element>

Das „Element“ qr-code, das wir hier neu anlegen, wird hinterher als Wert im is-Attribut von <a>-Elementen fungieren. Das <span>-Element im Shadow-DOM-Template nimmt den erzeugten QR-Code auf.

Der Prototyp unseres qr-code-Elements braucht als erstes eine Methode zum erstellen neuer QR-Codes. Diese Objekte, erstellt durch die QR-Library, werden in der qrcode-Eigenschaft der Element-Instanz gespeichert (this.qrcode). Eingesetzt wird diese Methode wenn das Element bereit ist, d.h. der ready-Callback feuert:

Polymer('qr-code', {

  createCode: function(){
    return this.qrcode = new QRCode(this.$.Code, {
      text: this.getAttribute('href'),
      width: this.getAttribute('width') || 128,
      height: this.getAttribute('height') || 128
    });
  },

  ready: function(){
    this.createCode();
  }

});

Um auf Attribut-Änderungen reagieren zu können, bedienen wir uns des attributeChanged-Events. Wenn sich nur das href-Attribut ändert, können wir die makeCode()-Methode von QRCode-Objekten nutzen; nur bei Änderungen der Maße muss eine komplett neue QRCode-Instanz erstellt werden:

Polymer('qr-code', {

  ...

  attributeChanged: function(attr, oldVal, newVal){
    if(attr === 'href'){
      this.qrcode.makeCode(newVal);
    }
    if(attr === 'width' || attr === 'height'){
      this.createCode();
    }
  }

});

Stand jetzt ist unser Werk immer noch ein normales Custom Element. Und wie machen wir jetzt eine <a>-Erweiterung daraus? Es könnte einfacher nicht sein:

<polymer-element name="qr-code" extends="a">
  ...
</polymer-element>

Das ganze komplizierte Prototypen-Setup übernimmt Polymer und unsere Element-Erweiterung funktioniert einfach!

Selbstgebaute Custom Elements lassen sich in Polymer auch erweitern, wenn auch auf etwas andere Art und Weise. Was eigentlich gar nicht geht, sind Mehrfacherweiterungen.

Mixins statt Mehrfacherweiterungen

Mehrfacherweiterungen nach dem Muster <a is="foo bar baz"> gibt es nicht. Das ist auch einigermaßen nachvollziehbar, denn letztlich laufen Erweiterungen immer noch auf Prototypen-Ketten hinaus und da ist klar, dass ein Element in der Kette nicht mit mehr als einem anderen verbunden sein kann. Aber das als zweites Argument in die Polymer()-Funktion gesteckte Konfigurationsobjekt kann natürlich ein aus mehreren Objekten zusammengesetztes Objekt sein – Mixins sind hier die beste Lösung.

Nehmen wir einmal an, wir wollten ein Set von Social-Media-Buttons bauen. Jeder Social-Media-Button muss eine URL kennen, für die er Tweet-Links, Like-Schaltflächen o.Ä. bereitstellen soll. Es macht Sinn, diese Funktionalität in eine entsprechende Social-Media-Button-„Basisklasse“ auszulagern:

<polymer-element name="social-media-button" attributes="url">
<script>
  Polymer('social-media-button', {
    url: null,
    ready: function(){
      this.url = this.url || window.location.href;
    }
  });
</script>
</polymer-element>

Erweiterungen dieses allgemeinen Social-Media-Buttons könnten dann konkrete Facebook- und Twitter-Buttons sein. Diese sind keine Type Extensions sondern eigenständige Elemente, da die Custom-Elements-Spezifikation Type Extensions nur für native Elemente vorsieht. Polymer behilft sich, indem aus selbstdefinierten Elementen abgeleitete selbstdefinierte Elemente einfach per Prototypen-Kette hintereinandergeschaltet werden und so komplett neue Elemente entstehen, die die gleichen Fähigkeiten wie ihre „Basisklassen“ haben.

<polymer-element name="twitter-button" extends="social-media-button" attributes="via">
  <template>
    <iframe allowtransparency="true" frameborder="0" scrolling="no"
            src="https://platform.twitter.com/widgets/tweet_button.html?url={{encodedUrl}}&amp;via={{via}}"
            style="width:130px; height:20px;"></iframe>
  </template>
  <script>
    Polymer('twitter-button', {
      via: 'sir_pepe',
      encodedUrl: null,
      observe: {
        url: 'encode'
      },
      encode: function(){
        this.encodedUrl = encodeURIComponent(this.url);
      }
    });
  </script>
</polymer-element>


<polymer-element name="facebook-button" extends="social-media-button" attributes="share">
  <template>
    <iframe allowTransparency="true" frameborder="0" scrolling="no"
            src="//www.facebook.com/plugins/like.php?href={{encodedUrl}}&amp;width&amp;layout=standard&amp;action=like&amp;show_faces=false&amp;share={{share}}&amp;height=35&amp;appId=263047413871308" style="border:none; overflow:hidden; height:35px;" ></iframe>
  </template>
  <script>
    Polymer('facebook-button', {
      share: true,
      encodedUrl: null,
      observe: {
        url: 'encode'
      },
      encode: function(){
        this.encodedUrl = encodeURIComponent(this.url);
      }
    });
  </script>
</polymer-element>

Beide Elemente haben große Unterschiede im Shadow DOM und dezent andere Features (via-Attribut beim Twitter-Button, share-Attribut beim Facebook-Widget) aber sie haben auch ein gemeinsames Feature: beide erzeugen (und verwenden) eine encodedUrl-Eigenschaft. Wenn nicht alle Social-Media-Buttons diese Eigenschaft benötigen, hat sie in der „Basisklasse“ nichts verloren, andererseits ist es auch etwas blöd, diesen Code doppelt vorliegen zu haben. Die Lösung besteht darin, die gemeinsamen Teile des Konfigurations-Objekts in ein eigenes Objekt auszulagern …

var encodedUrlMixin = {
  encodedUrl: null,
  observe: {
    url: 'encode'
  },
  encode: function(){
    this.encodedUrl = encodeURIComponent(this.url);
  }
};

… und dieses Objekt mit den individuellen Konfigurations-Objekten der Buttons zu kombinieren. Hierfür kann jede beliebige Mixin-Funktion verwendet werden; Polymer bringt in Form von Platform.mixin() auch eine mit, die so gut wie jede andere ist:

<polymer-element name="twitter-button" extends="social-media-button" attributes="via">
  <template>
    ...
  </template>
  <script>
    Polymer('twitter-button', Platform.mixin({
      via: 'sir_pepe',
    }, encodedUrlMixin));
  </script>
</polymer-element>


<polymer-element name="facebook-button" extends="social-media-button" attributes="share">
  <template>
    ...
  <script>
    Polymer('facebook-button', Platform.mixin({
      share: true
    }, encodedUrlMixin));
  </script>
</polymer-element>

Und schon funktionieren beide Buttons! Der Schlüssel zu zwischen Komponeten geteilter Funktionalität ist also die Kombination von normaler „Vererbung“ und flexiblen Mixins.

Fazit

Werden dank Web Components viele komische neue Dinge als zweifelhafte HTML-Element umgesetzt werden? Vermutlich. Als unsemantische Div-Suppe muss das Ganze allerdings nicht enden, da, wie wir gesehen haben, sich auch einfach native Elemente erweitern lassen. Und für komplexere Funktionalitäts-Transplantationen stehen Mixins bereit, die ein ganz eigener Teil der Web-Component-Welt werden können. Es ist also alles halb so wild.

Sieben ausgewählte JSConf-Videos

Veröffentlicht am 24. Juli 2014

In letzter Zeit sind sehr viele neue Videos aus dem JSConf-Umfeld bei Youtube aufgeschlagen. Obwohl die Talks meist vergleichsweise kurz ausfallen (lobenswerterweise!) dürfte kein normaler Mensch die Zeit haben, sich alles anzusehen. Zum Glück bin ich nicht normal und habe in den letzten Wochen wirklich extrem viel Zeit in der Eisenbahn verbracht. Und ich habe mir währenddessen alles angesehen.

Die folgenden 7 Videos sind die, die mir am besten gefallen haben und aus denen ich am meisten für den Alltag mitgenommen habe. Das ist notwendigerweise ziemlich subjektiv (abgefahrene ES6-Experimente finde ich persönlich sehr viel spannender als enterprisige Scaling-Probleme) aber ich versichere euch, dass ihr mit dem Konsum der folgenden Talks nichts falsch macht. Die Videos sind nach meiner persönlichen Bewertung sortiert – was weiter oben steht, fand ich besser. Und sie sind alle kurz genug, um bequem in eine Mittagspause zu passen.

Guy Bedford: Package Management for ES6 Modules

Noch ein Package Manager? Vade retro satana! Aber Guy Bedford verheiratet mit JSPM die Gegenwart von JavaScript-Modulen (AMD, CommonJS, Globals) mit der Zukunft (ES6-Module) auf entzückend elegante Art und Weise. Egal wie das Modul verfasst ist, mit JSPM funktioniert es in jedem Fall. Ein Polyfill bringt der JS-Umgebung ES6-Module bei und der Moduloader ist qua ES6-Standard sowieso in der Lage angepasst zu werden, was in diesem Fall für all die verschiedenen DIY-Formate passiert. Ein Package Manager für Zukunft und Gegenwart.

James Long: Unshackling JavaScript with Macros

SweetJS stattet JavaScript mit Makros aus. So weit, so altbekannt; dass man SweetJS als eine Art Traceur Light verwenden kann, war zumindest mir nicht gegenwärtig. Das Package es6-macros implementiert die meiner Meinung nach wichtigsten ES6-Features (Destructuring, Klassen, Arrow Functions) ohne dass man mit dem Monstrum Traceur (und seinen heillos kaputten Grunt-Plugins) ringen müsste. Und natürlich kann man mit SweetJS noch mehr anstellen, was James Long in epischer Breite erklärt.

Neil Green: Writing Custom DSLs

Eine eigene DSL bauen? „Brauche ich nicht, kann ich nicht“ war mein erster Gedanke. Doch Neil Green zeigt sehr schön auf, wie falsch dieser Gedanke ist. Vom Nutzen von DSLs über die Planung mit dem Endnutzer bis hin zur konkreten Umsetzung wird kein Schritt auf dem Weg ausgelassen, so dass man sofort seine eigene kleine Sprache bauen möchte.

John-David Dalton: Fearless Browser Testing

Der Autor von Lo-Dash erzählt aus seiner Test-Praxis – und die hat es in sich. Er hat den Anspruch, dass seine Library auch in den ollsten Browsern und ungewöhnlichsten Umgebungen funktioniert und führt zu diesem Zwecke allerlei Tricks und Tools zu Felde. Wenn man vielleicht auch nicht jedes seiner Probleme selbst jederzeit hat (wandernde Timer in alten IE z.B.) ist seine Test-Infrastruktur doch sehr beeindruckend und inspirierend.

Nick Bray: Native Code on the Web

Wenn ein Google-Mitarbeiter über „Native Code on the Web“ erzählt, befürchtet man zunächst eine Werbeveranstaltung für Native Client. Tatsächlich ist dieser Talk jedoch ein schön nerdiger Rundumschlag, der auch ASM.js und der konkreten Umsetzung von Webapps mit nativem Kern Zeit widmet. Was Nick Bray erzählt und zeigt ist für die tägliche Arbeit von Otto Normalentwickler vermutlich nicht direkt relevant, ist aber unter dem Gesichtspunkt Nerd-Sport sehr beeindruckend.

Christoph Burgmer: Hacking a HTML renderer in plain browser-side JS

Und nochmal Frontend-Extremsport in seiner feinsten Form. Christoph Burgmer kombiniert über 9000 verschiedene Hacks um in seinem Browser einen Browser zu bauen. Yo dawg.

Ryan Florence: Embularactymerbone

Ein kurzer Vergleich zwischen Ember, Angular, React, Polymer, und Backbone. Ich weiß nicht was Polymer in dieser Reihe zu suchen hat, aber trotzdem: schneller als in diesem 30-Minuten-Video bekommt man nicht die Unterschiede zwischen all diesen Frameworks erklärt. Kann man sich gut ansehen, wenn man sich mit den einzelnen Tools noch nicht befasst hat.