Reputation: 3183
I've been trying to load a big js script only once. I'm not loading it on page load as it is not needed then and will slow the page load down.
So I've been trying to load it with jQuery's $.getScript and Modernizr.load ( since I'm already using Modernizr ).
I've tried iterating through all the <script></script>
elements and checking their src attributes and seeing if this script is one of them, but that still loaded them each time I ran the test.
I also tried setting a global variable to true at the beginning of the script and checking if that was set and it still loaded every time I checked. Here is what I was doing in that instance:
Modernizr.load({
test:if(window.flag === undefined),
yep:'conversation.js',
callback:function(){
antecedent();
}
});
and var flag = true;
was set at the first line of conversation.js
Does anyone have any ideas how I can get this script to load once when this test is called to check if it is has been loaded and only load it once then?
** Edit/Update: ** document.getElementsByTagName('head')[0].appendChild(document.createElement('script')).src = 'conversation.js';
could work, but how do I check if the script is already loaded or not and then only call this if it isn't already included?
Upvotes: 16
Views: 40516
Reputation: 421
Here is a small function that will add the script tag to the document and execute a callback once it is loaded. The script will only be added if it doesn't exist.
export const loadScript = (url, callback) => {
let script;
const scripts = Array.from(document.querySelectorAll('script'));
const existingScript = scripts.find((script) => script.src === url);
if (existingScript) {
script = existingScript;
} else {
script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
if (script.readyState) {
script.onreadystatechange = () => {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = () => callback();
}
};
Upvotes: 3
Reputation: 576
in plain php:
$JSsrcs = isset($JSsrcs) ? $JSsrcs : [];
function js_src_once($JSsrc)
{
global $JSsrcs;
if(in_array($JSsrc, $JSsrcs))
{
//return "duplicate";
}
else
{
$JSsrcs[]= $JSsrc;
return "<script src=\"".$JSsrc."\"></script>";
}
}
Upvotes: 0
Reputation: 22030
First, you'll need to check if the script is already on the page:
var list = document.getElementsByTagName('script');
var i = list.length, flag = false;
while (i--) {
if (list[i].src === 'filePathToJSScript') {
flag = true;
break;
}
}
// if we didn't already find it on the page, add it
if (!flag) {
var tag = document.createElement('script');
tag.src = 'filePathToJSScript';
document.getElementsByTagName('body')[0].appendChild(tag);
}
The code in the conditional can be used to dynamically add scripts to the page.
ES 6 simplifies this quite a bit:
let scripts = Array
.from(document.querySelectorAll('script'))
.map(scr => scr.src);
if (!scripts.includes('filePathToJSScript')) {
// same as above
}
Upvotes: 22
Reputation: 8819
A simpler solution (again assuming ES6) would be:
function load_script(url) {
if (!Array.from(document.querySelectorAll('script')).some(elm => elm.src == url)) {
let script = document.createElement('script')
script.src = url
document.getElementsByTagName('head')[0].appendChild(script)
}
}
The .some()
function will return true if any of the elements contain a src that is the URL, allowing you to then add the element to the end of the <head>
part of the script.
Upvotes: 1
Reputation: 4868
Life is easier if you insure the script is loaded only once in the source code. If php is being used to load scripts let it also handle the dependencies.
Functions are first class objects and if a object does not exist it converts to a Boolean false in if statement.
if (myFunction) { /* myFunction exists in myscript */
} else {
var myScript = document.createElement('script');
document.getElementById("main").appendChild(myScript);
myScript.src = "http://example/myscript.js";
}
However, this causes the script to be loaded asynchronously. Using document.write may still load asynchronously. Meaning a call can be made to the script elsewhere before the script is loaded. You may also need to program a promise maker and promise keeper to handle these calls, Not impossible but ... onload used to make promise, if it does not load my promise was broken.
if (myFunction) {
/* Call the function I needed no promise needed. */
myFunction();
} else { /* myFunction exists in myscript */
var myScript = document.createElement('script');
document.getElementById("main").appendChild(myjsonp);
myScript.onload = "/* promise fulfillment */"
myScript.src = "http://example/myscript.js";
}
All of this coding may end of removing any benefit of not having it loaded on every page, regardless if it is used, an letting it be a cached asset across the site.
Note onload for scripts is only HTML5.
myscript.js however can have the promise keeper. It can look for a global which is a array of promises, and if that array exists run the promises.
If myscript.js is loaded with jquery ajax, the promise keeper can be contained with the done function.
Upvotes: 1
Reputation: 1
Note, return value of console.log(Modernizr.hasOwnProperty("load"))
? . See http://github.com/SlexAxton/yepnope.js#deprecation-notice
"For these reasons, we're also not going to include yepnope in the next version of Modernizr as Modernizr.load."
Try
$(function() {
var callbacks = $.Callbacks("once");
callbacks.add(
$.getScript("conversation.js", function(data, textStatus) {
console.log(textStatus, window.flag)
})
);
if (window.flag === undefined) {
callbacks.fire()
} else {
console.log(window.flag)
}
})
jsfiddle http://jsfiddle.net/guest271314/voehd4ne/
Upvotes: 1