Skip to content

Instantly share code, notes, and snippets.

@byelipk
Last active August 14, 2017 19:40
Show Gist options
  • Save byelipk/de26365a1751a6495ea73c146e5c423f to your computer and use it in GitHub Desktop.
Save byelipk/de26365a1751a6495ea73c146e5c423f to your computer and use it in GitHub Desktop.
Reduce selector complexity and reduce number of affected elements
<!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