Inject external javascript file out of time - javascript

I want to load an external javascript. This javascript contains source for an ad. I use a method of injection because the javascript needs the user IP. The javascript code to use is:
<html>
<head>
<script type="text/javascript">var userip;</script>
<script type="text/javascript" src="http://service.get.userip?var=userip"></script>
<script>
var url="http://example.com/abc?user_ip=" + userip;
var headID = document.getElementsByTagName("head")[0];
var newScript = document.createElement('script');
newScript.src = url;
headID.appendChild(newScript);
</script>
</head>
</html>
I use a service javascript to get the IP of user ("http://service.get.userip?var=userip" in this example code). Then I added the user IP to the url where I download the javascript.
The javascript is inserted when it revised the web charged with developer mode browser but it doesn't work.
I think the problem is that the JavaScrip injection is slow and this is no time to charge properly.
In my tests I got a proper operation to put a alert before the end of the last script:
<script>
...
headID.appendChild(newScript);
alert("Script is Load!");
</script>
For this reason I think the problem is load time. I also try loops, sleep() and setTimeout(), but I do not reproduce the smooth running that I get with the alert().

Related

Problem with WebExtensions: second injected script doesn't see the first one

I need to create Firefox WebExtension that should use Amazon AWS SDK for Javascript. So to use its functions I try to inject such scripts into the loaded page:
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.485.0.min.js"></script>
<script>My code using AWS functions</script>
When I just create such page it works fine:
<html>
<body>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.485.0.min.js">
<script">console.log(AWS)</script>
</body>
</html>
Then I try to inject this code to the loaded page with WebExtensions content script like:
var s1 = document.createElement('script');
s1.src = "https://sdk.amazonaws.com/js/aws-sdk-2.485.0.min.js";
document.body.appendChild(s1);
var s2 = document.createElement('script');
s2.textContent = "console.log(AWS);";
document.body.appendChild(s2);
The scripts are successfully injected but in this case the second script returns the error: "AWS is not defined".
How can I fix this?
Thanks :)

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.

Page loading blocks by remote javascript

I am loading external javascript on my site through:
<script language="javascript" type="text/javascript">
document.write("<script src='http://remoteserver/banner.php?id=10&type=image&num=3&width=200'><\/script>");
</script>
Everything was fine, but today was a big timeout from the remote server and page loading on site was blocking.
Is there possibility to load javascript asynchronous or to load it after page loads?
I don't want page load interruption while remote server not working.
I know there are tag async in HTML5, but it's not working, i think because this script is more complex than javascript (its ending with ".php").
Also i know it's better to put javascript files before /body tag to prevent this problem, but it's not the best solution.
Do you know other solutions?
Sync script blocks the parser...
Incorrect Method:
<script type="text/javascript" src="https://apis.google.com/js/plusone.js">
</script>
Async script will not block the rendering of your page:
Correct method:
<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";
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(po, s);
})();
</script>
Source: http://www.youtube.com/watch?feature=player_detailpage&v=Il4swGfTOSM&t=1607
You can easily do it by creating DOM nodes.
<script>
var scriptEl = document.createElement("script"); //Create a new "script" element.
scriptEl.src = "http://remoteserver/banner.php?id=10&type=image&num=3&width=200";
// ^ Set the source to whatever is needed.
document.body.appendChild(scriptEl);
// Append the script element at the bottom of the body.
</script>

How can I include a script with document.write?

We need to add a script to our web application. It basically adds an corporate menu.
So we've received a script to include in the body of our web application:
<!-- BEGIN NAVIGATION -->
<script type="text/javascript" src="https://intranet.local/?getCorporateJsMenu"></script>
<!-- END NAVIGATION -->
And the content of https://intranet.local/?getCorporateJsMenu basically looks like this:
document.write('<script type="text/javascript" src="..."></script>');
//....
document.write('<div>');
document.write('<ul>');
//...
document.write('<li>...</li>');
//...
document.write('</ul>');
document.write('</div>');
After having placed the <!--NAVIGATION--><script... directly into the HTML body, we were experiencing severe page load performance problems.
So our idea was to add the menu with JavaScript, when everything has already been loaded with something like this:
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://intranet.local/?getCorporateJsMenu';
var domparent = jQuery('#header').get();
domparent[0].appendChild(script);
With Firebug we see, that the script element has been added to the HTML content and the script has been loaded from the network, but somehow the document.write of the loaded script doesn't get executed. Why?
We cannot modify the contents of https://intranet.local/?getCorporateJsMenu since it comes from third party.
This happens because the script execution stops at the first found literal </script> tag, no matter if it was enclosed in the parenthesis. You need to obfuscate the ending script tag, for example:
document.write('<script type="text/javascript" src="..."><\/script>');
However, it seems you use also jQuery. Why not use the jQuery methods to load a script and add the content to a page rather than document.write()?
Here is an example of hijacking document.write() to bring 21st century loading performance to legacy scripts:
<script>
var writes = [];
document.write = [].push.bind(writes);
</script>
<div id=targ></div>
<script type="text/javascript" src="https://intranet.local/?getCorporateJsMenu"></script>
<script>
document.getElementById("targ").innerHTML = writes.join(" ");
</script>
I've used the patterns to support ads on a SPA site, as well as to cache embeds, and in one case to modify the content's style before injecting.
Try this:
document.write('<scr' + 'ipt type="text/javascript" src="..."></scr' + 'ipt>');

Browser event when all JS files loaded

My AJAX app is basically one index.html plus a bunch of .js modules. My setup function hooks up the js handler code to the appropriate DOM element. I suspect I need to use window.onload() rather than jquery's $(document).ready() since all the .js files need to be available (i.e. downloaded) at hookup time.
My understanding is that only the DOM tree is ready at $(document).ready(), but there's no guarantee that the .js files have been loaded. Is that correct?
PS. I don't need multiple onload handlers; a single window.onload() is fine for me.
You definitely have a misunderstanding in this case. The whole reason why it is considered best practice to include your script tags just before the close of the body tag is because script loads are blocking loads. Unless specifically coded otherwise (i.e.; google analytics), JavaScript files are loaded synchronously.
That said, if there are dependencies between script files, the order in which the files are loaded can be important.
No, you can safely use $(document).ready() as long as your script tags are loaded synchronously (in most cases, this means "normally"). The browser waits for a <script> to be loaded before continuing. Therefore, $(document).ready() includes all <script> tags in the source.
There are two exceptions to this, if the script tags contains an async or defer attribute. The prior meaning the compatible browsers can continue rendering the page, and the latter signifying that the script is executed when the page has finished.
I setup two files as a test:
syncscript.html
<html>
<head>
<title></title>
<style type="text/css">
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript">
$(window).load(function(){
$(document.body).append('<p>window.load has run.');
});
$(document).ready(function(){
$(document.body).append('<p>document.ready has run.');
});
</script>
</head>
<body>
<p>Page has loaded. Now continuing page load and attempting to load additional script file (after 10 second pause).</p>
<script type="text/javascript">
var p = document.createElement('p');
p.innerHTML = '<p>Inline script preceding jssleep.php file has run.</p>';
document.body.appendChild(p);
</script>
<script type="text/javascript" src="http://jfcoder.com/test/jssleep.php"></script>
<script type="text/javascript">
var p = document.createElement('p');
p.innerHTML = '<p>This is an inline script that runs after the jssleep.php script file has loaded/run.</p>';
document.body.appendChild(p);
</script>
</body>
</html>
jssleep.php
<?php
header("content-type: text/javascript");
sleep(10);
?>
var p = document.createElement('p');
p.innerHTML = '<p>jssleep.php script file has loaded and run following <?php sleep(10); ?>.</p>';
document.body.appendChild(p);
This outputs (in Firefox):
Page has begun loading. Now continuing page load and attempting to
load additional script file (after 10 second pause).
Inline script preceding jssleep.php file has run.
jssleep.php script file has loaded and run following <?php sleep(10);
?>.
This is an inline script that runs after the jssleep.php script file
has loaded/run.
$(document).ready() has run.
$(window).load() has run.
That is correct. However window.onload also requires CSS and images to be downloaded, so may be a bit overkill.
What you can do is this:
var scriptsToLoad = 0;
// for each script:
s = document.createElement('script');
s.type = "text/javascript";
s.src = "path/to/file.js";
scriptsToLoad += 1;
s.onload = function() {scriptsToLoad -= 1;};
// after all scripts are added in the code:
var timer = setInterval(function() {
if( scriptsToLoad == 0) {
clearInterval(timer);
// do stuff here
}
},25);

Categories

Resources