Ich nutze meine Mastodon-Präsenz vor allem, um dumme Fragen zu stellen und laut nachzudenken. Fragenstellen und Nachdenken führt zu Erkenntnisgewinn (in meinem Fall meist in Sachen Browserbugs und Webstandards) und dieser Artikel fasst meine Erkenntnis-Highlights aus dem Januar in etwas organsierterer Form zusammen. Top-Fundstück des Monats waren definitiv die bereits in einem eigenen Artikel verarbeiteten CompressionStreams, aber der Rest kann sich auch sehen lassen!
TextEncoder und TextDecoder
Mir war neu, dass alle Browser unter der Sonne TextEncoder und TextDecoder unterstützen. Der Decoder schluckt Bytes und produziert Strings, der Encoder macht das Gegenteil:
const utf8bytes = new TextEncoder().encode("????"); // > Uint8Array(4) [ 240, 159, 164, 161 ] const string = new TextDecoder().decode(utf8bytes); // > "????"
Der Decoder kann natürlich auch mit mehr als UTF-8 umgehen und lief mir beim Austüfteln von CompressionStreams über den Weg.
Kein @import
im CSSStyleSheet()
-Constructor (und CSS-Modulen)
Ein Ansatz für CSS in Shadow DOM besteht darin, mit new CSSStyleSheet()
Stylesheets aus heißer Luft zu erzeugen und diese einem ShadowRoot (oder Document) zuzuweisen:
const host = document.querySelector("div"); const root = host.attachShadow({ mode: "open" }); root.innerHTML = `Hello`; const sheet = new CSSStyleSheet(); sheet.replaceSync(":host { color: red }"); // async-Alternative: replace() root.adoptedStyleSheets.push(sheet);
Die in diesem Stylesheet enthaltenen Regeln gelten dann ausschließlich in den ShadowRoots oder Dokumenten, deren adoptedStyleSheets
sie zugewiesen wurden. Zwar handelt es sich hierbei um grundsätzlich zweifelhafte JS-Hexerei auf CSS-Territorium, aber es mangelt nicht an Vorteilen:
- Web Components können ihre eigenen kleinen CSS-Dateien haben (anstelle von Strings in JavaScript)
- Wer sich etwas Mühe gibt, Memory Leaks zu umgehen, kann ein
CSSStyleSheet
-Objekt über mehrere Komponenten-Instanzen teilen - Unter Zuhilfenahme von Build-Tools kann CSS zur Compile-Zeit ins JavaScript gebundlet werden, falls das gewünscht ist
Allerdings musste ich feststellen, dass @import
in mit new CSSStyleSheet()
und CSS-Modulen genutzten Stylesheets nicht funktioniert.. Das Problem ist, dass jedes @import
traditionell ein Stylesheet von einer URL lädt und für mehrere Requests auf die gleiche Adresse komplett unterschiedliches CSS geliefert bekommen kann. Für ECMAScript-Module hingegen baut der Browser einmal einen Modul-Graph auf und lädt keine URL zweimal – zwei unterschiedliche Antworten innerhalb eines Ladezyklus sind also ausgeschlossen. CSS-Module wollen Syntax wie import css from './foo.css'
ermöglichen, doch hier kollidiert die Funktionsweise von ECMAScript-Modulen mit der von @import
. Wir erwarten von import
-Statements deterministische Ergebnisse und von @import
-Regeln das genaue Gegenteil. Die erwartbare Konsequenz: kein @import
in CSS-Modulen und auch kein @import
in mit new CSSStyleSheet()
erzeugten Stylesheets, in denen sich ein vergleichbarer Widerspruch manifestiert.
type
auf <textarea> und <select>
(und mehr)
Beim Zusammenstecken einiger Debug-Strings fiel mir auf, dass auf <textarea>
und <select>
das IDL-Attribut type
existiert:
const textarea = document.createElement("textarea"); console.log(textarea.type); // > "textarea" const select = document.createElement("select"); console.log(select.type); // > "select-one" const multiSelect = document.createElement("select"); multiSelect.multiple = true; console.log(multiSelect.type); // > "select-multiple"
Anders als bei <input>
, wo das Content-Attribut type
den Input-Typ bestimmt, ist das IDL-Attribut type
bei <textarea>
und <select>
read-only. Die Idee dahinter scheint zu sein, dass alle Formular-Elemente eine einheitliche API zum Ermitteln ihres Typs haben sollen, denn auch <output>
und <fieldset>
haben dieses Feature. Unter den verbliebenen form-associated elements haben <input>
, <button>
und <object>
ohnehin type
-Attribute und die einzigen Ausreißer sind <img>
(warum ist das überhaupt form-associated?) und eventuelle form-associated custom elements. Was lernen wir daraus?
- Formular-Elements können wir allein anhand ihres
type
auseinanderhalten. - Wenn wir form-assoicated custom elements bauen, sollten sich diese auch die Mühe machen, einen
type
-Getter zu implementieren, denn sonst funktioniert Punkt 1 nicht mehr.
Punkt 2 ist schon erfüllt, wenn wir einfach nur den folgenden Codeschnipsel in unsere Form-Element-Basisklassen einbauen:
export class FormBaseElement extends HTMLElement { // Boilerplate... get type() { return this.tagName.toLowerCase(); } // ... mehr Boilerplate... }
Damit funktioniert type
praktisch wie bei <textarea>
und erfüllt damit in 99% aller Fälle schon locker seinen Zweck!
Weitere Erkenntnisse und Fundstücke
- RSS-Parrot verwandelt Feeds in Mastodon-Accounts
- In Chrome feuern
reset
-Events nicht auf Formularen in einem neuen Dokument, gleiches gilt in Safari. Firefox feuert das Event, also hab ich das mal als Bug gemeldet. - Chrome kann seit über 6 Jahren FormData nicht mit dem Structured Clone Algorithm verdauen, Firefox hingegen schon
- In Chrome wird ein Element draggable im Sinne der HTML D&D-API, wenn es das Content-Attribut
draggable
hat. Im Firefox wird es aber auch draggable, wenn es in einen Shadow-Slot projiziert wird, der Kind eines Elements mitdraggable
-Attribut ist. Ich habe keine Bugs bei den BrowserN gemeldet, da ich nicht weiß, was der Soll-Zustand ist. - Das IDL-Gegenstück zum Content-Attribut
readonly
heißt... readOnly (mit großem „O“).
Folgt mir auf Mastodon, wenn ihr dem nächsten Erkenntnis-Paket live beim Entstehen zusehen wollt!