Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
feat(demos): Add global demoReady() function (#1919)
Browse files Browse the repository at this point in the history
For this PR, I only updated the button demo page to use the new `demoReady()` function. #1920 updates the remaining demo pages.

Also note that our packaged JS is now being loaded with `async`, which speeds up initial rendering time over slow connections. Using "Mid-tier mobile" network emulation in Chrome devtools, the [TTFMP](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint) of the button demo page was reduced from ~10.5s to ~9.5s, and the time to render the Material icon font  dropped from ~12s to ~10.5s.
  • Loading branch information
acdvorak authored Jan 12, 2018
1 parent aeed3bd commit da34cc9
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 20 deletions.
27 changes: 7 additions & 20 deletions demos/button.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="/assets/button.css">
<script src="/ready.js"></script>
<style>
.demo-wrapper {
padding-bottom: 100px;
Expand Down Expand Up @@ -75,7 +76,6 @@
left: 36px;
color: rgba(33, 33, 33, .38);
}

</style>
</head>
<body class="mdc-typography">
Expand Down Expand Up @@ -356,25 +356,12 @@ <h1 class="mdc-typography--display2">CSS Only</h1>
</section>
</main>

<script src="/assets/material-components-web.js"></script>
<script src="/assets/material-components-web.js" async></script>
<script>
// Because we load our CSS via webpack, we need to ensure that all of the correct styles
// are applied before ripples are attached. Otherwise, ripples may use the computed styles of
// elements before our CSS is applied, leading to improper UX.
(function() {
var pollId = 0;
pollId = setInterval(function() {
var pos = getComputedStyle(document.querySelector('.mdc-button')).position;
if (pos === 'relative') {
init();
clearInterval(pollId);
}
}, 250);
function init() {
var btns = document.querySelectorAll('.mdc-button:not([data-demo-no-js])');
for (var i = 0, btn; btn = btns[i]; i++) {
mdc.ripple.MDCRipple.attachTo(btn);
}
demoReady(function() {
var btns = document.querySelectorAll('.mdc-button:not([data-demo-no-js])');
for (var i = 0, btn; btn = btns[i]; i++) {
mdc.ripple.MDCRipple.attachTo(btn);
}

var demoWrapper = document.querySelector('.demo-wrapper');
Expand All @@ -393,7 +380,7 @@ <h1 class="mdc-typography--display2">CSS Only</h1>
});

});
})();
});
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions demos/common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ $mdc-theme-secondary: #018786 !default; // baseline teal, 600 tone

$dark-button-color: $material-color-light-green-a200;

// Used by ready.js to test whether the CSS has finished loading.
.demo-ready-detect {
position: relative;
}

fieldset {
margin: 0;
padding: 0;
Expand Down
103 changes: 103 additions & 0 deletions demos/ready.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* @license
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// eslint-disable no-unused-vars no-var

/**
* Adds the given event handler to the queue. It will be executed asynchronously after all external JS and CSS resources
* have finished loading (as determined by continuous long-polling with a timeout). If this function is called after all
* resources have finished loading, the given handler function will be invoked synchronously (in the same call stack).
* Handlers are invoked in FIFO order.
* @param {function() : undefined} handler
*/
window.demoReady = (function() {
var TIMEOUT_MS = 60 * 1000;
var DELAY_MS = 100;

var isReadyCached = false;
var handlers = [];
var testDom = null;
var startTimeMs = null;
var timer = null;

function isReady() {
if (isReadyCached) {
return true;
}
ensureDetectionDom();
isReadyCached = Boolean(window.mdc) && getComputedStyle(testDom).position === 'relative';
return isReadyCached;
}

function ensureDetectionDom() {
if (testDom) {
return;
}
testDom = document.createElement('div');
testDom.classList.add('demo-ready-detect');
document.body.appendChild(testDom);
}

function removeDetectionDom() {
if (!testDom) {
return;
}
document.body.removeChild(testDom);
testDom = null;
}

function startTimer() {
if (timer) {
return;
}
startTimeMs = Date.now();
timer = setInterval(tick, DELAY_MS);
}

function tick() {
if (isReady()) {
clearInterval(timer);
removeDetectionDom();
invokeHandlers();
return;
}

const elapsedTimeMs = Date.now() - startTimeMs;
if (elapsedTimeMs > TIMEOUT_MS) {
clearInterval(timer);
removeDetectionDom();
console.error('Timed out waiting for JS and CSS to load');
return;
}
}

function invokeHandlers() {
handlers.forEach(function(handler) {
handler();
});
}

return function addHandler(handler) {
if (isReady()) {
handler();
return;
}

handlers.push(handler);
startTimer();
};
})();

0 comments on commit da34cc9

Please sign in to comment.