How to change the source of a <script> element in JavaScript? - javascript

I am trying to change the src attribute of a script in the head of the page positioned below the main script. Here is a part of the code:
function loadLevel(l) {
switch (l) {
case 0:
document.getElementById("level").src = "levels/level0.js";
level0.load();
break;
case 1:
document.getElementById("level").src = "levels/level1.js";
level1.load();
break;
}
level = l;
}
And the code of the script:
<script id="level" src=""></script>
When the function loadLevel(l) is called the switch checks the value of l, changes the value of the source of the script called "level" accordingly and calls the right loading function. The objects level0 and level1 and their load functions are stored respectively in the files level0.js and level1.js.
But when I do this, it won't work. level0.js and level1.js have no mistakes in them because when I do this it does load level 0, but obviously not level 1 when I press the button to do so:
<script id="level" src="levels/level0.js"></script>

When you load a new script the process is async, so calling the function stright after wont work as the script wont have loaded, you'd need a call back once complete.
function loadLevel(level) {
(function(d, script) {
script = d.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.onload = function(){
/*
Your script has loaded
You can now call the code you have loaded
*/
};
script.src = '/levels/level' + level + '.js';
d.getElementsByTagName('head')[0].appendChild(script);
}(document));
}

I refer you to use modules instead of using regular scripts.
Here is the code you can use:
async function loadLevel(l){
let {load}= await import("/levels/level"+l+".js");
load();
}
And in your level scripts:
function load(){
//Your code
}
export {load};
And some references for modules:https://javascript.info/modules-intro, https://javascript.info/import-export, https://javascript.info/modules-dynamic-imports
And here is my example code on Github: https://github.com/Learndev-student/Example/tree/main/Example%20001
And see it running on web: https://learndev-student.github.io/Example/Example%20001/index.html

Related

How do I load a new script after the page is done loading?

I have two separate script files (script1.js, script2.js). Each of the files has its own functions/variables defined in it. For the sake of simplicity, I will assume each file holds a separate variable. So the files will look like:
script1.js
var x = 2;
script2.js
var y = 2;
I am using the scripts in index.html:
index.html
<button onclick="change()">Change script</button>
<script id="file" src="script1.js"></script>
<script>
function change() {
var file = document.getElementById("file");
if(file.src.slice(-10) == "script1.js") {
file.src = "script2.js";
} else {
file.src = "script1.js";
}
}
</script>
But when I change the src attribute for the script, the loaded script does not change. So even after switching scripts, x has the value 2 while y is undefined.
How do I switch the script after the page has finished loading?
Not sure what you want to accomplish, but as far as loading of javascript is concern, you can use:
$("#id_of_button").click(function(){
$.getScript('helloworld.js', function() {
//do whatever you want to accomplish here....
});
});
More detail here
A better way may be to keep the related code in separate functions in same js file and calling the specific function to override the logic based upon your condition check. Though I'm still not clear what you are trying to achieve. Could I get some scenario based idea to get it clear?
You have to create a new script in order to loaded it, the problem is that you also want to maintain the position of the script.
So here I wrote an example that will replace the old script and insert the new one at the same position.
Read the comment to understand how this work.
function change() {
var file = document.getElementById("file"); // get the script you want to change
var newscript = document.createElement("script"); // create new script
newscript.src = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" // set the new script src
newscript.setAttribute("id","file"); // set the id to the same id as the old script
newscript.type = 'text/javascript';
file.parentNode.insertBefore(newscript, file); // insert the new script before the old one
file.remove() // remove the old script
var callback= function(){ // when the script has been loded then test and see if jQuery is working now
$("body").append("<p>Jq loaded</p>"); // no error then jQuery has been loaded
}
newscript.onreadystatechange = callback;
newscript.onload = callback;
}
<script id="file" src="script1.js"></script>
<button onclick="change()">Change script</button>
You can try this: https://codepen.io/anon/pen/mvMZOR
HTML
<button type="button">Change script</button>
<script id="file" src="script1.js"></script>
Javascript
var index = 1;
var scriptId = 'file';
var button = document.querySelector('button');
button.addEventListener('click', function() {
// Remove the old script
document.getElementById(scriptId).remove();
// Create the new one
var s = document.createElement('script');
// Add the id you want, in this case "file"
s.id = scriptId;
// It will return "script1.js" or "script2.js" alternatively
s.src = 'script' + (index++ % 2 + 1) + '.js';
// Append your new script at the end of your body
document.querySelector('body').append(s);
});

What is the difference between these two snippets of javascript code?

