Dynamic change of script source not working - javascript

I have two files. An HTML file:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function update_layout()
{
var new_source = document.createElement('script')
new_source.src = 'script_code.js'
new_source.type = 'text/javascript'
document.getElementsByTagName('head')[0].appendChild(new_source)
}
function do_layout()
{
alert(Lay_Table['nam'])
}
</script>
</head>
<body>
<input type="button" value="Update" onclick="update_layout();do_layout();"/>
</body>
</html>
and an JavaScript file named "script_code.js":
Lay_Table = {}
Lay_Table['nam'] = 'US 104 Key'
When I click on the "Update" button I get an error instead of an alert containing the text "US 104 Key".
I have looked at very similar questions on Stack Overflow but cannot find out what is wrong. Have I made a mistake, or is this method no longer allowed due to security reasons? I am using Google Chrome.

The script takes a bit of time to get inserted into the document and run - it doesn't happen immediately, so the Lay_Table is not defined in time when do_layout runs. Consider adding a load listener to the inserted tag (and avoid inline handlers, they have way too many problems to be worth using nowadays, such as a demented scope chain and quote escaping issues):
window.addEventListener('DOMContentLoaded', () => {
document.querySelector('input').addEventListener('click', update_layout);
});
function update_layout()
{
var new_source = document.createElement('script')
new_source.src = 'script_code.js'
new_source.addEventListener('load', do_layout);
document.head.appendChild(new_source)
}

Related

How to execute a script on click

I'm trying to create a cookie only when a visitor clicks on the Accept button, but can't make the script execute on click. Here is my code:
function myFunction(){
var scripts = document.getElementsByTagName("script");
for (s of scripts) {
if (s.type == "text/plain") {
s.setAttribute('type', 'text/javascript');
eval(s);
}
}
}
<head>
# Some other scripts and title
<script type="text/plain">
document.cookie = "username=John Doe";
alert();
</script>
</head>
<body>
<button href="javascript:void(0);" onclick="myFunction();">
Accept
</button>
</body>
When I click on the button, I can see that the type of the script is changed from text/plain to text/javascript, but I didn't manage to execute the script after this, even with eval() (after inspecting the application's cookies, the new cookie is not created so I assume it was not executed).
I saw some other solutions on StackOverflow but they involved putting the content of the script I want to be executed in myFunction() and I don't want to do that (if possible).
Thanks for your help.
var scripts = document.getElementsByTagName("script");
for (s of scripts) {
if (s.type == "text/plain") {
//s.setAttribute('type', 'text/javascript');
eval(s);
}
}
eval(s) does not make sense here, s does not contain JavaScript code in text form, it is a reference to the DOM element.
eval(s.innerText) would eval the content of your script element.
(And you don’t need to modify its type then either, because the script element itself is not involved in the execution any more, it just provides the “data”.)

Registering an async javascript, declarative (static) vs dynamic

