I'm wiriting on a Chrome Extension. I need too check if a URL is online or not. The URL returns me a variable, so if the var is true, the URL is online.
In case the URL is offline it takes about 2 seconds for the error, so the Extension popup needs 2 seconds to start EVERYtime.
This is my "old" version:
popup.html:
<script language="javascript" src="http://example.org/jdcheck.js"></script>
<script language="javascript" src="popup.js"></script>
popup.js:
if (variable) { [...] }
Well, that worked - after 2 seconds.
Now I had an idea, so I removed the scriptlink in the popup.html.
And that's my new popup.js:
background.$(document).ready(function() {
var jq = document.createElement('script'); jq.type = 'text/javascript';
jq.src = 'http://127.0.0.1:9666/jdcheck.js';
document.getElementsByTagName('head')[0].appendChild(jq);
if(jdownloader){
[...action]
}
});
You see, I use jQuery to load the Checkfile.
Now, it throws me an error:
Uncaught ReferenceError: jdownloader is not defined
Well, it looks like the createElement did not work.
I'm 100% sure that the URL gives me the variable I want.
Could you please help me? I don't know how to solve this..
Thank you!
Markus
edit: I removed the jQuery part, added the keepGoing and the jq.onload:
function keepGoing() {
console.log("JS should have been loaded");
if(jdownloader){
[action]
}
}
var jq = document.createElement('script');
jq.onload = keepGoing();
jq.src = 'http://127.0.0.1:9666/jdcheck.js';
document.getElementsByTagName('head')[0].appendChild(jq);
NOW, the console gives me this:
JS should have been loaded popup.js:98
Uncaught ReferenceError: jdownloader is not defined popup.js:100
So it seems like the jdownloader var is not passed to the popup.js.
Why..why?! I don't know.
Markus
When you append a script tag to the DOM, the code does not wait for the browser to download and evaluate the script before continuing.
So you have to check back. In Chrome, you can use the load event on the script element.
background.$(document).ready(function() {
var jq = document.createElement('script'); jq.type = 'text/javascript';
jq.onload = keepGoing; // Or an anonymous inline function if you prefer
jq.src = 'http://127.0.0.1:9666/jdcheck.js';
document.getElementsByTagName('head')[0].appendChild(jq);
function keepGoing() {
if(jdownloader)...
}
});
("On Chrome" because in older versions of IE, script didn't fire the load event, it fired readystatechange.)
Side note: You don't have to supply the type attribute if it's text/javascript. That is, and always has been, the default.
The solution may be quite simple, at least for the edited part of your question.
See my jsfiddle for reference: http://jsfiddle.net/6kkC4/1/
jq.onload = keepGoing();
vs.
jq.onload = keepGoing;
Calling "onLoad()" will evaluate the function immediately (so not onload).
With jq.onload = keepGoing; only a reference to the function is passed
and the function is evaluated onload.
Since the "creating a script node" method for doing a JavaScript file Include is asynchronous, we should be using the await and async keywords.
Here is a complete example of how to Include a function called "Context" and then to call it, using Promises (await and async):
CallContext(); // Call the async function to do the call
async function CallContext()
{
await Include("context.js");
alert(Context()); // Call Context when Include is done
} // CallContext
// Wrap the callback of the script element creation in a Promise
async function Include(jsFilePath)
{
return new Promise((resolve,reject) =>
{
var js = d.createElement("script");
js.onload = resolve;
js.src = jsFilePath;
d.body.appendChild(js);
});
} // Include
Note that, as yet, there is no modern way to hide the explicit "new Promise" code.
Related
first i want to check if jquery exists or not then move on to next function. But next function abc() is not being executed. Maybe somebody knows why!
window.onload = function () {
if (window.jQuery) {
localStorage.setItem("key", "abc");
} else {
script = document.createElement("script");
script.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js";
document.head.appendChild(script);
}
};
function abc() {
$(document).keypress(function (e) {
alert(e.key);
});
}
There are a few issues with that code. It sounds like you want to call abc when jQuery has been loaded, and for some reason you're allowing for the possibility it may or may not have been loaded prior to this code running. The issues I see are:
Setting a value in localStorage won't cause the function to be run; in fact, nothing in that code will cause abc to be run.
You're not declaring your script variable, which means the code is falling prey to what I call The Horror of Implicit Globals.
The load event on window happens really, really late in the page load process, after all images and other ancillary items are loaded. It's generally best to do your initialization sooner than that, by either using type="module" on your script element (if you're happy to use modules, which are handy) or by setting the defer attribute, or by putting the script at the very end of the body, just before the closing </body> tag (which is compatible with older browsers).
So here's what I might do instead, if I had to allow for this jQuery loaded/not loaded thing and I wasn't able to use modules (see comments):
// Use a wrapper if not using modules, to avoid creating unnecessary globals
(function() {
// Define your initialization function
function init() {
$(document).keypress(function (e) {
alert(e.key);
});
}
// If jQuery is loaded...
if (window.jQuery) {
// ...init
init();
} else {
// Otherwise, load it and init when it's loaded
var script = document.createElement("script");
// ^^^−−−−−−−−− prevents the horror of implicit globals
script.onload = init; // <=== Calls `init` when the script successfully loads
script.src = "https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js";
document.head.appendChild(script);
}
})();
Let's say I've got my-file.js or some CDN file in different server that has
for (var i = 0; i < 1000; i ++) {
//something really long and hard to execute
}
//after this long execution
window.myObj = {
//initialization of some global object that I need
}
(I cannot change my-file.js...)
I want to add my-file.js asynchronously to page, and then, after it is loaded and EXECUTED I want to call some event like:
//when my my-file.js is loaded I finally use window.myObj
window.myObj.somefunc(); //yaaay
Is it possible? (cross browser for IE9+ is important for me, but any not critical)
Note:
In case file I need is on CDN or somewhere on different server - I need to remember about cross-domain policy, so I think loading content of file in ajax is not possible in such case.
Note 2:
http://www.chromestatus.com/features/5711871906676736 there is exacly what I need, but I bet it'll be few years before you can easly use it globally.
var script = document.createElement('script');
script.onload = function(){
// loaded
}
document.head.appendChild(script);
script.src = "path/to/script";
That's about the simplest example. And yes, the entire thing is async, hence it needed the onload handler.
You could also wrap it up in a function:
function getScript(src,callback){
var script = document.createElement('script');
script.onload = callback;
document.head.appendChild(script);
script.src = src;
}
getScript('path/to/js',function(){
//loaded
});
However, thinking out of the box, it sounds like you need dependency management. Use RequireJS. Should fit your needs.
jQuery getScript has a callback you can use to execute events after a file is loaded, or you can use script.onload as in Joseph's example:
http://api.jquery.com/jquery.getscript/
If you want to wait until a certain property is available (ie after some work has finished), you can create an interval and then clear it as soon as the property is found in window, ie:
var waitForObj = setInterval(function(){
if(window.myObj && window.myObj.somefunc){
clearInterval(waitForObj);
//Do some stuff you need this object for
}
}, 100);
I'm trying to do the following:
- from an HTML page I want to call a .js file in the header. I want this file to contain 2 functions, A and B, that I want to call at different times as the page loads.
This first part I have ready, my problem is:
-Function B is supposed to call another .js file, wait for it to complete loading and run some code specific to function B.
This is what I have so far:
<head><script type="text/javascript" src="first.js"></script></head>
I have this to call the different functions that are inside first.js
<script language="JavaScript" type="text/javascript">
functionA ();
</script>
Now inside first.js:
function functionA ()
{
alert("A runs!");
}
function functionB ()
{
alert("B runs!");
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "http://www.URL.com" ;
--some additional code--
}
Suggestions? Thanks!
First off, you don't "call a javascript file". You load the javascript file which causes it to get parsed and any top level code is executed. Once loaded, you can then call any functions in it.
So, to load a javascript file from functionB and then call a function in it, you can finish the dynamically loaded code you started with, but then you need to add a notification for when it is loaded:
function functionB () {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "http://www.URL.com" ;
script.onload = function() {
// call the function in the newly loaded scrip here
};
document.getElementsByTagName("head")[0].appendChild(script);
}
Here's a post with a loadScript function that takes a callback when the script is loaded.
Here's a post with a bit more capable function that can actually load scripts sequentially and also contains support code for older versions of IE (if that is required).
Making sure an external js file has been loaded across browsers requires more than a simple onload event; If you want a well-tested solution with added features, your best bet is using a library such as require.js.
Your code, after you include the require.js file somewhere, will look something like:
function functionA () {
alert("A runs!");
}
function functionB () {
alert("B runs!");
require(['yourotherfile'], function(myfile) { // no js extension, read more in require's docs
// --some additional code--
});
}
When using an importjs() type of function (see below for an example), jQuery doesn't seem to be loading before the code following it.
Here's a sample html file:
<html>
<head></head>
<body>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
function importjs(jsFile) {
var body = document.getElementsByTagName('head').item(0);
var scpt = document.createElement('script');
scpt.src = jsFile;
scpt.type = 'text/javascript';
body.appendChild(scpt);
}
var f1="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js";
//importjs(f1);
var $j=jQuery;
alert("hello stackoverflow!");
</script>
</body>
</html>
With the above code, the alert should successfully fire.
Next, comment out the first script block, i.e. the one explicitly loading jQuery, and uncomment the importjs(f1) line in the second script block. This time, the alert does not fire, at least in firefox and safari.
Now, put in an extra alert before the line "var $j=jQuery". For me, it works in both browsers, regardless of how long or short I wait. A setTimeout would probably also do the trick, but it's also not an ideal way to program something like this.
If javascript is single-threaded, why does the importjs fail? Is it because the new element created by importjs doesn't get 'executed' until the first block finishes, or should the new element be executed as soon as it is created?
There are several problems here:
you have jQuery duplicated, one in the html, one in the js
dynamically added javascript won't be available immediately
if you load scripts this way the dependant code should be in a callback function
function importjs(jsFile, callback) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.src = jsFile;
script.type = 'text/javascript';
script.onload = script.onreadystatechange = function() {
// execute callback
if (callback) callback();
// prevent memory leak in IE
script.onload = null;
head.removeChild(script);
};
head.appendChild(script);
}
then you should use it as:
importjs("jquery.js", function(){
// all jQuery dependant code
// goes here...
});
UPDATE
There is a more robust solution for including javascript files which allows you to:
include multiple files that are related
ensure they are executed in order
load them in a non-blocking way (parallel with other resources)
I'm still working on this script, but pretty much works right now. Be sure to check it out.
It combines the advantages of different techniques to give a huge benefit on page load time. Here is a related article: Loading Scripts Without Blocking
The syntax is:
include(['jquery.js','jquery-ui.js'], myjQueryCode); // executed in order
I'm downloading JQuery asynchronously:
function addScript(url) {
var script = document.createElement('script');
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
}
addScript('jquery.js');
// non-jquery code ...
// jquery specific code like: $()...
As such - how do I call my JQuery specific code once JQuery is loaded (because since I'm downloading my JavaScript asynch - it's not blocking, which is good, but is trying to execute my JQuery specific code before JQuery has been loaded).
You can host a copy of the jquery file yourself. Then you can add a call to the callback function at the bottom of jquery.js:
/* jquery code goes here ... */
my_onload_callback();
For me this works (tested in FireFox 33.0.3):
if(typeof(jQuery) == "undefined"){
//create onload-callback function
window["__9384nalksdfalkj04320"] = function(){
console.log("jQuery=" + jQuery);
};
//load jQuery asynchronously
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("onload", "__9384nalksdfalkj04320();"); //register onload-callback listener function
script.setAttribute("src", "http://code.jquery.com/jquery-latest.min.js");
document.head.appendChild(script);
}
You can inline LabJs into your page (potentially, every page). On the downside, you're inlining a script over and over. On the upside, LabJs is pretty small - 4k minified - and it lets you handle complex asynchrony load patterns cross-browser with very simple code like:
<script>
// Minified LabJs goes here
</script>
<script>
function init() {
// Your code after jquery loads goes here
}
$LAB
.script('//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js')
.wait(init);
</script>
</body>
I'm not much on standard Javascript, but you may try doing something like this:
var script_object = new addScript('jquery.js');
script_object.onLoad('addScript("my_jquery_related.js")');
Admittedly, that's a mega shot in the dark.
If that doesn't work, maybe pass through your function as a callback variable in your JS loader:
addScript(url, function(){ function_to_call();})
function addScript(url, call_back_function) {
var script = document.createElement('script');
script.src = url;
document.getElementsByTagName('head')[0].appendChild(script);
call_back_function.call;
}
addScript('jquery.js');
That's all I got :\