From 6370db2d0e5a21c3681d16ea61a30266ffd71d83 Mon Sep 17 00:00:00 2001 From: Ollie Relph Date: Fri, 25 Nov 2016 17:07:17 +0000 Subject: [PATCH 1/2] Hide all examples when JS has been run --- javascript/index.js | 1 + sass/example.sass | 2 ++ 2 files changed, 3 insertions(+) diff --git a/javascript/index.js b/javascript/index.js index 2124d6e5..73ae48cf 100644 --- a/javascript/index.js +++ b/javascript/index.js @@ -1,3 +1,4 @@ +document.body.classList.add('has-js'); document.addEventListener('DOMContentLoaded', function() { // Logo var $logo = document.getElementById('logo'); diff --git a/sass/example.sass b/sass/example.sass index b59687b0..ed298672 100644 --- a/sass/example.sass +++ b/sass/example.sass @@ -100,6 +100,8 @@ width: 4px .example-output + body.has-js & + display: none border: 1px solid $border border-bottom-left-radius: 4px border-bottom-right-radius: 4px From 25b57c2269bb080bcd29e98539efce989cd8a1f9 Mon Sep 17 00:00:00 2001 From: Ollie Relph Date: Fri, 25 Nov 2016 17:16:21 +0000 Subject: [PATCH 2/2] Dynamically display the examples to reduce initial browser paint time --- javascript/index.js | 103 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/javascript/index.js b/javascript/index.js index 73ae48cf..89c526e8 100644 --- a/javascript/index.js +++ b/javascript/index.js @@ -1,5 +1,26 @@ document.body.classList.add('has-js'); document.addEventListener('DOMContentLoaded', function() { + + // dynamically show examples + var $exampleOutputs = [].slice.call(document.querySelectorAll('.example-output')); + var $exampleBrowsers = [].slice.call(document.querySelectorAll('.example-browser')); + var $exampleWrappers = [].slice.call(document.querySelectorAll('.example')); + + var propPositions = $exampleBrowsers.map(function($exampleBrowser, ix) { + var offsetProps = getOffset($exampleBrowser); + var $wrapper = $exampleWrappers[ix]; + return { + displayed: false, + $exampleBrowser: $exampleBrowser, + $exampleOutput: $exampleOutputs[ix], + offsetTop: offsetProps.top, + $wrapper: $wrapper, + originalWrapperHeight: getHeight($wrapper) + }; + }); + + startWatching(); + // Logo var $logo = document.getElementById('logo'); @@ -24,4 +45,86 @@ document.addEventListener('DOMContentLoaded', function() { } }); }); + + // Helpers + + function startWatching() { + var doc = document.documentElement; + var prevTop = 0; + var prevWindowHeight = 0; + var scrollMargin = 200; + window.requestAnimationFrame(function loop() { + var top = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); + var windowHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); + // if we've scrolled, clicked on an anchor, or made the window bigger + if (top !== prevTop || windowHeight > prevWindowHeight) { + prevWindowHeight = windowHeight; + prevTop = top; + var aboveTheWindow = top - scrollMargin; + var belowTheWindow = top + windowHeight + scrollMargin; + showExamples(propPositions, aboveTheWindow, belowTheWindow); + } + setTimeout(window.requestAnimationFrame.bind(window, loop), 100); + }); + } + + function showExamples(propPositions, aboveTheWindow, belowTheWindow) { + var i = 0; + while (++i < propPositions.length) { + // if we've already show this example, or we're before the viewing window + if ( + propPositions[i].displayed || + propPositions[i].offsetTop < aboveTheWindow + ) { + continue; + } + // PERF: because the elements are effectively sorted by offsetTop ASC + // we know that once we are higher than the bottom range + // we no longer need to check the remainder + if (propPositions[i].offsetTop > belowTheWindow) { + return; + } + // we now know we're in the loading window + propPositions[i].displayed = true; + propPositions[i].$exampleOutput.style.display = 'block'; + var newWrapperHeight = getHeight(propPositions[i].$wrapper); + addHeightToRest(propPositions, newWrapperHeight - propPositions[i].originalWrapperHeight, i); + } + } + + function addHeightToRest(positions, height, ix) { + for (var i = ix + 1; i < positions.length; i++) { + positions[i].offsetTop += height; + } + } + + function isWindow(obj) { + return obj != null && obj === obj.window; + } + + function getWindow($element) { + return isWindow($element) ? $element : $element.nodeType === 9 && $element.defaultView; + } + + function getOffset($element) { + var win; + var box = { top: 0, left: 0 }; + var doc = $element && $element.ownerDocument; + var docElem = doc.documentElement; + + if (typeof $element.getBoundingClientRect !== typeof undefined) { + box = $element.getBoundingClientRect(); + } + win = getWindow(doc); + return { + top: box.top + win.pageYOffset - docElem.clientTop, + left: box.left + win.pageXOffset - docElem.clientLeft + }; + }; + + function getHeight($element) { + var style = window.getComputedStyle($element, null); + return parseInt(style.getPropertyValue('height'), 10); + } }); +