Skip to content

Instantly share code, notes, and snippets.

@nolanlawson
Last active February 7, 2022 18:18
Show Gist options
  • Save nolanlawson/dbd5cda339d5a58d0027df1d0e6e1b6b to your computer and use it in GitHub Desktop.
Save nolanlawson/dbd5cda339d5a58d0027df1d0e6e1b6b to your computer and use it in GitHub Desktop.
Constructable stylesheets push vs reassignment
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Constructable styles benchmark: push versus reassignment</title>
<style>
label {
display: block;
margin: 20px 0;
}
</style>
</head>
<body>
<h1>Constructable styles benchmark: push versus reassignment</h1>
<form>
<label>
Number of components:
<input id="numComponents" type="number" value="1000">
</label>
<label>
Number of stylesheets per component:
<input id="numSheets" type="number" value="100">
</label>
<label>
Use <code>adoptedStyleSheets.push</code> instead of assignment
<input id="usePush" type="checkbox" checked>
</label>
<button id="render" type="button">Render components</button>
</form>
<pre></pre>
<script type="module">
const $ = document.querySelector.bind(document)
const $$ = _ => [...document.querySelectorAll(_)]
const usePushCheckbox = $('#usePush')
const numComponentsInput = $('#numComponents')
const numSheetsInput = $('#numSheets')
const renderButton = $('#render')
const pre = $('pre')
function createDiv() {
const div = document.createElement('div')
div.textContent = 'hello'
return div
}
renderButton.addEventListener('click', () => {
const numSheets = parseInt(numSheetsInput.value, 10) || 0
const numComponents = parseInt(numComponentsInput.value, 10) || 0
const usePush = usePushCheckbox.checked
const rando = (Math.round(1000000 * Math.random())).toString(16)
const name = `construct-component-${rando}`
const sheets = Array(numSheets).fill().map((_, i) => {
const css = `div { color: #${i.toString(16).padStart(6, '0')}; }`
const sheet = new CSSStyleSheet()
sheet.replaceSync(css)
return sheet
})
customElements.define(name, class extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
for (const sheet of sheets) {
if (usePush) {
this.shadowRoot.adoptedStyleSheets.push(sheet)
} else {
this.shadowRoot.adoptedStyleSheets = [...this.shadowRoot.adoptedStyleSheets, sheet]
}
}
this.shadowRoot.appendChild(createDiv())
}
})
performance.mark('start')
for (let i = 0; i < numComponents; i++) {
document.body.appendChild(document.createElement(name))
}
// requestPostAnimationFrame polyfill using rAF + postMessage
requestAnimationFrame(() => {
addEventListener('message', () => {
performance.measure('total', 'start')
const duration = performance.getEntriesByName('total').slice(-1)[0].duration
pre.innerText += `${duration} ms\n`
// cleanup
for (const el of $$(name)) {
el.remove()
}
}, { once: true })
postMessage('', '*')
})
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment