one .js to load all .js and .css - javascript

I need to create a simple javascript embed type thing where the user adds a small amount of javascript to their site and my script does the rest.
This is the sort of thing I would like to see on the user site:
Included once:
<script type="text/javascript" src="path/to/doEverythingScript.js"></script>
And this in the HTML:
<div id="target-div">
<script type="text/javascript">
doThis(1714581, "#target-div");
</script>
</div>
I would also need to be able to have multiple versions running on the same page.
The id (eg: 1714581) is passed to my function along with my target div.
This issue is I need to have the one script load all the dependancies:
eg:
path/to/style1.css
path/to/style2.css
path/to/style3.css
path/to/jquery.js
path/to/script1.js
path/to/script2.js
path/to/script3.js
path/to/script4.js
And once they are all loaded I would then be able to run my function.
If my function ran before they all loaded It naturally wouldn't work.
I tried using LazyLoad and LAB but couldn't work out how to have one JS file sort all this out with only one linkied script and a small function on the page.
What I have writen so far doesnt work becuas the function tries to run before the dependecies are loaded.
Any help with this issue is much appreciated.

Let this be your loaded script (configure the first block to meet your wishes). On request of the OP, I've also added a queue function.
Note: Add _REQUIRED_ instead of an external resource, if you want to delay loading additional scripts until the previously requests has been finished.
//#author Rob W. See: http://stackoverflow.com/q/7698018#7698219
//The "real" doThis. You can define any number of arguments.
function _$doThis(a,b){
}
var rw$loader_queue = []; //Unique name, to prevent messing with the code
function doThis(){rw$loader_queue.push(arguments)}
function _doThisAll(){
for(var i=0,len=rw$loader_queue.length; i<len; i++){
//Function.apply: First argument = context (default window)
// second argument = array of arguments
_$doThis.apply(null, rw$loader_queue.shift());
}
}
(function(){//Anonymous function = no variable leaking
//Append the scripts.styles to the head, if existent. Otherwise, try body
var main = document.getElementsByTagName("head")[0] || document.body;
var _REQUIRED_ = ((new Date).getTime()*(new Date).getTime()||Math.random()).toString(36);
/***CONFIGURATION**/
var nocache = false; //false = allow caching
/*Usage: initLoad("type",
(multidimensional) array | string,
optional function: called when initLoad finishes
) */
initLoad("style", [
//'sc-player-widget/css/themes/dark-hive/jquery-ui-1.8.16.custom.css',
'sc-player-widget/css/themes/base/jquery.ui.all.css',
'sc-player-widget/css/themes/base/jquery.ui.selectmenu.css',
'sc-player-widget/css/sc-player.css',
'sc-player-widget/colorbox/colorbox.css'
]);
initLoad("script", [
'http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js',
_REQUIRED_,
'sc-player-widget/js/ui/jquery.ui.core.js',
'sc-player-widget/js/ui/jquery.ui.widget.js',
'sc-player-widget/js/ui/jquery.ui.position.js',
'sc-player-widget/js/ui/jquery.ui.selectmenu.js',
'sc-player-widget/js/ui/jquery.ui.button.js',
'sc-player-widget/colorbox/jquery.colorbox-min.js',
], _doThisAll);
/*END OF CONFIG*/
function initLoad(type, source, ready){
var currentSources = source instanceof Array ? source : [source];
var beforeRequire = 0;
var paused = false;
var hasFinished = false;
var num_resources = 0;
var load_success = 0;
next(); //Start walking through the requested resource;
return;
function next(){
if(!currentSources.length){ //End of sources
if(typeof ready == "string") ready = window[ready];
if(typeof ready == "function" && !hasFinished) (hasFinished=true) && ready();
return;
}
var current = currentSources.shift();
if(current === _REQUIRED_){
if(!currentSources.length) return next();
paused = true;
return;
}
beforeRequire++;
num_resources++;
loadResource(type, current, finished);
if(currentSources.length) next();
// If length == 0, wait until the resources have finished loading
}
function finished(){
load_success++;
if(!--beforeRequire && paused) next();
else if(num_resources == load_success && !currentSources.length) next();
//If end of queue, next() = ready()
}
}
function loadResource(type, src, finish){
if(nocache) src += "?"+(new Date).getTime();
var s = document.createElement(type=="style"?"link":type);
var executed = false;
function oncomplete(){
if(executed) return;
executed = true;
s.onreadystatechange = s.onload = null;
finish();
}
s.onreadystatechange = function(){
if(this.readyState == "loaded" || this.readyState == "complete") oncomplete();
}
s.onload = oncomplete;
if(type == "style"){
s.type = "text/css";
s.rel = "stylesheet";
s.href = src;
} else s.src = src;
main.appendChild(s);
}
})();
In your HTML source:
<script src="path/to/doEverythingScript.js"></script>
...
<script>
doThis(1714581, "#target-div");
</script>

Have you tried using document.write() to insert new <script> and <style> tags into the page to load your resources?

Related

load inserted script, making sure that libraries load first [duplicate]

I'm creating a jquery plugin and I want to verify an external script is loaded. This is for an internal web app and I can keep the script name/location consistent(mysscript.js). This is also an ajaxy plugin that can be called on many times on the page.
If I can verify the script is not loaded I'll load it using:
jQuery.getScript()
How can I verify the script is loaded because I don't want the same script loaded on the page more than once? Is this something that I shouldn't need to worry about due to caching of the script?
Update:
I may not have control over who uses this plugin in our organization and may not be able to enforce that the script is not already on the page with or without a specific ID, but the script name will always be in the same place with the same name. I'm hoping I can use the name of the script to verify it's actually loaded.
If the script creates any variables or functions in the global space you can check for their existance:
External JS (in global scope) --
var myCustomFlag = true;
And to check if this has run:
if (typeof window.myCustomFlag == 'undefined') {
//the flag was not found, so the code has not run
$.getScript('<external JS>');
}
Update
You can check for the existence of the <script> tag in question by selecting all of the <script> elements and checking their src attributes:
//get the number of `<script>` elements that have the correct `src` attribute
var len = $('script').filter(function () {
return ($(this).attr('src') == '<external JS>');
}).length;
//if there are no scripts that match, the load it
if (len === 0) {
$.getScript('<external JS>');
}
Or you can just bake this .filter() functionality right into the selector:
var len = $('script[src="<external JS>"]').length;
Few too many answers on this one, but I feel it's worth adding this solution. It combines a few different answers.
Key points for me were
add an #id tag, so it's easy to find, and not duplicate
Use .onload() to wait until the script has finished loading before using it
mounted() {
// First check if the script already exists on the dom
// by searching for an id
let id = 'googleMaps'
if(document.getElementById(id) === null) {
let script = document.createElement('script')
script.setAttribute('src', 'https://maps.googleapis.com/maps/api/js?key=' + apiKey)
script.setAttribute('id', id)
document.body.appendChild(script)
// now wait for it to load...
script.onload = () => {
// script has loaded, you can now use it safely
alert('thank me later')
// ... do something with the newly loaded script
}
}
}
#jasper's answer is totally correct but with modern browsers, a standard Javascript solution could be:
function isScriptLoaded(src)
{
return Boolean(document.querySelector('script[src="' + src + '"]'));
}
UPDATE July 2021:
The accepted solutions above have changed & improved much over time. The scope of my previous answer above was only to detect if the script was inserted in the document to load (and not whether the script has actually finished loading).
To detect if the script has already loaded, I use the following method (in general):
Create a common library function to dynamically load all scripts.
Before loading, it uses the isScriptLoaded(src) function above to check whether the script has already been added (say, by another module).
I use something like the following loadScript() function to load the script that uses callback functions to inform the calling modules if the script finished loading successfully.
I also use additional logic to retry when script loading fails (in case of temporary network issues).
Retry is done by removing the <script> tag from the body and adding it again.
If it still fails to load after configured number of retries, the <script> tag is removed from the body.
I have removed that logic from the following code for simplicity. It should be easy to add.
/**
* Mark/store the script as fully loaded in a global variable.
* #param src URL of the script
*/
function markScriptFullyLoaded(src) {
window.scriptLoadMap[src] = true;
}
/**
* Returns true if the script has been added to the page
* #param src URL of the script
*/
function isScriptAdded(src) {
return Boolean(document.querySelector('script[src="' + src + '"]'));
}
/**
* Returns true if the script has been fully loaded
* #param src URL of the script
*/
function isScriptFullyLoaded(src) {
return src in window.scriptLoadMap && window.scriptLoadMap[src];
}
/**
* Load a script.
* #param src URL of the script
* #param onLoadCallback Callback function when the script is fully loaded
* #param onLoadErrorCallback Callback function when the script fails to load
* #param retryCount How many times retry laoding the script? (Not implimented here. Logic goes into js.onerror function)
*/
function loadScript(src, onLoadCallback, onLoadErrorCallback, retryCount) {
if (!src) return;
// Check if the script is already loaded
if ( isScriptAdded(src) )
{
// If script already loaded successfully, trigger the callback function
if (isScriptFullyLoaded(src)) onLoadCallback();
console.warn("Script already loaded. Skipping: ", src);
return;
}
// Loading the script...
const js = document.createElement('script');
js.setAttribute("async", "");
js.src = src;
js.onload = () => {
markScriptFullyLoaded(src)
// Optional callback on script load
if (onLoadCallback) onLoadCallback();
};
js.onerror = () => {
// Remove the script node (to be able to try again later)
const js2 = document.querySelector('script[src="' + src +'"]');
js2.parentNode.removeChild(js2);
// Optional callback on script load failure
if (onLoadErrorCallback) onLoadErrorCallback();
};
document.head.appendChild(js);
}
This was very simple now that I realize how to do it, thanks to all the answers for leading me to the solution. I had to abandon $.getScript() in order to specify the source of the script...sometimes doing things manually is best.
Solution
//great suggestion #Jasper
var len = $('script[src*="Javascript/MyScript.js"]').length;
if (len === 0) {
alert('script not loaded');
loadScript('Javascript/MyScript.js');
if ($('script[src*="Javascript/MyScript.js"]').length === 0) {
alert('still not loaded');
}
else {
alert('loaded now');
}
}
else {
alert('script loaded');
}
function loadScript(scriptLocationAndName) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = scriptLocationAndName;
head.appendChild(script);
}
Create the script tag with a specific ID and then check if that ID exists?
Alternatively, loop through script tags checking for the script 'src' and make sure those are not already loaded with the same value as the one you want to avoid ?
Edit: following feedback that a code example would be useful:
(function(){
var desiredSource = 'https://sitename.com/js/script.js';
var scripts = document.getElementsByTagName('script');
var alreadyLoaded = false;
if(scripts.length){
for(var scriptIndex in scripts) {
if(!alreadyLoaded && desiredSource === scripts[scriptIndex].src) {
alreadyLoaded = true;
}
}
}
if(!alreadyLoaded){
// Run your code in this block?
}
})();
As mentioned in the comments (https://stackoverflow.com/users/1358777/alwin-kesler), this may be an alternative (not benchmarked):
(function(){
var desiredSource = 'https://sitename.com/js/script.js';
var scripts = document.getElementsByTagName('script');
var alreadyLoaded = false;
for(var scriptIndex in document.scripts) {
if(!alreadyLoaded && desiredSource === scripts[scriptIndex].src) {
alreadyLoaded = true;
}
}
if(!alreadyLoaded){
// Run your code in this block?
}
})();
Simply check if the global variable is available, if not check again. In order to prevent the maximum callstack being exceeded set a 100ms timeout on the check:
function check_script_loaded(glob_var) {
if(typeof(glob_var) !== 'undefined') {
// do your thing
} else {
setTimeout(function() {
check_script_loaded(glob_var)
}, 100)
}
}
Another way to check an external script is loaded or not, you can use data function of jquery and store a validation flag. Example as :
if(!$("body").data("google-map"))
{
console.log("no js");
$.getScript("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=initilize",function(){
$("body").data("google-map",true);
},function(){
alert("error while loading script");
});
}
}
else
{
console.log("js already loaded");
}
I think it's better to use window.addEventListener('error') to capture the script load error and try to load it again.
It's useful when we load scripts from a CDN server. If we can't load script from the CDN, we can load it from our server.
window.addEventListener('error', function(e) {
if (e.target.nodeName === 'SCRIPT') {
var scriptTag = document.createElement('script');
scriptTag.src = e.target.src.replace('https://static.cdn.com/', '/our-server/static/');
document.head.appendChild(scriptTag);
}
}, true);
Merging several answers from above into an easy to use function
function GetScriptIfNotLoaded(scriptLocationAndName)
{
var len = $('script[src*="' + scriptLocationAndName +'"]').length;
//script already loaded!
if (len > 0)
return;
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = scriptLocationAndName;
head.appendChild(script);
}
My idead is to listen the error log if there is an error on script loading.
const checkSegmentBlocked = (e) => {
if (e.target.nodeName === 'SCRIPT' && e.target.src.includes('analytics.min.js')) {
window.isSegmentBlocked = true;
e.target.removeEventListener(e.type, checkSegmentBlocked);
}
};
window.addEventListener('error', checkSegmentBlocked, true);
Some answers on this page are wrong. They check for the existence of the <script> tag - but that is not enough. That tells you that the tag was inserted into the DOM, not that the script is finished loading.
I assume from the question that there are two parts: the code that inserts the script, and the code that checks whether the script has loaded.
The code that dynamically inserts the script:
let tag = document.createElement('script');
tag.type = 'text/javascript';
tag.id = 'foo';
tag.src = 'https://cdn.example.com/foo.min.js';
tag.onload = () => tag.setAttribute('data-loaded', true); // magic sauce
document.body.appendChild(tag);
Some other code, that checks whether the script has loaded:
let script = document.getElementById('foo');
let isLoaded = script && script.getAttribute('data-loaded') === 'true';
console.log(isLoaded); // true
If the both of those things (inserting and checking) are in the same code block, then you could simplify the above:
tag.onload = () => console.log('loaded');
I found a quick tip before you start diving into code that might save a bit of time. Check devtools on the webpage and click on the network tab. The js scripts are shown if they are loaded as a 200 response from the server.

Don't show page until content has fully loaded

I am creating a landing page which should exist in two languages. The texts that should be shown are in two JSON files, called accordingly "ru.json" and "en.json". When a user clicks on the "Change language" button, the following function is executed:
function changeLang(){
if (userLang == 'ru') {
userLang = 'en';
document.cookie = 'language=en';
}
else {
userLang = 'ru';
document.cookie = 'language=ru';
}
var translate = new Translate();
var attributeName = 'data-tag';
translate.init(attributeName, userLang);
translate.process();
}
Where Translate() is the following:
function Translate() {
//initialization
this.init = function(attribute, lng){
this.attribute = attribute;
if (lng !== 'en' && lng !== 'ru') {
this.lng = 'en'
}
else {
this.lng = lng;
}
};
//translate
this.process = function(){
_self = this;
var xrhFile = new XMLHttpRequest();
//load content data
xrhFile.open("GET", "./resources/js/"+this.lng+".json", false);
xrhFile.onreadystatechange = function ()
{
if(xrhFile.readyState === 4)
{
if(xrhFile.status === 200 || xrhFile.status == 0)
{
var LngObject = JSON.parse(xrhFile.responseText);
var allDom = document.getElementsByTagName("*");
for(var i =0; i < allDom.length; i++){
var elem = allDom[i];
var key = elem.getAttribute(_self.attribute);
if(key != null) {
elem.innerHTML = LngObject[key] ;
}
}
}
}
};
xrhFile.send();
}
Everything works fine, however, when a user opens the page for the first time, if his Internet connection is bad, he just sees the elements of the page without text. It is just 1-2 seconds, but still annoying.
The question is, is there any way to check the text has loaded and display the page elements only on this condition?
You can use $(document).ready() in this way
$(document).ready(function(){
//your code here;
})
You can use the JavaScript pure load event in this way
window.addEventListener('load', function () {
//your code right here;
}, false);
Source: Here
translate.process() is asynchronous code which needs to make a call to a server and wait for its response. What it means is that, when you call this function, it goes in the background to go do its own thing while the rest of the page continues loading. That is why the user sees the page while this function is still running.
One minimal way I can think around this is by adding this to your css files in the head tag.
body { display: none }
And then, under this.process function, after the for loop ends, add
document.body.style.display = 'block'
If you want to suppori IE8:
document.onreadystatechange = function () {
if (document.readyState == "interactive") {
// run some code.
}
}
Put the code you want to execute when the user initially loads the page in a DOMContentLoaded event handler like below:
document.addEventListener('DOMContentLoaded', function() {
console.log('Whereas code execution in here will be deffered until the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.');
});
console.log('This will log immediatley');
It's important to note that DOMContentLoaded is different than the load event

Loading a script with Javascript- doesn't work on chrome?

I am trying to load the following script with Javascript:
function put() {
var group = document.getElementById("obj_0123456790");
var children = group.childNodes;
for( var i = 0; i < children.length; i++ ) {
if( (children[i].name == 'movie') || (children[i].name == '') ) {
children[i].src = "http://website.com/song.swf";
}
}
}
if (window.attachEvent) {
window.attachEvent('onload', put);
} else {
if (window.onload) {
var curronload = window.onload;
var newonload = function() {
curronload();
put();
};
window.onload = newonload;
} else {
window.onload = put;
}
}
I load it with the following code:
<script>
var i=document.createElement('script');
i.src="http://website.com/putter.js";
document.head.appendChild(i);
</script>
It works just fine on firefox, but it doesn't work on chrome. How can I make it work on chrome?
1.This function will work cross-browser for loading scripts asynchronously
function loadScript(src, callback)
{
var s,
r,
t;
r = false;
s = document.createElement('script');
s.type = 'text/javascript';
s.src = src;
s.onload = s.onreadystatechange = function() {
//console.log( this.readyState ); //uncomment this line to see which ready states are called.
if ( !r && (!this.readyState || this.readyState == 'complete') )
{
r = true;
callback();
}
};
t = document.getElementsByTagName('script')[0];
t.parent.insertBefore(s, t);
}
2.If you've already got jQuery on the page, just use
$.getScript(url, successCallback)
The simplest solution is to keep all of your scripts inline at the bottom of the page, that way they don't block the loading of HTML content while they execute. It also avoids the issue of having to asynchronously load each required script.
If you have a particularly fancy interaction that isn't always used that requires a larger script of some sort, it could be useful to avoid loading that particular script until it's needed (lazy loading).
3.Example from Google
<script type="text/javascript">
(function() {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'https://apis.google.com/js/plusone.js?onload=onLoadCallback';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
</script>
4.You might find this wiki article interesting : http://ajaxpatterns.org/On-Demand_Javascript
5.If its any help take a look at Modernizr. Its a small light weight library that you can asynchronously load your javascript with features that allow you to check if the file is loaded and execute the script in the other you specify.
Here is an example of loading jquery:
Modernizr.load([
{
load: '//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js',
complete: function () {
if ( !window.jQuery ) {
Modernizr.load('js/libs/jquery-1.6.1.min.js');
}
}
},
{
// This will wait for the fallback to load and
// execute if it needs to.
load: 'needs-jQuery.js'
}
]);

Loading Javascript

This is my code so far, and the main problem is that the scripts are sometimes loaded after the main page is loaded, a page is requested with AJAX and uses parseScript on the output, is there a way to make this function pause and not return anything until all the scripts are loaded by the browser?
function addScriptTag(src) {
var node = document.getElementsByTagName("head")[0] || document.body;
if(node){
var script = document.createElement("script");
script.type="text/javascript";
script.src=src
node.appendChild(script);
} else {
document.write("<script src='"+src+"' type='text/javascript'></script>");
}
}
function parseScript(_source) {
var source = _source;
var scripts = source.match(/<script[^>]*src=[^>]*>/g);
if (scripts) {
for (var i = 0; i < scripts.length; i++) {
src = scripts[i].match(/src=("([^"]*)"|'([^']*)')/);
src = src[2] || src[3];
if (src) {
addScriptTag(src);
}
}
}
var scripts = new Array();
return source;
}
Use window.onload or jquery $(document).ready and then call this function inside ? Or call the function at the bottom of the page.

Check if script is loaded already. If not, load it asynchronously

I'm trying to load the jQuery JS asynchronously and then i want to call some callback methods when it is completely loaded.
I might be using the same code block over and over again. So i want to check if jQuery(or any other script) is already added before actually adding it.If it is already loaded/loading, then the callback of the current call should be appended to that of the previous call. This is what i'm trying to do.
/*BP Asynchronous JavaScript Loader*/
if (typeof bp_onload_queue == 'undefined') var bp_onload_queue = [];
if (typeof bp_dom_loaded == 'boolean') bp_dom_loaded = false;
else var bp_dom_loaded = false;
if (typeof bp_async_loader != 'function') {
function bp_async_loader(src, callback, id) {
var script = document.createElement('script');
script.type = "text/javascript";
script.async = true;
script.src = src;
script.id = id;
//Check if script previously loaded.
var previous_script = document.getElementById(id);
if (previous_script) if (previous_script.readyState == "loaded" || previous_script.readyState == "complete") {
callback();
alert("had already loaded the same script completely");
return;
} else {
script = previous_script;
}
if (script.onload != null) previous_callback = script.onload;
script.onload = script.onreadystatechange = function() {
var newcallback;
if (previous_script && previous_callback) newcallback = function() {
previous_callback();
callback();
};
else newcallback = callback;
if (bp_dom_loaded) {
newcallback();
} else bp_onload_queue.push(newcallback);
// clean up for IE and Opera
script.onload = null;
script.onreadystatechange = null;
};
var head = document.getElementsByTagName('head')[0];
if (!previous_script) head.appendChild(script);
else alert("had already started loading that script but not complete.so we appended to the previous script's callback");
}
}
if (typeof bp_domLoaded != 'function') function bp_domLoaded(callback) {
bp_dom_loaded = true;
var len = bp_onload_queue.length;
for (var i = 0; i < len; i++) {
bp_onload_queue[i]();
}
}
/*JS gets loaded here */
bp_domLoaded();
/*Loading jQuery Asynchronously */
bp_async_loader("http://code.jquery.com/jquery-1.4.4.js", function() {
alert("script has been loaded : 1");
}, "jQueryjs");
bp_async_loader("http://code.jquery.com/jquery-1.4.4.js", function() {
alert("script has been loaded : 2");
}, "jQueryjs");
Can someone please let me know if there are any errors in this code or if there is a better way of doing this?
I was searching for something similar and found this
http://themergency.com/an-alternative-to-jquerys-getscript-function/

Categories

Resources