Last active
August 14, 2017 19:40
-
-
Save byelipk/de26365a1751a6495ea73c146e5c423f to your computer and use it in GitHub Desktop.
Reduce selector complexity and reduce number of affected elements
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> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | |
<meta name="author" content="" /> | |
<meta name="viewport" content="width=device-width"> | |
<title>Box Style Change</title> | |
<style> | |
html, body { | |
background: #ECECEC; | |
} | |
main .box-container .box { | |
display: inline-block; | |
width: 40px; | |
height: 40px; | |
background: #FFF; | |
box-shadow: 0 1px 1px rgba(0,0,0,0.3); | |
margin: 5px; | |
position: relative; | |
will-change: transform; | |
} | |
/* RECALCULATE STYLE: 10.33ms */ | |
body.toggled main .box-container .box:nth-child(2n) { | |
background: #777 !important; | |
} | |
/* NOTE: Reduce selector complexity */ | |
.box--toggled { | |
background: #777 !important; | |
} | |
button { | |
margin: 0 auto; | |
padding: 20px; | |
background: #CC0000; | |
font-size: 20px; | |
border: none; | |
border-radius: 3px; | |
box-shadow: 0 1px 1px rgba(0,0,0,0.3); | |
} | |
</style> | |
</head> | |
<body> | |
<button>Click me</button> | |
<main> | |
<div class="box-container"> | |
<!-- Boxes go here --> | |
</div> | |
</main> | |
<script> | |
// Anatomy of a frame | |
// | |
// (1) VSYNC | |
// | |
// The VSYNC event is a signal posted by the operating system | |
// kernel at a fixed interval. Modern browsers use the VSYNC | |
// event to construct a new frame that the GPU can display | |
// on the screen. The operating system also uses the VSYNC | |
// event to inform the browser of event signals such as | |
// keypress, input, or mousemove. All this communication | |
// between the browser and the operating system happens in what | |
// is called the COMPOSITOR THREAD. | |
// | |
// | |
// (2) INPUT EVENT HANDLERS | |
// | |
// The COMPOSITOR THREAD passes input event data to the MAIN THREAD. | |
// | |
// | |
// (3) REQUEST ANIMATION FRAME | |
// | |
// This is the ideal time to make visual updates to the screen. It's | |
// close to the VSYNC event, and comes before RECALCULATE STYLES. This | |
// means that if you mutate 100 classes, you won't trigger 100 different | |
// style recalculations because they will all be batched into the | |
// RECALCULATE STYLES event that fires after RAF. Remember not to | |
// query any layout styles or computed properties because that | |
// will force the layout thrashing problem. | |
// | |
// | |
// (4) PARSE HTML | |
// | |
// Newly added HTML is processed here, then added to the DOM. | |
// | |
// | |
// (5) RECALCULATE STYLES | |
// | |
// Styles are computed for anything that is newly added to the DOM or | |
// mutated. Potentially the entire DOM tree could have changed, or | |
// the changes could be much narrower in scope. Changing classes on the | |
// body class can be far-reaching. Measure first. | |
// | |
// | |
// (6) LAYOUT | |
// | |
// This is where the browser recalculates geometric information for | |
// every visible element. It's normally done for the entire document, | |
// which makes the cost proportional to the size of the DOM | |
// (i.e. linear complexity). | |
// | |
// Layout thrashing is where you trigger the LAYOUT task by querying | |
// a computed property, such as `offsetWidth`, and then trigger | |
// RECALCULATE STYLES by updating DOM element styles. Updating | |
// styles invalidates the previous LAYOUT task, so LAYOUT must be | |
// recomputed again. | |
// | |
// | |
// (7) UPDATE LAYER TREE | |
// | |
// Chrome's internal rendering engine, Blink, is figuring out what layers are | |
// needed for the page. | |
// | |
// This is where the page's stacking context gets sorted out. Some elements get | |
// stacked on top of other elements like pancakes, like a modal or two absolutely | |
// positioned divs. Properties like `z-index` must be respected. | |
// | |
// Shows up in Chrome as "Update Layer Tree." | |
// | |
// (8) PAINT | |
// | |
// Painting is a two-part process: | |
// | |
// A) PAINTING is the recording of draw calls (fill a rectangle here, write | |
// text there) for any elements that are new or have changed visually. | |
// | |
// B) RASTERIZATION is where the draw calls are executed and the textures | |
// get filled in. The PAINT event is just the recording of draw calls. | |
// | |
// | |
// (9) COMPOSITE | |
// | |
// The layer and tile information is calculated and is passed back | |
// to the COMPOSITOR THREAD so it can eventually be rendered onto the screen. | |
// | |
// Shows up in Chrome as "Composite Layers." | |
// | |
// (10) RASTER SCHEDULED && RASTERIZE | |
// | |
// The draw calls recorded in the PAINT task are now executed. This work | |
// is done in a COMPOSITOR TILE WORKER. The number of workers is device- | |
// dependent. | |
// | |
// | |
// (11) FRAME END | |
// | |
// New tiles and input data are committed to the GPU thread. | |
// | |
// | |
// (12) FRAME SHIPS | |
// | |
// The tiles are uploaded to the GPU by the GPU thread. The GPU | |
// will draw the tiles to the screen. | |
// | |
var container = document.querySelector('.box-container'); | |
var button = document.querySelector('button'); | |
for (var i = 0; i < 3000; i++) { | |
var div = document.createElement('div'); | |
div.classList.add('box'); | |
div.index = i; | |
container.appendChild(div); | |
} | |
button.addEventListener('click', function() { | |
console.time('click'); | |
const children = container.children; | |
const length = children.length; | |
// Javascript and CSS working together to reduce the number | |
// of affected elements and reduce the CSS selector | |
// complexity. | |
// for (var i = 0; i < length; i++) { | |
// if (i % 2 !== 0) { | |
// children[i].classList.toggle('box--toggled'); | |
// } | |
// } | |
for (var i = 1; i < length; i+=2) { | |
children[i].classList.toggle('box--toggled'); | |
} | |
console.timeEnd('click'); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment