how can you determine location of <script> tag from inside said tag? - javascript

I am trying to figure out the location of the script tag the current javascript is running in. What is really going on is that I need to determine from inside a src'd, dynamically inserted javascript file where it is located in the DOM. These are dynamically generated tags; code snippet:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>where am i?</title>
<script type="text/javascript" charset="utf-8">
function byId(id) {
return document.getElementById(id);
}
function create_script(el, code) {
var script = document.createElement("script");
script.type = "text/javascript";
script.text = code;
el.appendChild(script);
}
</script>
</head>
<body>
<div id="find_me_please"></div>
<script>
create_script(byId("find_me_please"), "alert('where is this code located?');");
</script>
</body>
</html>

You could give the script an id tag, like this dude does...

You can use document.write to create a dummy DOM object and use parentNode to escape out. For example:
<script>
(function(r) {
document.write('<span id="'+r+'"></span>');
window.setTimeout(function() {
var here_i_am = document.getElementById(r).parentNode;
... continue processing here ...
});
})('id_' + (Math.random()+'').replace('.','_'));
</script>
This assumes you don't actually have control of the <script> tag itself, such as when it's inside a <script src="where_am_i.js"></script> - if you do have control of the <script> tag, simply put an ID on it, as in:
<script id="here_i_am">...</script>

If you are just running this on page load, this works
<script>
var allScripts = document.getElementsByTagName('script');
var thisScript = allScripts[allScripts.length];
alert(thisScript);
</script>

Related

Clarification for appendChild

I added a Cookies Consent banner. The requirement is that https://cdn.cookielaw.org/consent/c4337328/OtAutoBlock.js loaded before any other script. I now wonder if appendChild is the right choice. Will it load OtAutoBlock at the exact position I wrote it or will it append the script to the end of the tag (which would be too late). It has to be the first script that's loaded.
<!DOCTYPE html>
<html lang="en">
<head>
<!-- OneTrust Cookies Consent Notice -->
<script type="text/javascript">
if ("%REACT_APP_COOKIE_BAR%" === "true") {
var otAutoBlock = document.createElement("script");
otAutoBlock.setAttribute("type", "text/javascript");
otAutoBlock.setAttribute("src", "https://cdn.cookielaw.org/consent/c4337328/OtAutoBlock.js");
var otSDKStub = document.createElement("script");
otSDKStub.setAttribute("type", "text/javascript");
otSDKStub.setAttribute("src", "https://cdn.cookielaw.org/scripttemplates/otSDKStub.js");
otSDKStub.setAttribute("charset", "UTF-8");
otSDKStub.setAttribute("data-domain-script", "c4337328");
document.getElementsByTagName("head")[0].appendChild(otAutoBlock);
document.getElementsByTagName("head")[0].appendChild(otSDKStub);
function OptanonWrapper() { }
}
</script>
<script type="text/javascript">
/* [Should load after OtAutoBlock loads to avoid tracking before consent was given] */
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
document.head.appendChild(script) will add it to the end of the <head> tag, so it will load after all other scripts. You can do two things:
You load it with appendChild at the end, and have all your other scripts with a defer attribute, like this: <script defer> /* something */ </script> The defer forces a script to execute after the page is loaded, but if you load the OtAutoBlock without a defer, it will run before the others. The only thing to note is that defer will cause the scripts not to run last, but after the entire page loads, which includes CSS stylesheets, other JavaScripts, icons, images, HTML content, XHR requests in <script> tags, etc.
<script>
const load = true; // your stuff here
if (load)
{
const script = document.createElement('script');
window.hasRunned = false;
script.text = 'alert(\'OtAutoBlock running.\'); window.hasRunned = true;';
document.head.appendChild(script);
}
</script>
<script defer>
alert('Another script. Has runned OtAutoBlock: ' + window.hasRunned);
</script>
<script defer>
alert('And yet another. Has runned OtAutoBlock: ' + window.hasRunned);
</script>
You use insertBefore to add it before the other scripts, so it will run before them. No need to use defer with this method. This might be what you want if you need the scripts to run before the page loads.
<script>
const load = true; // your stuff here
if (load)
{
let script = document.createElement('script');
window.hasRunned = false;
script.text = 'alert(\'OtAutoBlock running.\'); window.hasRunned = true;';
document.head.insertBefore(script, document.head.children[0]);
}
</script>
<script>
alert('Another script. Has runned OtAutoBlock: ' + window.hasRunned);
</script>
<script>
alert('And yet another. Has runned OtAutoBlock: ' + window.hasRunned);
</script>

