{ "version": "https://jsonfeed.org/version/1.1", "title": "fboës - Der Blog | Artikel mit dem Tag \"Web-Components\"", "home_page_url": "https://journal.3960.org/", "feed_url": "https://journal.3960.org/tagged/web-components/feed.json", "description": "Programmierung, Luft- & Raumfahrt, Kurioses: Der Blog von und mit Frank Boës.", "icon": "https://cdn.3960.org/favicon-192x192.png", "favicon": "https://cdn.3960.org/images/tile-128x128.png", "author": { "name": "Frank Boës", "url": "mailto:info@3960.org" }, "authors": [ { "name": "Frank Boës", "url": "mailto:info@3960.org" } ], "language": "de-DE", "_rss": { "about": "http://cyber.harvard.edu/rss/rss.html", "copyright": "© 2008-2023 Creative Commons BY" }, "items": [ { "id": "user/posts/2023-03-05-24h-uhr-als-web-component/index.md", "url": "https://journal.3960.org/posts/2023-03-05-24h-uhr-als-web-component/", "title": "Die 24h-Uhr – als Web Component", "content_html": "
Web Components zu bauen ist gar nicht so kompliziert, selbst ohne Framework. Nach meinen vorherigen Überlegungen zur Verwendung von SVGs in Web Components war es also höchste Zeit, eine 24-Stunden-Uhr zu bauen.
\n\n24h-Uhren sind analoge Uhren, die einen feinen Unterschied zu herkömmlichen Uhren aufweisen. An Stelle von 12 Stunden pro Umdrehung des Stundenzeigers zeigen sie 24 Stunden an. Damit braucht also der Stundenzeiger einen kompletten Tag für einen Vollkreis.
\nDamit ist es möglich, die vierundzwanzig Stunden auf dem Ziffernblatt grafisch in Tag, Dämmerung und Nacht einzuteilen. Da diese sich in Bezug auf das Datum und die geografische Position verändern, brauchte die Uhr also auch diese Informationen. In meinem Fall habe ich eine Skala auf dem Stundenring angebracht, die sich für die Nacht dunkelblau, für die Dämmung mittelblau und für den Tag hellblau verfärbt.
\n\n
Der Bau als Web Component erlaubt es nun, die Uhr mit relativ wenig HTML in eine beliebige Seite einzubauen. Unter der Github-Seite zur 24h-Uhr findet sich eine knappe Anleitung, der Einbau einer Web Component muss aber nicht komplizierter sein als:
\n<twentyfour-hours-clock></twentyfour-hours-clock>\n
\nWeitere Attribute erlauben es, die Component vorher mit Werten zu bestücken – die sich auch im Nachhinein mit JavaScript ändern lassen:
\n<twentyfour-hours-clock width="128" height="128" datetime="2011-10-10T14:48:00" longitude="auto" latitude="auto"></twentyfour-hours-clock>\n
\nAuch das Einfärben, Vergrößern und Verkleinern von Einzelteilen der Web Component sind mit CSS Custom Properties möglich:
\n<style>\ntwentyfour-hours-clock {\n --color-watchhand: pink;\n --color-night: #111111;\n}\n</style>\n<twentyfour-hours-clock></twentyfour-hours-clock>\n
\nDie Web Component selber wurde mit TypeScript gebaut. Tatsächlich wäre auch der Bau direkt in Vanilla JavaScript möglich gewesen. Meine letzten Abenteuer in TypeScript sind aber so angenehme Erfahrungen gewesen, dass ich die Unterstützung bei der Typisierung in JavaScript nicht mehr missen möchte, und die Web Component in TypeScript gebaut habe. Inzwischen halte ich bei JavaScript die Verwendung von TypeScript für so hilfreich, dass ich auf ESLint zur Überprüfung meiner Programmierung bei Privatprojekten gerne verzichte.
\nEin funktionierendes Beispiel für die fertige Uhr findet sich auf der Demonstrationsseite für die 24h-Uhr.
", "summary": "Web Components zu bauen ist gar nicht so kompliziert, selbst ohne Framework. Nach meinen vorherigen Überlegungen zur Verwendung von SVGs in Web Components war…", "date_published": "2023-03-05T18:56:12+01:00", "date_modified": "2023-03-05T18:56:12+01:00", "author": { "name": "Frank Boës", "url": "mailto:info@3960.org", "avatar": "https://www.gravatar.com/avatar/71fcf51cf2ae9acdd54182d3e367ceca" }, "authors": [ { "name": "Frank Boës", "url": "mailto:info@3960.org", "avatar": "https://www.gravatar.com/avatar/71fcf51cf2ae9acdd54182d3e367ceca" } ], "banner_image": "https://journal.3960.org/posts/2023-03-05-24h-uhr-als-web-component/24h-clock.png", "language": "de-DE", "image": "https://journal.3960.org/posts/2023-03-05-24h-uhr-als-web-component/24h-clock.png", "tags": [ "Webdevelop", "Web-Components", "SVG", "Javascript", "CSS", "Geografie", "Outdoor", "Programmierung", "Technologie" ] }, { "id": "user/posts/2020-08-30-pragmatisches-css-fuer-amp-layouts/index.md", "url": "https://journal.3960.org/posts/2020-08-30-pragmatisches-css-fuer-amp-layouts/", "title": "Pragmatisches CSS für AMP-Layouts", "content_html": "Mit Accelerated Mobile Pages hat eine inzwischen nur noch lose an Google angekoppelte Stiftung einen Standard verabschiedet, der das Internet deutlich schneller machen soll – vor allen Dingen im mobilen Bereich. Die Einschränkungen im Bereich Layout (u.a. in Bezug auf CSS) sind für Entwickler aber eine gewisse Herausforderung.
\nZum Glück gibt es SASS – damit können wir ein bestehendes Layouts für eine „konventionelle“ Site pragmatisch für AMP übernehmen und anpassen, ohne ein komplett eigenständiges AMP-Layout bauen zu müssen.
\n\nDazu muss man wissen, wie AMP dafür sorgt, dass die in AMP geschriebenen Seiten schnell werden:
\n<script>
, <iframe>
).Für die Verwendung von AMP gibt es zwei Strategien:
\nWenn wir uns für die Strategie „AMP-Duplikat“ entscheiden, haben wir auf einmal zwei Template-Sets (reguläres HTML und AMP), und deswegen auch zwei verschiedene Layouts. Natürlich können wir versuchen, auf beiden Seiten auch das selbe Layout zu verwenden – aber wie schaffen wir es, die Größenbeschränkung von AMP einzuhalten, und für die eigenen AMP-Tags eigene Styles zu schreiben, ohne zwei verschiedene CSS-Dateien schreiben zu müssen?
\nHier kommt SASS ins Spiel: Mit diesem CSS-Preprocessor können wir nicht nur eine CSS-Datei erzeugen – wir können auch zwei CSS-Dateien erzeugen. Dabei machen wir uns zu nutze, dass SASS jede .scss
-Datei ohne _
am Anfang des Dateinamens in eine CSS-Datei kompiliert.
Unser Setup ist also:
\nstyles.scss
enthält das Layout für die regulären HTML-Seiten, erzeugt styles.css
amp.scss
enthält das Layout für die AMP-Seiten, erzeugt amp.css
SASS bietet darüber hinaus an, mittels @import
den Inhalt anderer SASS-Dateien zu inkludieren. Das machen wir uns im Falle der amp.scss
zu nutze:
// amp.scss\n\n@import "styles";\n
\nJetzt wird SASS die amp.scss
zu einer amp.css
kompilieren, die das selbe Ergebnis hat wie styles.scss
bzw. styles.css
.
Ein Konzept, mit dem jeder CSS-Entwickler vertraut ist, sind Media Queries: Als CSS-Entwickler sperren wir CSS-Regeln in diese Queries ein, um je nach Ausgabegerät bzw. -kanal ein unterschiedliches Layout zu erzwingen.
\nGenau so denken wir nun AMP-CSS: Wir klammern in unserer SASS-Datei styles.scss
nun Blöcke ein, die nicht (oder nur) in der amp.css
auftauchen sollen. Dazu schreiben wir uns zwei kleine Mixins:
// styles.scss\n\n$amp: false !default;\n\n// Only output this block in AMP pages.\n@mixin amp() {\n @if($amp) {\n @content;\n }\n}\n\n// Only output this block in non-AMP pages.\n@mixin no-amp() {\n @if(not($amp)) {\n @content;\n }\n}\n
\nDiese beiden Mixins reagieren auf eine Variable namens $amp
. Im oberen Mixin wird der an das Mixin übergebene Inhalt nur ausgegeben, wenn $amp: true
ist – im unteren Beispiel wird er nur ausgeben, wenn $amp: false
ist.
Davor definieren wir die Variable $amp
. Wichtig ist hier die Verwendung von !default
. Damit verraten wir SASS, dass der Wert dieser Variable nur false
sein soll, wenn $amp
nicht bereits vorher gesetzt wurde.
Übrigens können wir die Mixins auch in eine eigene Datei auslagern (zum Beispiel _mixins.scss
), das Prinzip bleibt aber das gleiche.
Unsere styles.scss
soll nun alle AMP-Regeln nicht ausgeben, und alle Nicht-AMP-Regeln ausgeben. Dementsprechend setzen wir $amp
zu false
.
Derweil in der amp.scss
:
// amp.scss\n\n$amp: true;\n@import "styles";\n
\nHier setzen wir also $amp
auf true
und holen uns danach die Inhalte von styles.scss
– hier wird das dort definierte $amp
überschrieben, und danach die styles.scss
von SASS ganz normal weiter kompiliert. Damit haben wir die selben Inhalte, aber die Mixins werden sich nun genau umgekehrt verhalten.
Damit können wir nun alle CSS-Regeln für unsere regulären HTML-Seiten und AMP-Seiten in der style.scss
schreiben. An den Stellen, wo eine Unterscheidung zwischen regulären und AMP-CSS notwendig wird, verwenden wir die Mixins:
// styles.scss\n\n// AMP & regular CSS\nnav {\n a {\n color: red;\n }\n}\n\n// Hide rule for AMP CSS\n@include no-amp() {\n header {\n background-color: black;\n color: white;\n }\n}\n\nfooter {\n background-color: black;\n color: white;\n // Hide partial rule for AMP CSS\n @include no-amp() {\n font-size: 0.6em;\n }\n}\n\n// Hide rule for non-AMP CSS\n@include amp() {\n amp-img {\n border: 1px solid red;\n }\n}\n
\nDamit müssen viele CSS-Regeln nur einmal definiert werden, und werden in beiden Layouts identisch ausgegeben. Dieser Artikel zum Beispiel verwendet im regulären HTML und AMP-HTML weitestgehend identisches CSS, und kann trotzdem auf die Besonderheiten und Kompressionswünsche von AMP eingehen.
\namp.css
montierenDas so produzierte amp.css
dürft ihr gemäß den Vorschriften von AMP für die Integration von CSS nicht als separates Stylesheet laden. Stattdessen erreicht AMP seine hohe Geschwindigkeit durch die Tatsache, dass das gesamte AMP-CSS in die HTML-Seite eingebunden werden muss. Hier müssen wir also nur eine kleine Template-Anpassung in den AMP-Templates durchführen, die den Inhalt der amp.css
im AMP-Tag <style amp-custom>
am Beginn einer jeden AMP-Seite montiert.
<!doctype html>\n<head>\n …\n <style amp-custom>\n /* Content of amp.css goes here. */\n </style>\n …\n</head>\n
\nÜbrigens empfiehlt es sich spätestens hier, alle überflüssigen Leerzeichen aus der amp.css
zu entfernen, um weiter Platz zu sparen. Eine Ersetzung mit einem regulären Ausdruck (wie z.B. /\\n\\s+/
) lässt hier weiter die Luft raus, oder ihr setzt für den SASS-Compiler den Schalter compressed
.
Der Artikel „How we build the site and use Web Components“ deutet im Nebensatz eine wunderbare Methode an, um in Content-Management-Systemen auf Markdown-Basis redaktionell Web Components zum Einsatz zu bringen.
\n\nMarkdown hat eine oft gar nicht benötigte Eigenschaft: Inline-HTML. In Markdown auftretendes HTML wird nach dem Umwandeln von Markdown in HTML vollkommen unverändert wieder ausgegeben. Also wird folgendes Markdown…
\nThis **Markdown example** will <i>output HTML tags</i> as well.\n
\n…zu folgendem HTML:
\n<p>This <strong>Markdown example</strong> will <i>output HTML tags</i> as well.</p>\n
\nWir erinnern uns: Web Components sind durch JavaScript-Bibliotheken definierte HTML-Tags, die mit ihrem eigenen Verhalten und Layout versehen sind. So kann man Web Components wie HTML im Markdown verwenden, z.B. word-count
:
<word-count>\n Lorem ipsum _dolor_ sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et _dolore magna_ aliquyam erat, sed diam voluptua.\n</word-count>\n
\n…wird damit zu…
\n<word-count>\n <p>Lorem ipsum <em>dolor</em> sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et <em>dolore magna</em> aliquyam erat, sed diam voluptua.</p>\n</word-count>\n
\n…was wiederum im Browser dann den obigen Absatz mit einem kleinen Wort-Zähler dahinter anzeigt.
\nDieser Weg funktioniert unabhängig von der Art des eingesetzten Content Management Systems, solange das CMS redaktionelles Markdown verwendet. Für den Redakteur ist diese Lösung so einfach oder so kompliziert wie einen Wordpress-Shortcode einzusetzen.
\nDazu muss die zu verwendende Web-Component natürlich vorher geladen sein. Dies kann entweder im Template mit einem simplen <script>
-Aufruf geschehen – oder (je nach Sicherheitseinstellung des CMS') auch direkt im Markdown des Artikels:
<script type="module" src="https://unpkg.com/@lrnwebcomponents/word-count@2.6.5/word-count.js?module"></script>\n
\nSo oder so steht Redakteuren nun eine hinreichend bedienbare Methode zur Verfügung, in ihren Artikel Web Components zu verwenden. Alleine für diesen Zweck hatte ich schon ein paar Ideen, welche Web Components Redakteuren helfen könnten:
\n<twitter-tweet>
zum Einbetten von Tweets<video-embed>
zum Einbetten von Videos, zum Beispiel YouTube<map-embed>
zum Einbetten von Karten, wie z.B. Google Maps<pull-quote>
zum Anzeigen von aus dem Text herausgezogenen Zitaten<linkbox-category>
oder <linkbox-tag>
zum Einbetten einer Linkbox, die verwandte Artikel anzeigt<chess-board>
oder <bar-chart>
zum Darstellung von Spezialgrafiken wie einem Schachbrett oder Säulengrafiken (wir erinnern uns: Web Components können auch SVG-Grafiken anzeigen)Zu berücksichtigen wäre nur, dass einige ältere Browser Web Components nicht darstellen können. Das betrifft zum Beispiel auch einige RSS-Reader und E-Mail-Clients, so dass Artikel in diesem Umfeld nur eingeschränkt dargestellt werden. Das kann aber sogar erwünscht sein – denn dann wird das innerhalb des Web-Component-Tags verwendete Markdown bzw. HTML ausgegeben.
", "summary": "Der Artikel „How we build the site and use Web Components“ deutet im Nebensatz eine wunderbare Methode an, um in Content-Management-Systemen auf Markdown…", "date_published": "2020-04-10T19:32:33+02:00", "date_modified": "2020-04-10T20:34:10+02:00", "author": { "name": "Frank Boës", "url": "mailto:info@3960.org", "avatar": "https://www.gravatar.com/avatar/71fcf51cf2ae9acdd54182d3e367ceca" }, "authors": [ { "name": "Frank Boës", "url": "mailto:info@3960.org", "avatar": "https://www.gravatar.com/avatar/71fcf51cf2ae9acdd54182d3e367ceca" } ], "banner_image": "https://cdn.3960.org/favicon-192x192.png", "language": "de-DE", "image": "https://cdn.3960.org/favicon-192x192.png", "tags": [ "Webdevelop", "Web-Components", "Blog", "Javascript", "Programmierung", "Technologie" ] }, { "id": "user/posts/2020-04-07-ideen-fuer-web-components/index.md", "url": "https://journal.3960.org/posts/2020-04-07-ideen-fuer-web-components/", "title": "Ideen für Web Components", "content_html": "Schlaue Beispiele gefällig, was man mit Web Components alles bauen könnte, um HTML zu erweitern?
\n\nWeb Components eignen sich ganz hervorragend, um von der Darstellung und/oder Funktionalität her komplexe Aufgaben gekapselt und wiederverwendbar zu lösen. Zudem können Web Components (ähnlich wie <iframe>
, <object>
und viele andere neue Tags) in ihrem Inneren einen Fallback enthalten, so dass Browser ohne die Fähigkeit zur Darstellung von Web Components trotzdem etwas anzuzeigen haben.
Außerdem können Web Components dieses Fallback gleichzeitig via slot
-Mechanismus auswerten, um die Fallback-Inhalte entweder wieder anzuzeigen, oder aber aus dem HTML noch weitere Informationen zu ziehen.
Im Sinne des progressive enhancement könnte man also mit Web Components viele alltägliche Probleme lösen:
\n<twitter-status status="https://twitter.com/Interior/status/463440424141459456">\n <blockquote>Sunsets don't get much better than this one over @GrandTetonNPS. #nature #sunset</blockquote>\n <cite>US Department of the Interior</cite>\n</twitter-status>\n
\nDiese Komponente zieht datenschutzzkonform Tweets von Twitter – und als Fallback braucht sie gar keine weiteren Requests, da der Content bereits auf der Seite zu sehen ist. 😉
\n<video-embed url="https://www.youtube.com/embed/G2dGWH90aew?autoplay=1">\n <a href="https://www.youtube.com/embed/G2dGWH90aew?autoplay=1">Youtube-Video</a>\n</video-embed>\n
\nDiese Komponente verwandelt mittels einfacher Magie den Link zu einem Video in eine Video-Einbettung. Redakteure werden es lieben, nicht mehr mit Embed-Codes herumzuhantieren, sondern einfach den Link zum Video aus der URL-Leiste ihres Browsers kopieren zu können – und trotzdem im fertigen Artikel ein eingebettetes Video zu sehen. (Ein Trick, den das Blogophon auch beherrscht.)
\nMit lite-youtube
schon ähnlich umgesetzt: Mit dieser Komponente bekommt man eine deutlich datenschutzkonforme Darstellung eines eingebetteten Videos von YouTube, Viemo oder ähnlichem – ähnlich wie in diesem Blog YouTube-Videos in die Seite eingebettet werden.
<code-highlighted lang="javascript">\n <pre>\n <code>\n …\n </code>\n </pre>\n</code>\n
\nMit dieser Komponente kann ich Code-Beispiel mit Syntax-Highlighting versehen. In dem lang
-Attribut wird dabei die verwendete Programmiersprache vermerkt.
<map-embed provider="google-maps" coordinates="53.246, 10.412">\n <a href="https://www.google.de/maps/place/53°14'45.6"N+10°24'43.2"E">Die wunderschöne Stadt Lüneburg</a>\n</map-embed>\n
\nKarten in redaktionellen Kontext sind immer wieder hilfreich, um Orte und ihre Umgebung darzustellen. Als Fallback kann immer noch ein Link zu einer Karten-Plattform angeboten werden.
\n<input-coordinates provider="google-maps">\n <input name="coordinates" type="coordinates" step="0.001" value="53.246, 10.412" />\n</input-coordinates>\n
\nGetreu meiner Idee, ein Eingabefeld für Geo-Koordinaten in HTML anzubieten, könnte diese Komponente eine Geo-Lokalisierung und Kartendarstellung beinhalten, um den Nutzer die Auswahl eines Standorts zu erlauben.
\n<textarea-special type="text/html">\n <textarea name="html">\n …\n </textarea>\n</textarea-special>\n
\nWie lange haben wir schon auf einen einfach im Browser funktionierenden Rich-Text-Editor gewartet. Die Komponente ist sogar so schlau, das generierte HTML wieder in das über ihm liegende Formular zurückzugeben.
\nTheoretisch könnte sie ja aber auch ganz andere Dinge zur Bearbeitung anbieten. text/markdown
? text/csv
? text/yaml
? Warum nicht? Das könnte man alles über das type
-Attribut lösen.
Web Components erlauben in modernen Browsern, eigene Tags mit beliebig komplexen Verhalten zu definieren. Und wie der Name „Komponente“ schon nahelegt, kann man diese kleinen Bibliotheken beliebig oft weiterverwenden, sobald diese einmalig geladen wurden.
\nMit einigen kleinen Kniffen kann die Entwicklung solcher Komponenten noch schneller von der Hand gehen.
\n\nFür den Einstieg in das Thema Web Components empfiehlt sich die Lektüre der ausgezeichneten Einführung von Google und CSS-Tricks. Andere Anleitungen basieren teilweise auf älteren Ideen, die getrost ignoriert werden können.
\nUpdate 2020–09: Die Vielzahl der Wege, wie man eine Web Component bauen kann, hat webcomponents.dev veranlasst, eine Auflistung aller möglichen Wege zum Erstellen einer Web Component zusammenzustellen.
\nAußerdem lohnt es sich immer, moderne Beispiele anzuschauen. Ich für meinen Teil habe entsprechend versucht, einen Horizontal Situation Indicator als Vanilla-JavaScript Web Component mustergültig zu bauen. Der daraus resultierende Quellcode der „Horizontal Situation Indicator“ Web Component wird im Rahmen dieses Artikels immer wieder als Beispiel herangezogen.
\n\nDie größte Hürde für den angehenden Komponenten-Bauer ist das Verständnis, wie all die schönen Teile zusammenpassen.
\nDa eine Web Compoment gekapselt ist, kann nur über eine vorher definierte Schnittstelle von außen Zugriff auf ihr Verhalten genommen werden. Es ist also sehr sinnig, die Konzeption und den Bau einer Web Component wie den Bau einer Schnittstelle bzw. eines Interfaces zu verstehen.
\nAls Beispiel nehmen wir einfach die fertige Implementation der HSI-Web-Component:
\n<horizontal-situation-indicator id="hsi" heading="45.0" heading-select="0.0"></horizontal-situation-indicator>\n
\nBeim Bau einer Web Component müssen neben dem Namen der Web Component auch die Attribute der Web Component und ihre möglichen Werte definiert werden.
\nDiese Attribute verwandeln sich innerhalb der JavaScript-Repräsentation der Web Component in Properties, die mit den Attributen synchronisiert sind:
\nconst el = document.getElementById('hsi');\nel.getAttribute('heading'); // "45.0"\nel.heading; // "45.0"\n\nel.setAttribute('heading', '60.0');\nel.getAttribute('heading'); // "60.0"\nel.heading; // "60.0"\n\nel.heading = '135.0';\nel.getAttribute('heading'); // "135.0"\nel.heading; // "135.0"\n
\nBei dem Zugriff auf eine Property mit einem -
im Namen funktioniert der Zugriff leicht anders:
el.getAttribute('heading-select');\n//el.heading-select existiert nicht\nel['heading-select']; // korrekte Schreibweise in `[]`\n
\nDie Schreibweise mit []
erlaubt auch den dynamischen Zugriff auf Properties:
let attrName = 'heading-select';\n\nel.getAttribute(attrName);\nel[attrName];\n
\nDarüber hinaus kann der Entwickler einer Web Component noch festlegen, dass die Komponente JavaScript-Methoden anbietet. Diese erlauben zum Beispiel von außen der Komponente zu befehlen, komplexe Prozesse innerhalb der Komponente zu erledigen.
\nel.synchronizeHeading();\n
\nDa JavaScript keine Sichtbarkeiten wie public
und private
für Methoden hat, hat sich als Konvention herausgebildet, private Methoden mit einem _
zu beginnen.
Außerdem kann eine Web Component noch JavaScript-Events erzeugen, die außerhalb der Komponente registriert werden können. So emittiert z.B. das <video>
-Tag Events, wenn ein Video beendet wurde, was ohne dieses Event außerhalb des Tags keiner wissen könnte.
Web Components können fast alles darstellen, was auch regulär in einem Browser dargestellt werden kann. Besonderes Augenmerk muss aber darauf gelegt werden, dass alle benötigten Bestandteile in der einen JavaScript-Datei enthalten ist, die die Web Component definiert.
\nAm Einfachsten einzubinden sind die folgenden Dinge:
\nAlle anderen Ressourcen (Bilder, Videos, Töne) können mit einem Trick eingeschmuggelt werden: Mittels Data URLs können Binär-Dateien in Base64-Zeichenketten umgewandelt werden, die dann z.B. im src
-Attribut eines <img>
eingebunden werden können.
Besonders spannend: SVG-Bilder sind nicht nur schön kompakt in Bezug auf ihren Speicherplatz, sondern können auch direkt in das HTML eingebunden werden – benötigen also den Base64-Trick nicht.
\nIn der Tat gibt es eine ganze Menge Tools, um das Zusammensetzen der einzelnen Teile einer Web Component zu unterstützen. In der Regel reicht aber ein kleiner flotter Node.js-Mehrzeiler als Web Component Build Tool, der aus einzelnen Dateien die eigentliche Web Component zusammensetzt. Die Kurzfassung:
\nconst fs = require('fs');\n\nlet source = fs.readFileSync(`horizontal-situation-indicator.js`).toString();\nlet templateCss = fs.readFileSync(`src/horizontal-situation-indicator.css`).toString();\nlet templateSvg = fs.readFileSync(`src/horizontal-situation-indicator.svg`).toString();\n\nsource = source.replace(/(<style>).*(<\\/style>)/ms, templateCss);\nsource = source.replace(/(<\\/style>).*(`)/ms, templateSvg);\nfs.writeFileSync(`horizontal-situation-indicator.js`, source);\n
\nDas tatsächliche Skript ist zwar etwas komplexer, das Grundprinzip ist aber ein denkbar einfaches: Die Entwicklung von SVG und CSS (oder jedem anderen Dateitypen) findet in separaten Dateien statt, die mit dem obigen Skript einfach in das JavaScript der Web Component hineinkopiert werden. Unter anderem könnte hier auch die Konvertierung von Binär-Daten in ihre Base64-Entsprechung durchgeführt werden.
\nget
und set
für jede Property abkürzen?Da jeder Web Component eine Liste der zu synchronisierenden Attribute / Properties mit der Methode observedAttributes
bekannt gemacht werden muss, kann genau diese Liste im constructor
auch zum programmatischen Erzeugern von Gettern / Settern verwendet werden.
this.constructor.observedAttributes.forEach((attrName) => {\n Object.defineProperty(this, attrName, {\n get() {\n return this.getAttribute(attrName);\n },\n set(attrValue) {\n this.setAttribute(attrName, attrValue);\n }\n });\n});\n
\nDiese Methode hat in einigen Web-Components-Frameworks möglicherweise Nachteile – für die Vanilla-Nutzung ist sie aber weitestgehend ungefährlich.
\nDa Web Components sowieso nur in aktuellen Browsern zuverlässig funktionieren, kann man sich gleichzeitig auch auf fortgeschrittene CSS-Möglichkeiten verlassen. Um CSS innerhalb der Komponente von außen zu beeinflussen, verwende ich CSS-Variablen bzw. CSS-Custom-Properties. Innerhalb des CSS' des Komponente definiere ich sie direkt an der DOM-Wurzel der Komponente:
\n:host {\n --background-color: black;\n --foreground-color: white;\n --heading-select-color: cyan;\n --stroke-width: 0.5;\n}\n/*…und verwende diese CSS-Custom-Properties dann später in Variablen - bei mir z.B. als SVG-CSS-Eigenschaften:*/\n\n#background {\n fill: var(--background-color);\n}\n* {\n fill: var(--foreground-color);\n}\n*[stroke] {\n stroke-width: var(--stroke-width);\n}\n\n#heading-select {\n fill: var(--heading-select-color);\n}\n
\nWer nun auch immer diese Komponente verwendet, kann diese CSS-Custom-Properties von außen beeinflussen:
\nhorizontal-situation-indicator {\n --heading-select-color: red;\n --stroke-width: 1;\n}\n
\nBei der Beispiel-Implementation von <horizontal-situation-indicator>
kann auch bewundert werden, wie durch JavaScript diese CSS-Custom-Properties am lebenden Objekt verändert werden, und in der Komponente sich alles fröhlich verfärbt.
Ganz nebenbei haben wir für die Komponente eine weitere Schnittstelle geschaffen – in diesem Fall eine Styling-Schnittstelle.
\nUpdate: Andererseits können aber auch einzelne DOM-Knoten ohne CSS-Properties zum expliziten Styling freigegeben werden. Eine Anleitung zum Freigeben von DOM-Knoten aus dem Shadow-DOM zum CSS-Styling bei CSS-Tricks zeigt die notwendigen Anpassungen im HTML:
\n<div part="style-me">…</div>\n
\n…und dem CSS im Eltern-Dokument:
\nhorizontal-situation-indicator::part(style-me) {\n font-weight: bold;\n}\n
\nAuch hier hat wieder der Autor der Komponente die Herrschaft über die Elemente, die er nach außen freigibt – wie bei einer Schnittstelle.
\nDer eigentliche Clou der HSI Web Component ist die generelle Fähigkeit von JavaScript, DOM-Elemente und ihre Eigenschaften zu verändern. Dies können sowohl CSS-Eigenschaften als auch generelle Attribute von DOM-Elementen sein.
\nBei SVG bieten sich die folgenden Operationen an:
\nstroke
und stroke-width
zur Beeinflussung von Linienfill
zur Veränderung der Füllfarbeopacity
zur Veränderung der Durchsichtigkeit eines Elementstransform
<text>
-Knoten mittels .textContent
Bei SVG gibt es dabei die Möglichkeit, nicht nur via CSS diese Eigenschaften zu beeinflussen, sondern auch durch das Setzen von Attributen innerhalb des SVGs an einzelnen SVG-DOM-Knoten.
\nAuch das ist in der Beispiel-Implementation von <horizontal-situation-indicator>
zu bestaunen – hier sind die Attribute der Komponente mit Animationsmethoden verknüpft, so dass Änderungen an den Attributen bzw. Properties der Web Component zeitgleich die Darstellung des eingeschlossenen SVGs ändert.
Da eine Web Component im Endeffekt eine Schnittstelle ist, muss es dazu eine Schnittstellen-Dokumentation geben. Ohne diese Dokumentation können andere Entwickler, die die Komponente verwenden möchten, nicht zuverlässig wissen, wie die Komponente zu bedienen ist.
\nAls Minimum muss eine Dokumentation enthalten:
\n## Properties\n\n| Name | Type | Default | Description |\n| -------------- | ------- | ------- | ------------ |\n| `heading` | `float` | `null` | Lorem ipsum… |\n\n## Methods\n\n| Name | Parameters | Description |\n| -------------- | ---------- | ------------------- |\n| `revHeading` | none | Lorem ipsum… |\n\n## Events\n\n| Name | Description |\n| -------------- | -------------------------------- |\n| `synchronized` | Lorem ipsum… |\n\n## Styling\n\n```css\ncomponent-name {\n --background: color; /* Lorem ipsum… */\n}\n\ncomponent-name::part(part-name) {} /* Lorem ipsum… */\n```\n
\nDer fertige Horizontal Situation Indicator als Web Component ist in einem GitHub-Repository gelandet, und einen Blick auf die fertige Implementation der HSI-Web-Component erlaubt einen interaktiven Blick auf die Zusammenhänge in der Komponente.
", "summary": "Web Components erlauben in modernen Browsern, eigene Tags mit beliebig komplexen Verhalten zu definieren. Und wie der Name „Komponente“ schon nahelegt, kann…", "date_published": "2020-04-05T18:41:26+02:00", "date_modified": "2023-03-05T10:14:07+01:00", "author": { "name": "Frank Boës", "url": "mailto:info@3960.org", "avatar": "https://www.gravatar.com/avatar/71fcf51cf2ae9acdd54182d3e367ceca" }, "authors": [ { "name": "Frank Boës", "url": "mailto:info@3960.org", "avatar": "https://www.gravatar.com/avatar/71fcf51cf2ae9acdd54182d3e367ceca" } ], "banner_image": "https://journal.3960.org/posts/2020-04-05-svg-web-components/hsi.png", "language": "de-DE", "image": "https://journal.3960.org/posts/2020-04-05-svg-web-components/hsi.png", "tags": [ "Webdevelop", "Web-Components", "SVG", "Javascript", "CSS", "Programmierung", "Technologie", "The Cool", "Fliegerei" ] } ] }