Created
April 14, 2021 21:31
-
-
Save nolanlawson/370c624f9dc1fcd03916f491d777e402 to your computer and use it in GitHub Desktop.
Selector perf test, more realistic scenario for attribute values
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 (more realistic scenario for attribute values)</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 (more realistic scenario for attribute values)</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-x-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-x-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-x-color`, nextColor(el.getAttribute('data-x-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