How to load javascript files using event listeners

This question is not duplicate of
Conditionally load JavaScript file
and nor this
How to include an external javascript file conditionally?
I have gone through them, they are kind of similar to what I want to do but not exactly same. Here is how, in the above question the user just wants to load the script file once based on a condition. But in my case I want to load different js files based on click events.
So here in my case I have an HTML document:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Experiment</title>
<link href="s.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="navigation">
<nav>
<ul>
<li id="home_btn"> Home</li>
<li id="about_btn"> About </li>
</ul>
</nav>
</div>
<canvas id="myCanvas">
</canvas>
<div class="notePane">
<p> This is just a bunch of text not explanation</p>
</div>
</body>
<script src="./exp.js" type="text/javascript"></script>
</html>
and this h.html file is linked to an exp.js file. Now in the exp.js file :
var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var head = document.getElementsByTagName("head")[0];
var js = document.createElement("script");
js.type="module";
h_btn.addEventListener("click", showHome );
a_btn.addEventListener("click", showAbout);
function showHome() {
js.src="./j1.js";
head.appendChild(js);
}
function showAbout() {
js.src="./j2.js";
head.appendChild(js);
}
So things work fine when I click the h_btn on the web page. It loads j1.js. But then when I click on the a_btn on the web page I expect to see j2.js linked but I don't see it. I have to refresh the page and then click on a_btn to see j2.js linked. How do I link j1.js and j2.js such that I don't have to refresh the page again and again to load the correct script.
Update: OP has updated the question requirements such that he wants to "unload" a JS file when another is clicked. There is no way to undo all the runtime logic once a JS file is loaded: the only way is to reload the page. Removing the <script> tag or changing the src attribute will not magically unbind event listeners or "undeclare" variables.
Therefore, if OP wants to "start anew", the only way is to check if a custom script has been loaded before: if it has, we force reload the page. There are of course many ways to "inform" the next page which source to load, if available: in the example below, we use query strings:
var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var head = document.getElementsByTagName("head")[0];
var appendedScriptKey;
var scripts = {
'home': './j1.js',
'about': './j2.js'
}
h_btn.addEventListener("click", showHome);
a_btn.addEventListener("click", showAbout);
// Check query string if a specific script is set
var params = (new URL(document.location)).searchParams;
var scriptKey = params.get('scriptKey');
if (scriptKey && scriptKey in scripts) {
appendScript(scriptKey);
}
function appendScript(key) {
if (hasAppendedScript) {
location.href = location.href + (location.search ? '?' : '&') + 'script=' + key;
location.reload();
}
var js = document.createElement("script");
js.type="module";
js.src = scripts[key];
head.appendChild(js);
appendedScript = key;
}
function showHome() {
appendedScriptKey('home');
}
function showAbout() {
appendScript('about');
}
This is because of how Node.appendChild() works. The first click works because you're creating a new element and inserting it into your document. However, the second click will not work as you've expected because the node already exists:
The Node.appendChild() method adds a node to the end of the list of children of a specified parent node. If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position
This means that the second click will only mutate the src attribute of the already-injected <script> element instead of creating a new one, and that also means that the second script src will not be loaded.
A solution will be to use a function that will create a script tag every single time:
var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var head = document.getElementsByTagName("head")[0];
h_btn.addEventListener("click", showHome);
a_btn.addEventListener("click", showAbout);
function insertScript(src) {
var js = document.createElement("script");
js.type = "module";
js.src = src;
head.appendChild(js);
}
function showHome() {
insertScript('./j1.js');
}
function showAbout() {
insertScript('./j2.js');
}
But this will also mean that multiple clicks on the same button will cause the script to be injected multiple times. This does not affect browser performance much since the browser has the loaded script cached somewhere, but to guard against this, it might be a good idea to implement some kind of unique identifier per script, and check against that before injection. There are many ways to do this, and this is just one way:
var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var head = document.getElementsByTagName("head")[0];
h_btn.addEventListener("click", showHome);
a_btn.addEventListener("click", showAbout);
// Store scripts that you've injected
var scripts = [];
function insertScript(src) {
// If we have previously attempted injection, then do nothing
if (scripts.indexOf(src) !== -1) {
return;
}
var js = document.createElement("script");
js.type = "module";
js.src = src;
head.appendChild(js);
// Push script to array
scripts.push(src);
}
function showHome() {
insertScript('./j1.js');
}
function showAbout() {
insertScript('./j2.js');
}
Alternative unique script injection strategies and ideas:
Use ES6 Map() to track unique script sources being injected
Perhaps only store src to array/dict/map when the script has successfully loaded
You have to create the element twice, as there can only be one element with 1 src.
var h_btn = document.getElementById("home_btn");
var a_btn = document.getElementById("about_btn");
var js1 = document.createElement("script");
var js2 = document.createElement("script");
h_btn.addEventListener("click", showHome);
a_btn.addEventListener("click", showAbout);
function showHome() {
js1.src = "j1.js";
document.body.appendChild(js1);
}
function showAbout() {
js2.src = "j2.js";
document.body.appendChild(js2);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Experiment</title>
<link href="s.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="navigation">
<nav>
<ul>
<li id="home_btn"> Home</li>
<li id="about_btn"> About </li>
</ul>
</nav>
</div>
<canvas id="myCanvas">
</canvas>
<div class="notePane">
<p> This is just a bunch of text not explanation</p>
</div>
</body>
<script src="exp.js" type="text/javascript"></script>
</html>

Function not working if src is a variable?

With this HTML the function myFunc() can be executed. https://myurl.de/myjs.js has the function myFunc in it.
<head>
<script type="text/javascript" src="https://myurl.de/myjs.js"></script>
<body>
<script type="text/javascript">
myFunc();
</script>
</body>
</head>
But with the second HTML I get an Error: Uncaught ReferenceError: myFunc is not defined.
https://myurl.de/settingsFile.js is a file that includes this url in a var: https://myurl.de/myjs.js so basically SettingsFile.UrlToMyJS is this https://myurl.de/myjs.js
<head>
<script src="https://myurl.de/settingsFile.js"></script>
<script type="text/javascript" id="myid"></script>
</head>
<body>
<script type="text/javascript">
document.getElementById('myid').src = SettingsFile.UrlToMyJS;
myFunc();
</script>
</body>
When I console.log(document.getElementById('myid')) this is the output:
<script type="text/javascript" id="myid" src="https://myurl.de/myjs.js></script> which is correct. It looks exactly like the script in the head of the first html (with the difference that it has the id="myid").
Yet it does not work. Why and how can I fix it?
settingsFile.js:
var defaultURL = 'https://myurl.de/';
var SettingsFile = {
UrlToMyJS : defaultURL + 'myjs.js',
}
The reason it's not working is that you can't add a src to a script element that's already in the DOM — or rather, doing so doesn't do anything. The script element has already been processed.
Instead, create it and then append it:
var script = document.createElement("script");
script.onload = function() {
myFunc();
};
script.src = SettingsFile.UrlToMyJS;
document.head.appendChild(script);
// If you need to support IE8, use the following instead of the previous line:
//document.getElementsByTagName("head")[0].appendChild(script);
That waits for the script to load, then calls myFunc (which should exist by then).
Also note that as I and Jeremy pointed out in the comments, body doesn't go in head, it goes after. It's also generally best to put script tags at the end of body (if you're not using async or defer attributes on them or type="module"). So in all, something like:
<head>
<!-- head stuff here -->
</head>
<body>
<!-- content here -->
<script src="https://myurl.de/settingsFile.js"></script>
<script type="text/javascript">
var script = document.createElement("script");
script.onload = function() {
myFunc();
};
script.src = SettingsFile.UrlToMyJS;
document.head.appendChild(script);
// If you need to support IE8, use the following instead of the previous line:
//document.getElementsByTagName("head")[0].appendChild(script);
</script>
</body>
Another option is to use document.write. This sort of thing may be the last at-least-partially appropriate use of document.write during the main parsing of the page:
<head>
<!-- head stuff here -->
</head>
<body>
<!-- content here -->
<script src="https://myurl.de/settingsFile.js"></script>
<script type="text/javascript">
document.write('<script src="' + SettingsFile.UrlToMyJS + '"><\/script>');
</script>
<script>
myFunc();
</script>
</body>
You can try creating a element and then appending it to your title
For Example (script code) :
var script = document.createElement("script");
script.src = "YOUR_SCRIPT_SRC_HERE";
document.getElementsByTagName('head')[0].appendChild(script);
Here I am creating a new tag in html and then appending it to the head of your html. Even as T.J. Crowder mentioned in the comment try removing your body from the head

How to append javascript variable to html script tag?

I have some javascripts where I need to have a variable hostname since I will run my scripts in dev, test, and prod. How do I get a variable hostname into the javascript script tag?
I can't build this script tag using javascript itself. The example below doesn't work. What does?
<!DOCTYPE html>
<html>
<body>
<script>
var hostname="sample.com";
</script>
<script src="https://" + hostname + "/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</body>
</html>
You can dynamically add your script to your page after building the full scr URL. Here is an example:
<!DOCTYPE html>
<html>
<body>
<script>
var hostname = "sample.com";
const script = document.createElement('script');
script.src = hostname + '/ajax/libs/jquery/3.4.1/jquery.min.js'
document.body.appendChild(script);
</script>
</body>
</html>
Otherwise:
You can dynamically add your script to your page after building the full scr URL. Here is an example:
<!DOCTYPE html>
<html>
<body>
<script src="%HOST_NAME%/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="%HOST_NAME%/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="%HOST_NAME%/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
var hostname = "sample.com";
var scripts = document.getElementsByTagName('script');
[ ...scripts ].forEach(script => {
script.src = script.src.replace('%HOST_NAME%', hostname);
})
</script>
</body>
</html>
You can give an id to the script tag and then do something like this
<script id="scriptID" src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
const hostName = "sample.com";
const scriptTag = document.getElementById("scriptID");
const newURL = scriptTag.src.replace('ajax.googleapis.com', hostName);
scriptTag.src = newURL;
<!DOCTYPE html>
<html>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
var hostname="sample.com";
//javascript
document.getElementById("link").href = hostname;
//jquery code
$("#link").attr("href",hostname);
</script>
</body>
</html>
Register your script using javascript itself. you can use like this
var hostName = 'http://sample.com/js/';
$(
'<script />',
{
type: 'text/javascript',
src: hostName + 'yourJSfile.js'
}
).appendTo('head');

actually creating a webpage with javascript

An extremely simple question but I am noob. I have been learning javascript and jquery for a while on jsfiddle, there everything works fine, building cool quizzes and all, but when I tried to actually create a directory, reference the jquery library and my javascript file, nothing works, even the below code, when saved as an HTML file doesn't work. I just paste it into notepad and save it as html, when I open it with it doesn'T work.
<html>
<head>
<title>webpage</title>
<script type="text/javascript">
window.onload = function() {
var myDiv = document.getElementById('#div');
myDiv.appendChild(document.createTextNode("Hi my name is Mehmetcan"));
}
</script>
</head>
<body>
<div id="myDiv"> </div>
</body>
</html>
Use this as starting point:
<html>
<head>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<title>webpage</title>
<script type="text/javascript">
$(document).ready(function () {
var myDiv = document.getElementById('myDiv');
myDiv.appendChild(document.createTextNode("Hi my name is Mehmetcan"));
});
</script>
</head>
<body>
<div id="myDiv"> </div>
</body>
Another approach that doesn't rely on JQuery but on pure, vanilla javascript.
<html>
<head>
<title>webpage</title>
<script type="text/javascript">
document.body.onload = loadSite;
function loadSite() {
var newDiv = document.createElement("span");
var newContent = document.createTextNode("Hi there and greetings!");
newDiv.appendChild(newContent);
myDiv.appendChild(newDiv);
}
</script>
</head>
<body>
<div id="myDiv"> </div>
</body>
</html>
You can find the full javascript sample as well as more information here, on the document.createElement MDN pages.
document.body.onload = addElement;
var my_div = null;
var newDiv = null;
function addElement () {
// create a new div element
// and give it some content
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hi there and greetings!");
newDiv.appendChild(newContent); //add the text node to the newly created div.
// add the newly created element and its content into the DOM
my_div = document.getElementById("org_div1");
document.body.insertBefore(newDiv, my_div);
}

Categories