Is there any difference in declaring my async javascript statically vs dynamically?
static
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript" src="https://foo.bar/myscript.js" async>
</script>
...
</body>
</html>
dynamic
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript">
var myScript = document.createElement("script");
myScript.src = 'https://foo.bar/myscript.js';
myScript.async = !0;
myScript.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(myScript);
</script>
...
</body>
</html>
I noticed that declaring a script statically let a browser detect it earlier and preload (chrome + firefox).
My goal is to load a javascript in async way in order not to block HTML rendering and other scripts execution. Sametime, I want it to be executed as soon as it's downloaded, having in mind that it requires one element to be in the DOM already. Once downloaded the script is executed and it accesses the my-script-needs-me div. One limitation, I cannot change the script itself.
supports async parameters allowing to make this call asynchronous.
The second way you described allows you to have the url as a parameter and bind it.
It allows too the use of a callback to do some stuff when your script is loaded.
let scriptElement = document.createElement('script');
let url = `https://maps.googleapis.com/maps/api/js?key=${apiKey}`;//&libraries=geometry
scriptElement.src = url;
//Chargement de l'API Google
scriptElement.onload = () => {
//API chargée, on peut lancer l'initialisation du composant
this._initializeMap();
};
I used this to load Google Maps API, it's not directly in the HTML, so i can modify the URL when my page loads. And when the API is loaded, I an launch treatments that need this API.
you can use defer for that instead of async.
your script will execute right after html be parsed.
Static
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript" src="https://foo.bar/myscript.js" async>
</script>
...
</body>
</html>
As you know, HTML is parsed top-bottom. So, if it placed within body tag, then as soon as parsed, if it is an IIFE or the file myscript.js contains a function call, it will execute immediately.
So, inside, body, put it the script at the bottom will help you to execute it after the div has loaded.
But we can't ensure because of caching.
If the browser cache the script and if it is an IIFE or contains a function call, we can't predict the behaviour.
Dynamic
In dynamic also, it depends on the order.
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript">
var myScript = document.createElement("script");
myScript.src = 'https://foo.bar/myscript.js';
myScript.async = !0;
myScript.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(myScript);
</script>
...
</body>
</html>
In both cases, it will render after HTML contents.
The best way to ensure it loads only after all contents are loaded is
Giving an eventListener on Window.
Check the code below
<html>
<head>
...
</head>
<body>
...
<div id='my-script-needs-me'></div>
<script type="text/javascript">
function load(){
var myScript = document.createElement("script");
myScript.src = 'https://foo.bar/myscript.js';
myScript.async = !0;
myScript.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(myScript);
}
window.addEventListener("DOMContentLoaded",load);
</script>
...
</body>
</html>
Check this line window.addEventListener("DOMContentLoaded",load);.
The DOMContentLoaded is similar to jQuery's $(document).ready(). It will trigger the callback function when the HTML is properly loaded. So, you don't have to check for the existence of the HTML Element.
From what I've learned it's better to go with static way to declare an async script (in my particular scenario) than dynamic. Here some of why(s):
static async script declaration is detected by a browser and kicked off right away (at the very top of the page processing);
(deferred from #1) a browser puts the script request earlier in requests queue and if you have enough (30-40 requests per page load) it could be crucial to be in first 10 requests, not at the position 30-40;
adding a script dynamically to the head from the body doesn't introduce any performance advantage against the static declaration as long as whole head is already processed and it won't delay execution of the statically declared script;
at the moment when we reach the script declaration, static will work instantly because it's already pre-loaded and ready to be executed (in most cases, async is crucial here) while the dynamic script declaration will just kick off the request to download the script and only after then execute it;
I hope my thoughts will help someone as well.

Change the default browser behaviour of interpreting script tag

We all know what <script src="/something.js"></script> does. That file is loaded in the page and the script is run.
Is there any way to override the default behaviour of interpreting <script> elements?
I want the same syntax (<script src='...'></script>) that will only get the code from something.js (probably via XHR/jQuery ajax) and pass it to a foo (...) {...} function. Then I will care what I will do with it.
To clarify the problem:
I can easily create a pseudo <script> tag alternative using:
<div data-script-src="/1.js"></div>
<div data-script-src="/2.js"></div>
<div data-script-src="/3.js"></div>
<div data-script-src="/4.js"></div>
And then in the js side I would do:
var $scripts = $("[data-script-src]")
, scriptContents = [];
(function loadInOrder (i) {
if (!$scripts[i]) { alert("Loaded"); }
$.ajax($($scripts[i]).attr("data-script-src"), function (data) {
scriptContents[i] = data;
loadInOrder(++i);
});
})(0);
But how can I replace div[data-script] with <script>? How can I force the browser NOT to load the <script> tags that have the attribute data-load="false", for example?
I think your best bet here is to use onbeforescriptexecute. This event fires right before the script executes, so you can then modify the type attribute to something other than text/javascript (thus telling the browser not to execute the contents). This will still load the data from the server.
Unfortunately, onbeforescriptexecute is only supported in FF/Opera.
Example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script>
document.addEventListener("beforescriptexecute", function (e) {
console.log(e.target.innerHTML || e.target.src);
}, true);
</script>
<script>
console.log("12");
</script>
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
</head>
<body>
Hi
</body>
</html>
The console output will be:
'console.log("12")'
'http://code.jquery.com/jquery-1.10.2.min.js'
Create Your own PHP file, which will work as a kind of a gate. Then
<?php
$file = file_get_contents(your_url);
$file = str_replace('<script', '<myscript', $file);
$file = str_replace('</script>', '</myscript>', $file);
echo $file;
?>
And then if needed add Your type of real script that will search for myscript tags and run Javascript code...
You can use div element as well (if scripts are loaded in body, and You need validation):
substitute: '<script' -> '<div data-id="my-script" '
substitute: '<script' -> '</div>'
PS. I don't know if these are Your sites or sites from internet, that You want the default behaviour to be overwritten. So always be careful of what You are "file_getting_contents" because, this will be echoed directly to Your browser.

Chrome Extensions: Pass data from popup to another script file before the script is injected

Problem
Let's assume I go to a website called RENT.com. Let's also assume for this chrome extension there is a script A (JS) that is injected to RENT.com. Script A is a large script file that does a lot of interacting with RENT.com's HTML elements such as form fields. Before the script runs however, it needs some DOM ID's of a couple elements such as the email field because it modifies them.
Objective
I'd like to create a couple input fields (let's call them InputEmail and InputName) in popup.html to enter in the ID's of the elements on RENT.com. Obviously I'd be looking up the ID's manually by viewing the source, this is intentional.
A button in popup.html let's call it "GO BUTTON" will then read the value of InputEmail and InputName and send it to Script A. Script A now has everything it needs to function properly and is now injected into the page.
The appropriate interactions from Script A and RENT.com are now completed.
I've tried a few things, read a ton of information from Docs and Stack but I don't understand I think fundamentally how this can work. I want to pass data to Script A via popup.js before I execute content_script which ultimately is just injecting Script A. Seems like a chicken/egg problem and I'm not hungry for breakfast or lunch ;).
Thanks guys!
popup.html
<!doctype html>
<html>
<head>
<title>Getting Started Extension's Popup</title>
</head>
<body>
<ul>
<li><label>Email ID</label><input type="text" id="emailID"></input></li>
<li><label>Company ID</label><input type="text" id="nameID"></input></li>
</ul>
<input type="button" id="Modify" style="" value="GO BUTTON"></input>
<script src="popup.js"></script>
</body>
</html>
Popup.js
function click(e) {
//Ideally pass these values to Script A somehow
var email = document.getElementById("emailID").value;
var company = document.getElementById("nameID").value;
//then execute this or pass the ID's to content_script, inject into Script A, then inject into page
chrome.tabs.executeScript(null, {file:"contentscript.js"});
window.close();
}
document.addEventListener('DOMContentLoaded', function () {
var d = document.getElementById("Modify");
d.addEventListener('click',click);
});
ContentScript to inject Script A
var s2 = document.createElement('script');
s2.src =chrome.extension.getURL("ScriptA.js");
s2.async = false;
s2.onload = function() {
s2.parentNode.removeChild(s2);
};
(document.head||document.documentElement).appendChild(s2);
There are several ways to accomplish this. One would be:
function click(e) {
var elementIDs = {
email: document.getElementById("emailID").value,
company: document.getElementById("nameID").value
};
chrome.tabs.executeScript({
code: 'window.elementIDs='+JSON.stringify(elementIDs)
}, function() {
chrome.tabs.executeScript({file: "ScriptA.js"});
});
window.close();
}
This way, ScriptA will be able to access the values in window.elementIDs. This will work because content scripts from the same extension on the same page will share the execution environment, and the chaining of the calls to chrome.tabs.executeScript ensures that the script defining the global variable has run before ScriptA is run.