I know they do the same thing more or less, its just the approach on how its done.
<script src="example.js" type="text/javascript" charset="UTF-8"></script>
<script type="text/javascript">
function OptanonWrapper() { }
</script>
<script type="text/javascript">
var x = x || [];
(function(){
setTimeout(function(){
var d = document, f = d.getElementsByTagName('script')[0], s = d.createElement('script'); s.type = 'text/javascript';
s.async = true; s.src = "example.js"; f.parentNode.insertBefore(s,f);
}, 1);
})();
</script>
--
I am not a native js programmer so your help would be greatly appreciated.
In the first example, the second <script> tag will only execute after example.js has finished loading.
In the second example, the <script> tag that loads example.js is created dynamically and inserted into the document (in a needlessly roundabout way, if I may add my own two cents), and it will start loading asynchronously, i.e. it does not delay the execution of any <script> tags after it. The same effect could be achieved this way:
<script src="example.js" async></script>
<script>
function OptanonWrapper() { }
</script>
Read up on the <script> element on MDN for more details:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script

Serve alternate external Javascript file depending on if elements are defined or not in the page

Ok, here goes my first question on here.
Setup: We use a javascript based tool to A/B test our landing page designs. I need version A (control) to link to one external javascript file, and version B (variation) to link to an alternate javascript file.
Goal: to have an internal js script at the bottom of the control that looks to see if the tool is in fact serving A or B, and if true, which one was served. The result indicates which external script should be linked.
Issue: regardless of if the tool is in fact serving A or B, the original script is linked first, then if the tool is detected, the appropriate script is linked after that.
Here is my code (I apologize in advance for any newbie mistakes):
//script at bottom of original or tool-served control html page template
<script type="text/javascript">
valForTool = function () {
var variationId = _tool_exp[_tool_exp_ids[0]].combination_chosen;
if (variationId == 1) {
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'js/scripts.js';
document.body.appendChild(newScript);
};
}
originalValidation = function () {
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'js/scripts.js';
document.body.appendChild(newScript);
}
$(function(){
if (typeof _tool_exp_ids !== 'undefined' && typeof _tool_exp_ids[0] !== 'undefined') {
valForTool();
} else {
originalValidation();
};
});
</script>
//end script on control or original template
//script on tool-served variation html template - will run after the above script
<script type="text/javascript">
$(function() {
$('#project_info input[type=submit]').removeAttr('disabled');
$('#project_info').unbind();
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = 'js/scripts2.js';
document.body.appendChild(newScript);
$('.input_text').parent().addClass('contact_field');
});
</script>
// end script on variation template
Any ideas as to what I'm doing wrong? Did I provide enough information? Thanks! I love this site as a reference for my questions, but this is my first time actually posting one.
Shortening it down a bit, it seems like your just doing this:
<script type="text/javascript">
$(function(){
if (typeof _tool_exp_ids !== 'undefined' && typeof _tool_exp_ids[0] !== 'undefined') {
var variationId = _tool_exp[_tool_exp_ids[0]].combination_chosen;
if (variationId == 1) {
$.getScript('js/scripts.js', function() {runSecond();});
}
}else{
$.getScript('js/scripts.js', function() {runSecond();});
}
function runSecond() {
$('#project_info input[type=submit]').removeAttr('disabled').unbind();
$.getScript('js/scripts2.js');
$('.input_text').parent().addClass('contact_field');
}
});
</script>
Now looking at that, it's obvious that both scripts are running no matter what conditions are met in those if/else statements, and I don't really get what it is your trying to do, but the first thing i would do, is to add some console.logs to see if those if/else statements are working like they are supposed to, and then figure what scripts should be loaded under which conditions etc ?

Check if Javascript script exists on page

