Gepubliceerd: 19 mei 2026
Het web is allang niet meer het statische, documentgedreven medium waarmee het begon. Moderne, geavanceerde webapplicaties worden door iedereen gebruikt voor allerlei doeleinden, van communicatie en online winkelen tot het consumeren van rijke content en het beheren van ons complexe leven.
Ondanks alle vooruitgang wordt HTML nog steeds van boven naar beneden geladen, zonder veel rekening te houden met wanneer de inhoud gereed is of wanneer de gebruiker deze consumeert. Met CSS kun je de volgorde van de inhoud wijzigen, maar dit gaat vaak ten koste van de toegankelijkheid. JavaScript stelt je in staat om de DOM te manipuleren via verschillende API's om dit enigszins te omzeilen, maar dit vereist vaak een omslachtige syntaxis of de constructie van DOM-structuren om in HTML te integreren.
Prestaties zijn ongelooflijk belangrijk voor het web, gezien het client-serverkarakter van het medium. Er worden echter vaak suboptimale keuzes gemaakt om deze volgorde van HTML te omzeilen, wat de prestaties vertraagt. Dit omvat bijvoorbeeld wachten tot de hele pagina gereed is of het gebruik van een zwaar framework om componenten asynchroon te leveren. De populariteit van JavaScript-frameworks laat zien dat webontwikkelaars de voorkeur geven aan een componentgebaseerd model boven het rigide documentmodel dat aan de basis stond van het web.
Het Chrome-team heeft zich over dit probleem gebogen en werkt aan nieuwe toevoegingen aan het webplatform onder de naam Declarative Partial Updates .
Twee nieuwe API-sets maken het eenvoudiger om HTML op een minder lineaire manier te leveren, ofwel door de elementen in een andere volgorde in het HTML-document zelf te plaatsen, ofwel door op eenvoudigere manieren dynamisch HTML in bestaande documenten in te voegen met behulp van nieuwe JavaScript-API's. Deze zijn vanaf Chrome 148 klaar voor ontwikkelaarstesten met de vlag chrome://flags/#enable-experimental-web-platform-features . Er zijn ook polyfills beschikbaar waarmee je deze nieuwe API's direct kunt gebruiken, zelfs in browsers die ze nog niet ondersteunen.
Deze toevoegingen aan het webplatform worden gestandaardiseerd, met positieve feedback van andere browserleveranciers en via verschillende standaardiseringskanalen. De relevante standaarden worden momenteel bijgewerkt om deze nieuwe API's op te nemen.
Streaming in een verkeerde volgorde
De eerste reeks wijzigingen betreft nieuwe API's voor streaming in een andere volgorde, waarbij gebruik wordt gemaakt van het HTML-element <template> en placeholders voor verwerkingsinstructies. Bijvoorbeeld:
<div>
<?marker name="placeholder">
</div>
...
<template for="placeholder">
Here is some <em>HTML content</em>!
</template>
Verwerkingsinstructies bestaan al lange tijd in XML, maar werden in HTML als commentaar behandeld en genegeerd. Deze nieuwe API verandert dat en brengt verwerkingsinstructies naar HTML. Wanneer de browser de verwerkingsinstructies <?marker name="placeholder"> ziet, gebeurt er niet direct iets – net als voorheen – maar ze kunnen later wel worden geraadpleegd.
Het <template> -element zoekt de bijbehorende verwerkingsinstructies op met behulp van een name attribuut en vervangt de inhoud. In dit geval ziet de DOM er na het parsen als volgt uit:
<div>
Here is some <em>HTML content</em>!
</div>
Naast het <?marker> -attribuut voor vervangingen zijn er ook <?start> en <?end> -bereikmarkeringen waarmee tijdelijke placeholder-inhoud kan worden weergegeven voordat de sjabloon wordt verwerkt:
<div>
<?start name="another-placeholder">
Loading…
<?end>
</div>
...
<template for="another-placeholder">
Here is some <em>HTML content</em>!
</template>
In dit geval wordt Loading… weergegeven totdat de <template> zichtbaar is, waarna het wordt vervangen door de nieuwe inhoud.
Het is ook mogelijk om verwerkingsinstructies in sjablonen op te nemen, zodat meerdere updates mogelijk zijn:
<ul id="results">
<?start name="results">
Loading…
<?end>
</ul>
...
<template for="results">
<li>Result One</li>
<?marker name="results">
</template>
...
<template for="results">
<li>Result Two</li>
<?marker name="results">
</template>
...
Na het parsen resulteert dit in de volgende HTML:
<ul id="results">
<li>Result One</li>
<li>Result Two</li>
<?marker name="results">
</ul>
Met de laatste verwerkingsinstructie aan het einde voor het geval er later nog meer <template for="results"> aan het document worden toegevoegd.
Demo
In deze video wordt een eenvoudige fotoalbumapplicatie geïmplementeerd met behulp van streaming HTML:
Zowel de status als de foto's worden na de initiële lay-out in de HTML-code geladen.
Gebruiksvoorbeelden
Er zijn veel toepassingsmogelijkheden voor deze methode om HTML in een andere volgorde te patchen, in combinatie met streaming HTML:
- Eilandarchitectuur. Een veelvoorkomend patroon, populair gemaakt door frameworks zoals Astro, is de eilandarchitectuur waarbij componenten onafhankelijk van elkaar worden geladen bovenop statische HTML. De
<template for>-API maakt het mogelijk om statische content op een vergelijkbare manier direct in HTML te verwerken. JavaScript-frameworks kunnen dit ook gebruiken voor meer interactieve eilanden of om componenten te beheren. - Lever content zodra deze gereed is. Dankzij deze eilandarchitectuur kan content worden gestreamd zodra deze gereed is, in plaats van te worden tegengehouden voor content die extra verwerking vereist, zoals bijvoorbeeld een databasequery. Hoewel veel platforms het streamen van HTML toestaan, zorgt de in-order aard van HTML ervoor dat content vaak wordt tegengehouden of dat er gebruik wordt gemaakt van complexe JavaScript DOM-manipulaties. Nu kunt u de statische content leveren terwijl u wacht, en vervolgens de complexere content aan het einde van de HTML-stream toevoegen.
- HTML kan in de optimale volgorde worden geladen voor een snelle laadtijd van de pagina. Sterker nog, je kunt de volgorde zelfs nog aanpassen nadat de pagina al geladen is. Megamenu's zijn bijvoorbeeld een veelvoorkomende navigatiefunctie die veel HTML bevat die de gebruiker pas ziet wanneer de pagina interactief wordt. Dit grote stuk HTML kan later in het HTML-document worden geladen om prioriteit te geven aan belangrijkere HTML die nodig is voor de initiële paginalading. De volgorde is met HTML geen belemmering meer.
Dit zijn slechts enkele voorbeelden, en het is spannend om te zien waar ontwikkelaars deze nieuwe API voor gaan gebruiken.
Beperkingen en nuances
De API kent een aantal beperkingen en nuances waar je rekening mee moet houden:
- Om veiligheidsredenen kan
<template for>alleen verwerkingsinstructies bijwerken binnen hetzelfde bovenliggende element. Door een<template for>rechtstreeks aan het<body>-element toe te voegen, krijgt het toegang tot het hele document (inclusief de<head>). - De verwerkingsinstructie
<?end>is optioneel en als deze ontbreekt, wordt de inhoud tussen het<?start>-element en het einde van het omringende element vervangen. - Het verplaatsen van verwerkingsinstructies nadat een
<template for>is begonnen met streamen, kan ook onverwachte gevolgen hebben, waarbij de nieuwe inhoud blijft streamen naar de oude locatie. - Houd er rekening mee dat wanneer je dynamisch
<template for>invoegt met methoden zoalssetHTMLofinnerHTML, de "ouder" van het sjabloon tijdens het parsen een tussenliggend documentfragment is. Dit betekent dat het invoegen van HTML met deze methoden de bestaande DOM niet kan wijzigen en dat de aanpassing "ter plaatse" in het fragment plaatsvindt. Bij het streamen met methoden zoalsstreamHTMLUnsafe(waar we het zo meteen over zullen hebben!) is er echter geen tussenliggend fragment, waardoor de sjablonen de bestaande inhoud kunnen vervangen.
Toekomstige potentiële toevoegingen
Enkele mogelijke toekomstige toevoegingen die momenteel worden overwogen, zijn:
- Client-side includes. Bijvoorbeeld:
<template for="footer" patchsrc="/partials/footer.html">. - Batchverwerking. Aan de clientzijde kunnen fragment includes ook worden uitgebreid om batchverwerking af te handelen, zodat meerdere updates tegelijkertijd plaatsvinden.
- Voorkomen dat inhoud die niet verandert, wordt overschreven. Dit kan worden bereikt met een revisienummer of versiebeheer. Hierdoor blijft de status behouden tussen routewijzigingen of andere updates, in plaats van dat de inhoud opnieuw wordt ingesteld.
- Opschonen tijdens het patchen. Bijvoorbeeld:
<template for=icon safe><svg id="from-untrusted-source">...</svg></template>
Polyfill
Het Chrome-team heeft een template-for-polyfill uitgebracht die beschikbaar is op npm , zodat websites deze nieuwe functionaliteit direct kunnen gebruiken, zelfs voordat deze in andere browsers beschikbaar komt.
Er zijn enkele beperkingen, aangezien het de HTML-parsers van browsers niet direct kan bijwerken, maar de meest voorkomende gebruiksscenario's worden wel gedekt. Websites moeten de functionaliteit nog steeds in andere browsers testen.
Vernieuwde HTML-invoeg- en streamingmethoden
Niet alle content kan in HTML worden weergegeven. Een tweede onderdeel van het werk dat Chrome op dit gebied verricht, betreft het vereenvoudigen van het bijwerken van content via JavaScript.
Er bestaan al diverse manieren om dynamisch HTML in een bestaand document in te voegen met behulp van JavaScript:
-
setHTML -
setHTMLUnsafe -
innerHTMLenouterHTML-setters -
createContextualFragment -
insertAdjacentHTML
Ze werken echter allemaal op een iets andere manier, met subtiliteiten en verschillen waar ontwikkelaars mogelijk niet altijd aan denken:
- Wordt de nieuwe inhoud overschreven of toegevoegd?
- Worden potentieel gevaarlijke HTML-code gesaneerd door bijvoorbeeld
<script>-tags te escapen? - Zo niet, moet
<script>dan worden uitgevoerd? - Hoe werken ze samen met TrustedTypes?
Weinig ontwikkelaars zouden die API's eerlijk kunnen bekijken en met vertrouwen de vragen voor elk ervan kunnen beantwoorden.
Een grote beperking is dat ze alleen gebruikt kunnen worden voor een complete set HTML die van tevoren bekend is, wanneer er aanroepen zijn gedaan om het streamen van HTML toe te staan. In de praktijk betekent dit dat je de volledige inhoud moet downloaden voordat je deze kunt invoegen, terwijl een van de sterke punten van HTML juist de mogelijkheid is om inhoud direct te streamen. Dit kan in beperkte mate worden omzeild door payloads op te splitsen of door gebruik te maken van onorthodoxe, verouderde methoden zoals document.write , maar die brengen hun eigen problemen met zich mee.
Een nieuwe set statische en streaming API's
Chrome heeft een reeks nieuwe API's en uitbreidingen voorgesteld voor de bestaande setHTML en setHTMLUnsafe , die dit probleem oplossen en tevens streamingfunctionaliteit introduceren:
Er zijn methoden om inhoud in te stellen of te vervangen, evenals methoden om inhoud vóór of na bestaande HTML in te voegen. Elke methode heeft stream-equivalenten:
| Actie | Statisch | Streaming |
|---|---|---|
| Stel de HTML-inhoud van het element in. | setHTML(html, options); | streamHTML(options); |
| Vervang het hele element door deze HTML. | replaceWithHTML(html, options); | streamReplaceWithHTML(options); |
| Voeg de HTML toe vóór het element | beforeHTML(html, options); | streamBeforeHTML(options); |
| Voeg de HTML toe als eerste kindelement van het element. | prependHTML(html, options); | streamPrependHTML(options); |
| Voeg de HTML toe als laatste kindelement van het element. | appendHTML(html, options); | streamAppendHTML(options); |
| Voeg de HTML toe na het element | afterHTML(html, options); | streamAfterHTML(options); |
Er zijn ook Unsafe versies die we zo dadelijk zullen bespreken. Hoewel het er misschien veel lijken te zijn (vooral als je de Unsafe equivalenten meetelt), maakt de consistente naamgeving het duidelijker wat elke methode doet in vergelijking met de eerder genoemde, niet-gerelateerde methoden.
De statische versies accepteren nieuwe HTML als een DOM-stringargument, samen met optionele opties:
const newHTML = "<p>This is a new paragraph</p>";
const contentElement = document.querySelector('#content-to-update');
contentElement.setHTML(newHTML);
De streamingversies werken met de Streams API , bijvoorbeeld met een getWriter() :
const contentElement = document.querySelector('#content-to-update');
const writer = contentElement.streamHTMLUnsafe().getWriter();
// Example stream of updating content
while (true) {
await writer.write(`<p>${++i}</p>`);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
writer.close();
Of, als alternatief, vanuit een fetch-respons, met behulp van pijpketens :
const contentElement = document.querySelector('#content-to-update');
const response = await fetch('/api/content.html');
response.body
.pipeThrough(new TextDecoderStream())
.pipeTo(contentElement.streamHTMLUnsafe());
We zijn ook van plan een handige methode toe te voegen waarmee je direct kunt streamen zonder de tussenliggende stap TextDecoderStream() nodig te hebben.
Met het argument options kunt u een aangepaste sanitizer specificeren. default wordt de standaard sanitizerconfiguratie gebruikt. Het wordt als volgt gebruikt:
const newHTML = "<p>This is a new paragraph</p>";
const contentElement = document.querySelector('#content-to-update');
// Only allows basic formatting
const basicFormattingSanitzer = new Sanitizer({ elements: ["em", "i", "b", "strong"] });
contentElement.setHTML(newHTML, {sanitizer: basicFormattingSanitzer});
"Onveilige" methoden
Er bestaan ook "onveilige" versies van elk van de API's:
| Actie | Statisch | Streaming |
|---|---|---|
| Stel de HTML-inhoud van het element in. | setHTMLUnsafe(html,options); | streamHTMLUnsafe(options); |
| Vervang het hele element door deze HTML. | replaceWithHTMLUnsafe(html, options); | streamReplaceWithHTMLUnsafe(options); |
| Voeg de HTML toe vóór het element | beforeHTMLUnsafe(html, options); | streamBeforeHTMLUnsafe(options); |
| Voeg de HTML toe als eerste kindelement van het element. | prependHTMLUnsafe(html, options); | streamPrependHTMLUnsafe(options); |
| Voeg de HTML toe als laatste kindelement van het element. | appendHTMLUnsafe(html, options); | streamAppendHTMLUnsafe(options); |
| Voeg de HTML toe na het element | afterHTMLUnsafe(html, options); | streamAfterHTMLUnsafe(options); |
Deze "onveilige" methoden schakelen de sanitizer standaard uit (u kunt desgewenst een aangepaste sanitizer specificeren) en maken het ook mogelijk om scripts uit te voeren met een optionele runScripts optie (die standaard op false staat).
Net als setHTML is setHTMLUnsafe een bestaande methode, maar de optieparameter runScripts is eraan toegevoegd zodat deze ook gebruikt kan worden bij het uitvoeren van scripts:
const newHTML = `<p>This is a new paragraph</p>
<script src=script.js></script>`;
const contentElement = document.querySelector('#content-to-update');
contentElement.setHTMLUnsafe(newHTML, {runScripts: true});
De formulering "onveilig" in de methode is bedoeld om ontwikkelaars te herinneren aan het potentiële risico en hoe ze scripts eventueel kunnen beveiligen of beperken, en niet om te zeggen dat deze methoden niet gebruikt zouden moeten worden.
Hoe "onveilig" dit is, hangt af van hoe betrouwbaar de invoer is. De statische methoden van het Unsafe werken met zowel DOM String als TrustedHTML als html argumenten en staan ook het gebruik van sanitizers toe. Bij runScript is het echter de bedoeling om scripts toe te staan, vandaar dat er standaard geen sanitizer wordt gebruikt.
Gebruiksvoorbeelden
Deze nieuwe API's maken het voor ontwikkelaars eenvoudiger om HTML aan bestaande pagina's toe te voegen, door nieuwe API's met consistente namen en opties te introduceren. De streaming-API's bieden het voordeel dat er niet gewacht hoeft te worden tot alle nieuwe content beschikbaar is op het platform.
Toepassingsvoorbeelden zijn onder meer:
- Dynamische streaming van grote contentupdates in Single Page Apps. Zoals eerder vermeld, is een groot nadeel van de huidige SPA's dat ze niet konden profiteren van het streamingkarakter van de initiële HTML-ladingen – tot nu toe!
- Het invoegen van veelvoorkomende content zoals HTML-voetteksten. Met behulp van de JavaScript API's kunt u gedeeltelijke elementen ophalen en in de pagina invoegen, waardoor u profiteert van caching in plaats van ze op elke pagina te herhalen. Gezien de afhankelijkheid van JavaScript voor de werking, moet dit echter alleen worden gebruikt voor content die niet zichtbaar is bij het laden van de pagina.
Dit zijn slechts een paar voorbeelden en we zijn erg benieuwd wat jullie bedenken!
Beperkingen en nuances
Deze nieuwe API's kennen ook een aantal beperkingen en nuances waar je rekening mee moet houden:
- Integratie van streaming met de Trusted Types API vereist het gebruik van een nieuwe
createParserOptions-methode waarmee een sanitizer kan worden geïnjecteerd in elke HTML-instellingbewerking. Zie de uitleg voor meer informatie over de integratie van Trusted Types. - Net als bij
<template for>kan het verplaatsen van elementen die worden gestreamd, onverwachte gevolgen of streamfouten veroorzaken. -
streamHTMLUnsafewerkt in veel opzichten meer zoals de hoofdparser, onder andere door<template for>-instructies te verwerken zodra ze aan het hoofddocument worden toegevoegd en doordeferscripts uit te stellen tot het einde van de stream.
Polyfill
Het Chrome-team heeft een html-setters-polyfill uitgebracht die beschikbaar is op npm , zodat websites deze nieuwe functionaliteit direct kunnen gebruiken, zelfs voordat deze in andere browsers beschikbaar komt.
Merk op dat deze polyfill niet streamt, maar buffert en toepast zodra de verwerking is voltooid. Het is meer een polyfill voor de structuur van de API dan voor de functionaliteit.
Daarnaast is het instellen van veilige content afhankelijk van setHTML en de Sanitizer API , die niet door Safari worden ondersteund.
Gebruik ze allebei samen.
Hoewel dit twee afzonderlijke API's zijn, schuilt de echte kracht in de combinatie ervan. Door nieuwe <template for> -elementen in de HTML te streamen, kunt u verschillende delen van de content dynamisch bijwerken zonder dat u elk onderdeel rechtstreeks hoeft te benaderen met afzonderlijke JavaScript-referenties naar de DOM.
Een eenvoudige paginalading in SPA-stijl kan worden geïmplementeerd door een overzichtspagina met verwerkingsinstructies te laden en vervolgens de sjablonen van elke nieuwe pagina in de HTML te streamen, zodat ze in die verwerkingsinstructies passen.
Er is ongetwijfeld meer potentieel en er zijn meer toepassingsmogelijkheden voor beide API's, dus laat onze (beperkte!) verbeelding je niet tegenhouden. Door gedeeltelijke updates eenvoudiger te maken, kun je de hoeveelheid standaardcode verminderen, updates vereenvoudigen en nieuwe mogelijkheden voor het web ontsluiten!