How to call external JavaScript function in HTML

I have a small chunk of code I can't seem to get working. I am building a website and using JavaScript for the first time. I have my JavaScript code in an external file 'Marq_Msg.js' which looks like this:
var Messages = new Array();
Messages[0] = "This is message 1";
Messages[1] = "This is message 2";
Messages[2] = "This is message 3";
Messages[3] = "This is message 4";
function scroll_messages()
{
for (var i = 0; i < Messages.length; i++)
document.write(Message[i]);
}
and in my HTML file 'Index.html' I am trying to call it like this:
<div id="logo">
<marquee scrollamount="5" direction="left" loop="true" height="100%" width="100%">
<strong><font color="white"><script src="Marq_Msg.js">scroll_messages()</script></font></strong>
</marquee>
</div>
The 'logo' div is a CSS piece that I'm trying to marquee inside of. If I put the code embedded inside the 'head' tag and call it, it works perfectly! There are a few other things id like to do with this code (like space the messages out a little) but I can't get the code to work in the first place. I've also tried adding:
<script src="Marq_Msg.js"></script>
in the 'head' tag with a separate call, that was a no go. I also tried instead using:
<script type="text/javascript" src="Marq_Msg.js">scroll_messages()</script>
Hell, i even had the function try returning a string (even hardcoded a simple "hello" to be returned) but that didnt work either with and without the 'type':
//Marq_Msg.js
function scroll_messages()
{
return "hello";
}
//index.html
<script type="text/javascript" src="Marq_Msg.js">document.write(scroll_messages())</script>
What am I missing? Any help would be greatly appreciated!! I've looked all over Google, and every site I find wants to do it using some 'form'. I just want messages to be displayed across, no form attached.
If a <script> has a src then the text content of the element will be not be executed as JS (although it will appear in the DOM).
You need to use multiple script elements.
a <script> to load the external script
a <script> to hold your inline code (with the call to the function in the external script)
scroll_messages();
In Layman terms, you need to include external js file in your HTML file & thereafter you could directly call your JS method written in an external js file from HTML page.
Follow the code snippet for insight:-
caller.html
<script type="text/javascript" src="external.js"></script>
<input type="button" onclick="letMeCallYou()" value="run external javascript">
external.js
function letMeCallYou()
{
alert("Bazinga!!! you called letMeCallYou")
}
Result :
If anyone still has the reference error is probably because you are loading your Javascript with defer, or it's in the bottom of the body so when the function gets called your function still doesn't exist.

Categories

Resources