Skip to content

Instantly share code, notes, and snippets.

@th3hunt
Last active May 18, 2021 12:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save th3hunt/f5cb0f99da0b565c3b3f9897c1b80f04 to your computer and use it in GitHub Desktop.
Save th3hunt/f5cb0f99da0b565c3b3f9897c1b80f04 to your computer and use it in GitHub Desktop.
Script & CSS load utils
export const status = {
TIMEOUT: 'timeout',
SUCCESS: 'success',
SKIPPED: 'skipped'
};
/**
* Load a JS script programmatically.
*
* The <script> element get inserted as the last child of <body> by default but can be customized
* via `insertAfter` or `insertBefore` options.
*
* @param {String} url - the URL of the script
* @param {Object} [options] - options
* @param {Boolean} [options.async=true] - whether to execute the script asynchronously, as soon as it is available
* @param {Boolean} [options.defer=false] - whether to execute the script after the page has finished parsing
* @param {Number} [options.timeout=5000] - the timeout in ms after which the returned promise is rejected
* Keep in mind, that this doesn't guarantee that the script won't be parsed or executed
* eventually. There is no way to abort it. It only means, that we shouldn't care if it does after
* the timeout.
* @param {Function} [options.skipIf] - a convenience option. Return a truthy value to skip loading the script altogether.
* @param {Node} [options.insertAfter] - if given, the <script> element will be inserted after `insertAfter`
* @param {Node} [options.insertBefore] - if given, the <script> element will be inserted before `insertBefore`
* @returns {Promise} - resolves `onload` and rejects `onerror`
*/
export default function loadScript(url, {
async = true,
defer = false,
insertAfter: aref,
insertBefore: bref,
timeout = 5000,
skipIf
} = {}) {
return new Promise((resolve, reject) => {
if (typeof skipIf === 'function' && skipIf()) {
resolve(status.SKIPPED);
return;
}
const rejectWithTimeout = setTimeout(() => reject(status.TIMEOUT), timeout);
const script = document.createElement('script');
script.type = 'text\/javascript';
script.src = url;
script.async = async;
script.defer = defer;
script.onerror = err => {
reject(new URIError(`loadScript: the script ${err.target.src} is not accessible.`));
};
script.onload = () => {
clearTimeout(rejectWithTimeout);
resolve(status.SUCCESS);
};
if (aref) {
aref.parentNode.insertBefore(script, aref.nextSibling);
return;
}
if (bref) {
bref.parentNode.insertBefore(script, bref);
return;
}
document.body.appendChild(script);
});
}
/**
* Load a CSS stylesheet programmatically.
*
* The <link> element gets inserted as the last child of <head> by default but can be customized
* via `insertAfter` or `insertBefore` options.
*
* @param {String} url - the URL of the CSS resource
* @param {Object} [options] - options
* @param {String} [options.media='all'] - the media formats to which the styles apply
* {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement}
* @param {Node} [options.insertAfter] - if given, the <link> element will be inserted after `insertAfter`
* @param {Node} [options.insertBefore] - if given, the <link> element will be inserted before `insertBefore`
* @returns {Promise} - resolves `onload` and rejects `onerror`
*/
export default function importStyle(url, {media, insertAfter: aref, insertBefore: bref} = {}) {
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.type = 'text\/css';
link.rel = 'stylesheet';
link.href = url;
link.onerror = err => {
reject(new URIError(`loadStyle: the stylesheet ${err.target.src} is not accessible.`));
};
link.onload = () => {
link.media = media || 'all';
resolve();
};
// temporarily set media to something inapplicable to ensure it'll fetch without blocking render
link.media = 'only x';
if (aref) {
aref.parentNode.insertBefore(link, aref.nextSibling);
return;
}
if (bref) {
bref.parentNode.insertBefore(link, bref);
return;
}
document.head.appendChild(link);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment