I am not sure what is the best approach to load all .js files from a folder from within index.html
The number of .js files will change so I cannot define them in the index.html the usual way. I cannot use php or jQuery solutions which would make it easier.
<script>
var count = 1;
function LoadNext()
{
var newScript = document.createElement("script");
newScript.setAttribute("type", "text/javascript");
newScript.setAttribute("src", "page-" + count++ + ".js"); //Increment the counter so we get the next page each time
newScript.onload = LoadNext; //When the script loads call this function again
document.getElementsByTagName("head")[0].appendChild(newScript)
}
LoadNext();
</script>
You can load a new JS file by creating a new <script> tag.
var filename = "JSDir/JSFile.js";
var newScript = document.createElement("script"); ///Create a new <script> element
newScript.setAttribute("type", "text/javascript"); // Set type to JS
newScript.setAttribute("src", filename); //Set src, your file to load
document.getElementsByTagName("head")[0].appendChild(newScript) //Append this script tag to the end of your head element
If you know your JS files are going to be named basename-1.js you can set a callback so that onload you increment to the next fielname basename-2.js 3, 4... and when the final one fails to load it will stop trying to load further.
var count = 1;
function LoadNext()
{
var newScript = document.createElement("script");
newScript.setAttribute("type", "text/javascript");
newScript.setAttribute("src", "basename-" + count++ + ".js"); //Increment the counter so we get the next page each time
newScript.onload = LoadNext; //When the script loads call this function again
document.getElementsByTagName("head")[0].appendChild(newScript)
}
LoadNext(); //Load basename-1.js
^^Not tested, but if that way of checking the file exists fails. You can always use AJAX and check for 404
Related
Step 1: I'm loading an external script, onload is set to call window.initControllers:
(function(document, tag) {
var script = document.createElement(tag),
el = document.getElementsByTagName(tag)[0];
script.src = 'https://unpkg.com/#hotwired/stimulus#3.1.0/dist/stimulus.umd.js';
script.onload = function () {
window.initControllers();
};
el.parentNode.insertBefore(script, el);
}(document, 'script'));
Step 2: actually define the window.initControllers function, in another dynamically added script:
(function(document, tag) {
var script = document.createElement(tag),
el = document.getElementsByTagName(tag)[0];
script.type = 'module';
var contents = "var initControllers = function () { console.log('initControllers'); };";
contents += 'window.initControllers = initControllers;';
script.appendChild(document.createTextNode(contents));
el.parentNode.insertBefore(script, el);
}(document, 'script'));
The problem: on some page refresh, I get:
Uncaught TypeError: window.initControllers is not a function.
Apart from adding some fancy methods around setTimeout... which option do I have to ensure that window.initControllers is defined at the time that script step 1 is loaded?
script.type = 'module' means processing of the script content will be deferred - so you could explicitly set defer for the first one.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer:
Scripts with the defer attribute will execute in the order in which they appear in the document.
You might need to change the order that you append them in though, insertBefore will mean they end up in reverse order in the DOM.
Here is my code which might not be the best solution of my problem, and my goal is to check the external script (script.js) every 2 seconds if it's code has changed. If it is changed, then execute it.
function connectLoader(retval) {
console.log('Executing...');
var old = document.getElementById("EPMagic");
if (old !== null) {
old.outerHTML = "";
delete old;
}
var head = document.getElementsByTagName("head")[0];
var script = document.createElement('script');
script.setAttribute('id','EPMagic');
script.setAttribute('type','text/javascript');
script.setAttribute('src','http://server.com/script.js');
head.appendChild(script);
}
setInterval('connectLoader()',2000);
The problem is that /script.js is still executed even if it is not being changed.
The code on /script.js is simply alert('Execute');
Use setAttribue to set the attributes values:
var head = document.getElementsByTagName("head")[0];
var script = document.createElement('script');
script.setAttribute('id','loadScript');
script.setAttribute('type','text/javascript');
script.setAttribute('src','http://server.ip/script.js');
head.appendChild(script);
When a particular event happens that I listen out for - I want to populate the div 'divDynamicAdvert` with the javascript that calls my Google Ad code.
Huge thanks to "tenbits" for showing me that I need to be appending a script node like this:
function myEventHandler(evt) //called when event is caught
{
var script = document.createElement('script');
script.textContent = 'alert(1)';
document.getElementById('divDynamicAdvert').appendChild(script);
}
This works great, but in place of the alert, I need to insert the Google Ads Javascript:
<script type="text/javascript"><!--
google_ad_client = "ca-pub-3286208413631803";
/* Standard MPU */
google_ad_slot = "8630273973";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
I've tried:
var script = document.createElement('script');
script.textContent = 'google_ad_client = 'ca-pub-3286208413631803'; google_ad_slot = '8630273973'; google_ad_width = 300; google_ad_height = 250;';
document.getElementById('divDynamicAdvert').appendChild(script);
var script = document.createElement('script src="http://pagead2.googlesyndication.com/pagead/show_ads.js"');
document.getElementById('divDynamicAdvert').appendChild(script);
I've tried escaping the slashes too, but no luck... Can you guys help?
As #slebetman already said, innerHTML wont work. If you trying to evaluate script via DOM, and not eval(code), etc - do this with SCRIPT Node:
var script = document.createElement('script');
script.textContent = 'alert(1)';
document.getElementById('divDynamicAdvert').appendChild(script);
// actually it doesnt matter where you append it
EDIT:
In any case create a SCRIPT NODE, and then manipulate with it - add a script content to it OR add src attribute if you reference external source
var script;
// content
script = document.createElement('script')
script.textContent = 'alert(1)'
document.body.appendChild(script);
// google api source
script = document.createElement('script');
script.src = 'ANY_URL';
// -> in this case, same as script.setAttribute('src', 'ANY_URL');
document.body.appendChild(script);
Having some further questions, do not hesitate to ask in comments.
I don't know how I can synchronize next code:
javascript: (function() {
var s2 = document.createElement('script');
s2.src = 'http://192.168.0.100/jquery.js';
document.body.appendChild(s2);
s = document.createElement('link');
s.rel = "stylesheet";
s.href = "http://192.168.0.100/1.css";
s.type = "text/css";
document.body.appendChild(s);
})();
//var startTime = new Date().getTime();
//while (new Date().getTime() < startTime + 1000);
$(document).ready(function(){
b="c:\\1.txt";
var fso, f1;
fso = new ActiveXObject("Scripting.FileSystemObject");
f1 = fso.CreateTextFile(b, true);
f1.WriteLine("Testing") ;
document.writeln("File " + b + " is created.");
});
When I run this script at first I get an error SCRIPT5009: '$' is undefined. I think that is because jQuery library is yet no loaded. When I try to run the same script after error's appearencing - it behavior is corect. For synchonization I try to use code that is commented in upper listing. Is works (not always). But I understand that is no strict synchronization (it dependet of concrete situation). How can I use more clever synchronization?
(function($){
//use $ now
$(function(){
//dom ready
});
})(jQuery);
Be sure to load this libary in the footer below the jquery library.
This is due to the fact that putting in the script tag via appendChild dhtml method makes it evaluate async. To listen for it getting 'ready' as youre doing it on the document - follow this pattern
(function() {
var s2 = document.createElement('script');
s2.src = 'http://192.168.0.100/jquery.js';
document.body.appendChild(s2);
s2.onload = s2.onreadystatechange = function(){
if(s2.readyState == "complete") $(document).ready(function(){
b="c:\\1.txt";
var fso, f1;
fso = new ActiveXObject("Scripting.FileSystemObject");
f1 = fso.CreateTextFile(b, true);
f1.WriteLine("Testing") ;
document.writeln("File " + b + " is created.");
});
}
s = document.createElement('link');
s.rel = "stylesheet";
s.href = "http://192.168.0.100/1.css";
s.type = "text/css";
document.body.appendChild(s);
})();
The issue is that the line with the $ is being hit when the script tag has been added but before the file has been downloaded. Normally browsers block while loading JavaScript which prevents this issue, but since you're doing it programatically you don't get the standard synchrony.
Your best bet would be to wait for the script to be loaded. There's information here:
Dynamic, cross-browser script loading
On catching the load event for the completion of the script download. You can then call your existing $ ready function as a callback to that event. I'd suggest using a loader that is made for this stuff, or just use a standard script tag in the DOM unless you have a compelling reason not to.
How can I get the path of the current script in javascript using jQuery
for example I have site.com/js/script.js and there is a code in this script:
$(document).ready(function() {
alert( ... this code ... );
}
It Should return alert box with the "/js/script.js" message. This function should work like magic __FILE__ constant in php
So, why do I need this?
I want to set background image dynamically:
$("somediv").css("background-image", "url(" + $SCRIPT_PATH + "/images/img.png)");
and images directory is the /js directory, near the script.js file
and js folder's name can be dynamically set, so script and images can be in the /myprogect/javascript-files directory
You can rely on the fact that each <script> element has to be evaluated* before the next one is inserted into the DOM.
This means that the script currently evaluated (as long as it is part of your markup and not dynamically inserted) will be the last one in the NodeList retrieved with getElementsByTagName( 'script' ).
This allows you to read that elements src attribute and from that determine the folder that the script is being served from - like this:
var scriptEls = document.getElementsByTagName( 'script' );
var thisScriptEl = scriptEls[scriptEls.length - 1];
var scriptPath = thisScriptEl.src;
var scriptFolder = scriptPath.substr(0, scriptPath.lastIndexOf( '/' )+1 );
console.log( [scriptPath, scriptFolder] );
I tried this technique with 3 scripts loaded from different folders and get this output
/*
["http://127.0.0.1:8000/dfhdfh/folder1/script1.js", "http://127.0.0.1:8000/dfhdfh/folder1/"]
["http://127.0.0.1:8000/dfhdfh/folder2/script2.js", "http://127.0.0.1:8000/dfhdfh/folder2/"]
["http://127.0.0.1:8000/dfhdfh/folder3/script3.js", "http://127.0.0.1:8000/dfhdfh/folder3/"]
*/
* from John Resigs blog linked to above
This means that when the script finally executes that it'll be the
last script in the DOM - and even the last element in the DOM (the
rest of the DOM is built incrementally as it hits more script tags, or
until the end of the document).
Update
As pimvdb points out - this will work as the script is being evaluated. You will need to store the path somehow if you are going to use it later. You can't query the DOM at a later point. If you use the same snippet for each script the value of scriptFolder will be overwritten for each script. You should give each script a unique variable perhaps?
Wrapping your script in its own scope closes over the value of scriptFolder making it available to the rest of the script without fear of being overwritten
(function() {
var scriptEls = document.getElementsByTagName( 'script' );
var thisScriptEl = scriptEls[scriptEls.length - 1];
var scriptPath = thisScriptEl.src;
var scriptFolder = scriptPath.substr(0, scriptPath.lastIndexOf( '/' )+1 );
$( function(){
$('#my-div').click(function(e){
alert(scriptFolder);
});
});
})();
Add the following code to your JS :
var retrieveURL = function(filename) {
var scripts = document.getElementsByTagName('script');
if (scripts && scripts.length > 0) {
for (var i in scripts) {
if (scripts[i].src && scripts[i].src.match(new RegExp(filename+'\\.js$'))) {
return scripts[i].src.replace(new RegExp('(.*)'+filename+'\\.js$'), '$1');
}
}
}
};
Suppose these are the scripts called in your HTML :
<script src="assets/js/awesome.js"></script>
<script src="assets/js/oldcode/fancy-stuff.js"></script>
<script src="assets/js/jquery/cool-plugin.js"></script>
Then, you can use the function like this
var awesomeURL = retrieveURL('awesome');
// result : 'assets/js/'
var awesomeURL = retrieveURL('fancy-stuff');
// result : 'assets/js/oldcode/'
var awesomeURL = retrieveURL('cool-plugin');
// result : 'assets/js/jquery/'
Note that this only works when there are no two script files in your HTML with the same name. If you have two scripts with the same name that are located in a different folder, the result will be unreliable.
Note
If you dynamically add scripts to your page, you need to make sure your code is executed after the last script has been added to the DOM.
The follow example shows how to do this with one dynamically loaded script. It outputs a JSON array with the src link for scripts that load an external js file and a base64-encoded string of the JS content for inline scripts:
var addScript = function(src, callback) {
var s = document.createElement( 'script' );
s.setAttribute( 'src', src );
document.body.appendChild( s );
s.onload = callback;
}
var retrieveURL = function(filename) {
var scripts = document.getElementsByTagName('script');
if (scripts && scripts.length > 0) {
for (var i in scripts) {
if (scripts[i].src && scripts[i].src.match(new RegExp(filename+'\\.js$'))) {
return scripts[i].src.replace(new RegExp('(.*)'+filename+'\\.js$'), '$1');
}
}
}
};
addScript('https://code.jquery.com/jquery-1.12.4.min.js', function() {
var scripts = document.getElementsByTagName('script');
var sources = [];
for (var i = 0; i < scripts.length; i++) {
if(scripts[i].src == '') {
sources.push(btoa(scripts[i].innerHTML));
} else {
sources.push(scripts[i].src);
}
}
document.body.innerHTML += '<pre>' + JSON.stringify(sources,null,2) + '</pre>';
});
See also this Fiddle.
I don't think jQuery provide such a functionality.
Anyway you can get currentc script path path (both fully http and relative) by reading the answer here: What is my script src URL?
Can't you set a kind of path variable in the js? So, you save the path of the file in the file itself.
For example:
$(function() {
var FilePath = "js/some/path";
setMyDynamicImageUsingPath(FilePath);
});
// ...
function setMyDynamicImageUsingPath(path) {
$("somediv").css("background-image", "url(" + path + "/images/img.png)");
}