Created
April 6, 2021 23:19
-
-
Save nolanlawson/40ad8bfdf8e50705a5ab35599bccf826 to your computer and use it in GitHub Desktop.
Selector perf test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>Selector perf test</title> | |
<style> | |
pre { | |
position: fixed; | |
right: 0; | |
top: 0; | |
padding: 20px; | |
background: rgba(30, 30, 30, 0.8); | |
color: white; | |
pointer-events: none; | |
} | |
.container { | |
padding: 20px 0; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Selector perf test</h1> | |
<h2>Mode: <span class="mode"></span></h2> | |
<h2>Number of rules: <span class="rules"></span></h2> | |
<button onclick="swap()">Force style recalc</button> | |
<div class="container"></div> | |
<pre></pre> | |
<script type="module"> | |
const params = new URLSearchParams(location.search) | |
const mode = params.get('mode') || 'classes' | |
const numRules = parseInt(params.get('rules'), 10) || 0 | |
const pre = document.querySelector('pre') | |
const NUM_DOM_NODES_LEVEL_1 = 10 | |
const NUM_DOM_NODES_LEVEL_2 = 10 | |
const DOM_DEPTH = 10 | |
const COLORS = ['red', 'blue', 'green'] | |
const log = str => { | |
pre.textContent += str + '\n' | |
} | |
const makeBigDomTree = color => { | |
const tagName = mode === 'tags' ? `c-${color}` : 'div' | |
const el = document.createElement(tagName) | |
if (mode === 'attributes') { | |
el.setAttribute('data-' + color, '') | |
} else if (mode === 'attributeValues') { | |
el.setAttribute('data-color', color) | |
} else if (mode === 'classes') { | |
el.classList.add(color) | |
} | |
for (let i = 0; i < NUM_DOM_NODES_LEVEL_1; i++) { | |
const row = document.createElement('div') | |
for (let j = 0; j < NUM_DOM_NODES_LEVEL_2; j++) { | |
let deep = document.createElement('div') | |
let current = deep | |
for (let k = 0; k < DOM_DEPTH; k++) { | |
let inner = document.createElement('div') | |
current.appendChild(inner) | |
current = inner | |
} | |
current.textContent = 'lalala' | |
row.appendChild(deep) | |
} | |
el.appendChild(row) | |
} | |
document.querySelector('.container').appendChild(el) | |
} | |
for (const color of COLORS) { | |
makeBigDomTree(color) | |
} | |
const style = document.createElement('style') | |
const randoStyles = () => { | |
return Array(numRules).fill().map((_, i) => { | |
let selector | |
let str = `foo-${i.toString(16)}` | |
if (mode === 'attributes') { | |
selector = `[data-${str}] div` | |
} else if (mode === 'attributeValues') { | |
selector = `[data-color="${str}"] div` | |
} else if (mode === 'tags') { | |
selector = `c-${str} div` | |
} else { | |
selector = `.${str} div` | |
} | |
return `${selector} { color: gray; }` | |
}).join('\n') | |
} | |
style.textContent = COLORS.map(color => { | |
let selector | |
if (mode === 'attributes') { | |
selector = `[data-${color}] div` | |
} else if (mode === 'attributeValues') { | |
selector = `[data-color="${color}"] div` | |
} else if (mode === 'tags') { | |
selector = `c-${color} div` | |
} else { | |
selector = `.${color} div` | |
} | |
return `${selector} { | |
color: ${color}; | |
}` | |
}).join('\n') + '\n' + randoStyles() | |
// similar to https://github.com/WICG/request-post-animation-frame | |
const afterFrame = callback => { | |
addEventListener('message', callback, { once: true }) | |
postMessage('', '*') | |
} | |
const measureStyleLayout = () => { | |
performance.mark('start') | |
afterFrame(() => { | |
performance.measure('total', 'start') | |
const { duration } = performance.getEntriesByName('total').slice(-1)[0] | |
log('duration: ' + duration + ' ms') | |
}) | |
} | |
requestAnimationFrame(() => { | |
document.head.appendChild(style) | |
measureStyleLayout() | |
}) | |
const colorElements = [...document.querySelectorAll('.container > *')] | |
const nextColor = color => { | |
let idx = COLORS.indexOf(color) + 1 | |
if (idx === COLORS.length) { | |
idx = 0 | |
} | |
return COLORS[idx] | |
} | |
window.swap = () => { | |
if (mode === 'tags') { | |
alert('swapping tag names not supported') | |
return | |
} | |
requestAnimationFrame(() => { | |
for (const el of colorElements) { | |
if (mode === 'attributes') { | |
for (const color of COLORS) { | |
if (el.hasAttribute(`data-${color}`)) { | |
el.removeAttribute(`data-${color}`) | |
el.setAttribute(`data-${nextColor(color)}`, '') | |
break | |
} | |
} | |
} else if (mode === 'attributeValues') { | |
el.setAttribute(`data-color`, nextColor(el.getAttribute('data-color'))) | |
} else if (mode === 'classes') { | |
for (const color of COLORS) { | |
if (el.classList.contains(color)) { | |
el.classList.remove(color) | |
el.classList.add(nextColor(color)) | |
break | |
} | |
} | |
} | |
} | |
measureStyleLayout() | |
}) | |
} | |
document.querySelector('.mode').innerText = mode | |
document.querySelector('.rules').innerText = numRules | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment