Last active
May 18, 2021 12:53
-
-
Save th3hunt/f5cb0f99da0b565c3b3f9897c1b80f04 to your computer and use it in GitHub Desktop.
Script & CSS load utils
This file contains hidden or 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
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); | |
}); | |
} |
This file contains hidden or 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
/** | |
* 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