I have a bookmarklet that I've made and it loads a script from my server onto the users current page. However I have an if check in my script that if a condition is not met then no action is taken. However if the user then meets that condition then the code is run, but has caused there to be two sets of scripts inserted into their page. Can i prevent this?
<a href="javascript: (function () {
var jsCode = document.createElement('script');
jsCode.setAttribute('src', 'http://xxx.co.uk/xxx/script.js');
document.body.appendChild(jsCode);
}());">Bookmarklet</a>
You can check whether your script is loaded like this:
function isMyScriptLoaded(url) {
if (!url) url = "http://xxx.co.uk/xxx/script.js";
var scripts = document.getElementsByTagName('script');
for (var i = scripts.length; i--;) {
if (scripts[i].src == url) return true;
}
return false;
}
Alternatively, you could do something like this:
<a href="javascript:
if (!jsCode) {
var jsCode = document.createElement('script');
jsCode.setAttribute('src', 'http://xxx.co.uk/xxx/script.js');
document.body.appendChild(jsCode);
}
">Bookmarklet</a>
This "pollutes" the global namespace with the jsCode variable, but that might be a necessary evil. You could rename it to something that is unlikely to appear in the document where the bookmarklet is run.
Please note that while the javascript URI scheme is okay for bookmarklets as in this case, it's not considered to be a good practice for normal use.
Just check the selector length. Here's an example using jQuery:
if ($('script[src="http://xxx.co.uk/xxx/script.js"]').length > 0) {
//script exists
}
You can place id attributes on your script tags and use document.getElementById('your-id') to identify whether the script is on the page before adding.
if (!document.getElementById('your-id')) {
// append your script to the document here, ensure it has its id attribute set to 'your-id'
}
Solution with ES6, no jQuery:
const url = 'http://xxx.co.uk/xxx/script.js';
function scriptExists(url) {
return document.querySelectorAll(`script[src="${url}"]`).length > 0;
}
if(scriptExists(url)) {
...
}
It's not recommended to inline JS into HTML. Instead add event listeners:
function bookmark() {
if(scriptExists(url)) {
...
}
}
const button = document.querySelectorAll('a.bookmark');
button.addEventListener('click', bookmark, false);
In case working with local and live alternatively.
The exact URL may change. I think the ID method is better.
This is a combination of Two StackOverflow answers.
if (!document.getElementById('your-id')) {
addScript("your_script_src"); //adding script dynamically
addCSSFile("your_css_src"); // adding css files
}
function addScript(path) {
var head = document.getElementsByTagName("head")[0];
var s = document.createElement("script");
s.type = "text/javascript";
s.src = path;
s.id = "your-id";
head.appendChild(s);
}
function addCSSFile(path) {
var head = document.getElementsByTagName("head")[0];
var s = document.createElement("style");
s.type = "text/css";
s.src = path;
head.appendChild(s);
}
if you create a variable in the global scope (window.yourVariable) and check if that exists already then you can decide if you want to add your jsCode snippet code or run whatever you are running in script.js
if (document.getElementById('element-id')) {
// if exist must do something
}
hi, this is worked for me, please try it if you still need it

innerHTML to insert script in body (for ZeroClipboard) not working

I'm building <div> elements using AJAX, and I want to add ZeroClipboard functionality. Firebug shows the code is building correctly, and when I copy it into a raw HTML test page it works too. The builds are not happening at onload, but down the track.
The code is as follows, calling some functions that create the new elements:
dom_append_child_with_onclick ("img",export_id,"icon_active",report_heading_id, "event.cancelBubble = true;");
dom_append_child ("div",export_script_id,"",report_heading_id);
text = "<script language='JavaScript'>var clip" +rnum +"=new ZeroClipboard.Client();clip"+rnum+".setText('');clip"+rnum+".addEventListener('mouseDown',function(client){alert('firing');clip"+rnum+".setText(document.getElementById('SL40').value);});clip"+rnum+".glue('XR"+rnum+"','RH"+rnum+"');</script>";
document.getElementById(export_script_id).innerHTML=text;
My question: when you insert a script into the <body>, do you have to do something to get it to fire? The script appears not to be doing its thing, and I can't get the alert 'firing' to display.
Note: the cancelBubble is to stop the onClick function of the underlying element. It may be unnecessary if I can get the flash working.
Thanks.
You can just inject your script into the page as a DOM object, but this does not work in all browsers:
var s = document.createElement("script");
s.type = "text/javascript";
s.innerText = "var clip" +rnum +"=new ZeroClipboard.Client();clip"+rnum+".setText('');clip"+rnum+".addEventListener('mouseDown',function(client){alert('firing');clip"+rnum+".setText(document.getElementById('SL40').value);});clip"+rnum+".glue('XR"+rnum+"','RH"+rnum+"');";
document.getElementsByTagName("head")[0].appendChild(s);
Or, for better compatibility, you probably want to just declare a function which sets this up in your page, and then just call the function with the rnum as the parameter.
e.g.
function useZeroClipboard(rnum) {
window["clip" + rnum] = new ZeroClipboard.Client();
cwindow["clip" + rnum].setText('');
window["clip" + rnum].addEventListener('mouseDown', function(client){
alert('firing');
window["clip" + rnum].setText(document.getElementById('SL40').value);
});
window["clip" + rnum].glue('XR"+rnum+"','RH"+rnum+"');
}
Then you can just call that in your code:
useZeroClipboard(rnum);
Instead of writing the script block.
Here is a method that recursively replaces all scripts with executable ones:
function replaceScriptsRecurse(node) {
if ( nodeScriptIs(node) ) {
var script = document.createElement("script");
script.text = node.innerHTML;
node.parentNode.replaceChild(script, node);
}
else {
var i = 0;
var children = node.childNodes;
while ( i < children.length) {
replaceScriptsRecurse( children[i] );
i++;
}
}
return node;
}
function nodeScriptIs(node) {
return node.getAttribute && node.getAttribute("type") == "text/javascript";
}

Categories