I'm working on a tiny bookmarklet script (one big IIFE ). The script was previously created for pages that contain jQuery, and uses jQuery extensively.
The problem is we are now beginning to move away from jQuery in some of our products, but still need this bookmarklet to function the same way.
So, I want to update the bookmarklet so that it will first check if jQuery exists on the page.
if jQuery, continue execution as normal.
If !jQuery, download jQuery (AJAX), THEN execute the rest of the script.
The problem is I'm not sure how to do this inside of an IIFE. Since fetching the script would be an async action, how do I make sure jQuery is downloaded and good to go before continuing the script? This is what I have so far..
(function () {
var continueExec = true;
if (!window.jQuery) {
continueExec = false;
var s = document.createElement('script');
s.onload = function () {
continueExec = true;
};
s.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js';
document.head.appendChild(s);
}
// .. now I want to be able to use jQuery here. What do I need to change?
// $('.className')...
})()
note that since this is a bookmarket, it gets injected into the page after load/render, so it's not as simple as placing a jQuery script block higher in the head.
In javascript you need a callback to handle async operations.
In your example we can execute such a callback for two scenarios:
a) jQuery is already defined
b) jQuery was downloaded and is ready to use
So we change your code a little bit and pass a callback to the IIFE which contains the main application and call it for both scenarios.
(function (callback) {
if (!window.jQuery) {
var s = document.createElement('script');
s.onload = function() {
// Start the main application once jQuery was load:
callback(window.jQuery);
};
s.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js';
document.head.appendChild(s);
} else {
// Start the main application directly as jQuery is already part of the page:
callback(window.jQuery);
}
})(function($) {
// The main application
//
// here you can use
// $('.className')...
});
Related
This question already has answers here:
How to make JavaScript execute after page load?
(25 answers)
Closed 1 year ago.
I am using following code to execute some statements after page load.
<script type="text/javascript">
window.onload = function () {
newInvite();
document.ag.src="b.jpg";
}
</script>
But this code does not work properly. The function is called even if some images or elements are loading. What I want is to call the function the the page is loaded completely.
this may work for you :
document.addEventListener('DOMContentLoaded', function() {
// your code here
}, false);
or
if your comfort with jquery,
$(document).ready(function(){
// your code
});
$(document).ready() fires on DOMContentLoaded, but this event is not being fired consistently among browsers. This is why jQuery will most probably implement some heavy workarounds to support all the browsers. And this will make it very difficult to "exactly" simulate the behavior using plain Javascript (but not impossible of course).
as Jeffrey Sweeney and J Torres suggested, i think its better to have a setTimeout function, before firing the function like below :
setTimeout(function(){
//your code here
}, 3000);
JavaScript
document.addEventListener('readystatechange', event => {
// When HTML/DOM elements are ready:
if (event.target.readyState === "interactive") { //does same as: ..addEventListener("DOMContentLoaded"..
alert("hi 1");
}
// When window loaded ( external resources are loaded too- `css`,`src`, etc...)
if (event.target.readyState === "complete") {
alert("hi 2");
}
});
same for jQuery:
$(document).ready(function() { //same as: $(function() {
alert("hi 1");
});
$(window).load(function() {
alert("hi 2");
});
NOTE: - Don't use the below markup ( because it overwrites other same-kind declarations ) :
document.onreadystatechange = ...
I'm little bit confuse that what you means by page load completed, "DOM Load" or "Content Load" as well? In a html page load can fire event after two type event.
DOM load: Which ensure the entire DOM tree loaded start to end. But not ensure load the reference content. Suppose you added images by the img tags, so this event ensure that all the img loaded but no the images properly loaded or not. To get this event you should write following way:
document.addEventListener('DOMContentLoaded', function() {
// your code here
}, false);
Or using jQuery:
$(document).ready(function(){
// your code
});
After DOM and Content Load: Which indicate the the DOM and Content load as well. It will ensure not only img tag it will ensure also all images or other relative content loaded. To get this event you should write following way:
window.addEventListener('load', function() {...})
Or using jQuery:
$(window).on('load', function() {
console.log('All assets are loaded')
})
If you can use jQuery, look at load. You could then set your function to run after your element finishes loading.
For example, consider a page with a simple image:
<img src="book.png" alt="Book" id="book" />
The event handler can be bound to the image:
$('#book').load(function() {
// Handler for .load() called.
});
If you need all elements on the current window to load, you can use
$(window).load(function () {
// run code
});
If you cannot use jQuery, the plain Javascript code is essentially the same amount of (if not less) code:
window.onload = function() {
// run code
};
If you wanna call a js function in your html page use onload event. The onload event occurs when the user agent finishes loading a window or all frames within a FRAMESET. This attribute may be used with BODY and FRAMESET elements.
<body onload="callFunction();">
....
</body>
You're best bet as far as I know is to use
window.addEventListener('load', function() {
console.log('All assets loaded')
});
The #1 answer of using the DOMContentLoaded event is a step backwards since the DOM will load before all assets load.
Other answers recommend setTimeout which I would strongly oppose since it is completely subjective to the client's device performance and network connection speed. If someone is on a slow network and/or has a slow cpu, a page could take several to dozens of seconds to load, thus you could not predict how much time setTimeout will need.
As for readystatechange, it fires whenever readyState changes which according to MDN will still be before the load event.
Complete
The state indicates that the load event is about to fire.
This way you can handle the both cases - if the page is already loaded or not:
document.onreadystatechange = function(){
if (document.readyState === "complete") {
myFunction();
}
else {
window.onload = function () {
myFunction();
};
};
}
you can try like this without using jquery
window.addEventListener("load", afterLoaded,false);
function afterLoaded(){
alert("after load")
}
Alternatively you can try below.
$(window).bind("load", function() {
// code here });
This works in all the case. This will trigger only when the entire page is loaded.
window.onload = () => {
// run in onload
setTimeout(() => {
// onload finished.
// and execute some code here like stat performance.
}, 10)
}
If you're already using jQuery, you could try this:
$(window).bind("load", function() {
// code here
});
I can tell you that the best answer I found is to put a "driver" script just after the </body> command. It is the easiest and, probably, more universal than some of the solutions, above.
The plan: On my page is a table. I write the page with the table out to the browser, then sort it with JS. The user can resort it by clicking column headers.
After the table is ended a </tbody> command, and the body is ended, I use the following line to invoke the sorting JS to sort the table by column 3. I got the sorting script off of the web so it is not reproduced here. For at least the next year, you can see this in operation, including the JS, at static29.ILikeTheInternet.com. Click "here" at the bottom of the page. That will bring up another page with the table and scripts. You can see it put up the data then quickly sort it. I need to speed it up a little but the basics are there now.
</tbody></body><script type='text/javascript'>sortNum(3);</script></html>
MakerMikey
I tend to use the following pattern to check for the document to complete loading. The function returns a Promise (if you need to support IE, include the polyfill) that resolves once the document completes loading. It uses setInterval underneath because a similar implementation with setTimeout could result in a very deep stack.
function getDocReadyPromise()
{
function promiseDocReady(resolve)
{
function checkDocReady()
{
if (document.readyState === "complete")
{
clearInterval(intervalDocReady);
resolve();
}
}
var intervalDocReady = setInterval(checkDocReady, 10);
}
return new Promise(promiseDocReady);
}
Of course, if you don't have to support IE:
const getDocReadyPromise = () =>
{
const promiseDocReady = (resolve) =>
{
const checkDocReady = () =>
((document.readyState === "complete") && (clearInterval(intervalDocReady) || resolve()));
let intervalDocReady = setInterval(checkDocReady, 10);
}
return new Promise(promiseDocReady);
}
With that function, you can do the following:
getDocReadyPromise().then(whatIveBeenWaitingToDo);
call a function after complete page load set time out
setTimeout(function() {
var val = $('.GridStyle tr:nth-child(2) td:nth-child(4)').text();
for(var i, j = 0; i = ddl2.options[j]; j++) {
if(i.text == val) {
ddl2.selectedIndex = i.index;
break;
}
}
}, 1000);
Try this jQuery:
$(function() {
// Handler for .ready() called.
});
Put your script after the completion of body tag...it works...
In the JS library I am writing, I have this loadScript func:
function loadScript(src, callback) {
var script = document.createElement('script');
script.src = src;
// script.type = "text/javascript";
// script.async = false;
if (typeof callback !== 'undefined') {
script.onload = function () {
callback();
};
}
document.head.appendChild(script);
}
Inside the main js file, I use it to load the dependencies dynamically after the main js file is loaded, initiate the JSLib object in its callback.
loadScript(baseUrl + '/dependencies/you-load-me-long-time.js', function() {
window.JSLib = true;
}
Then I have a web page that calls this library.
var jsLib = new JSLib({...});
The problem that I encounter is that - in the web page that loads this JS library, the browser complains that the JSLib is not defined because the dependency file you-load-me-long-time.js has not finished loading yet when the script in the web page is executed.
A work-around that seems to be working for now is that, in the web page, I wrap the initiation code in a $(window).load(function() {}); call.
Is there any way that I can overcome this timing issue? ex: "blocking" the loading of the rest of the web page until JSLib is loaded (doesn't sound like a good idea anyway), etc...
There are only two ways to create blocking dynamic script loaded via JS and both are fairly undesirable.
If the document is still being parsed, you can use document.write() to insert a <script> tag at the current document location. That will then be parsed and loaded synchronously.
If the script resource is on the same origin as the document, you can fetch the script with a synchronous Ajax call and then eval the script.
Since neither of these is particularly desirable, the usual work-around is to surface a callback all the way back to the caller of the script so that they can participate in when the async operation is done and put their code in that async callback.
var jsLib = new JSLib({...}, function() {
// put code here that uses the jsLib because now it is loaded
});
For this messy reason, it is usually not a good practice to make the completion of a constructor be an async operation. It significantly complicates the use of the object.
More common would be to let the constructor just create the shell of the object and then require a .load(fn) method call to actually load it. This will likely lessen the chance of callers misuing the library.
var jsLib = new JSLib({....});
jsLib.load(function(err) {
if (err) {
// error loading the library
} else {
// library is loaded now and all functionality can be used
}
});
FYI, your idea to use $(window).load() is not a good idea. That method may accidentally work just because it delays the timing enough until your script happens to be loaded, but the window load event does not specifically wait until dynamically loaded scripts have been loaded so it is not a reliable way to wait for your script.
I'm building a dynamic website that loads all pages inside a "body" div via jquery's load(). The problem is I have a script looped with setInterval inside the loaded PHP page, the reason being I want the script loaded only when that page is displayed. Now I discovered that the scripts keep running even after "leaving" the page (loading something else inside the div without refresh) and if I keep leaving / returning the loops stack up flooding my server with GET requests (from the javascript).
What's a good way to unload all JS once you leave the page? I could do a simple dummy var to not load scripts twice, but I would like to stop the loop after leaving the page because it's causing useless traffic and spouting console errors as elements it's supposed to fill are no longer there.
Sorry if this has already been asked, but it's pretty hard to come up with keywords for this.
1) why don't you try with clearInterval?
2) if you have a general (main) function a( ) { ... } doing something you can just override it with function a() { }; doing nothing
3) if you null the references to something it will be garbage collected
no code provided, so no more I can do to help you
This really sounds like you need to reevaluate your design. Either you need to drop ajax, or you need to not have collisions in you method names.
You can review this link: http://www.javascriptkit.com/javatutors/loadjavascriptcss2.shtml
Which gives information on how to remove the javascript from the DOM. However, modern browsers will leave the code in memory on the browser.
Since you are not dealing with real page loads/unloads I would build a system that simulates an unload event.
var myUnload = (function () {
var queue = [],
myUnload = function () {
queue.forEach(function (unloadFunc) {
undloadFunc();
});
queue = [];
};
myUnload.add = function (unloadFunc) {
queue.push(unloadFunc);
};
return myUnload;
}());
The code that loads the new pages should just run myUnload() before it loads the new page in.
function loadPage(url) {
myUnload();
$('#page').load(url);
}
Any code that is loaded by a page can call myUnload.add() to register a cleanup function that should be run when a new page is loaded.
// some .js file that is loaded by a page
(function () {
var doSomething = function () {
// do something here
},
timer = setInterval(doSomething, 1000);
// register our cleanup callback with unload event system
myUnload.add(function () {
// since all of this code is isolated in an IIFE,
// clearing the timer will remove the last reference to
// doSomething and it will automatically be GCed
// This callback, the timer var and the enclosing IIFE
// will be GCed too when myUnload sets queue back to an empty array.
clearInterval(timer);
});
}());
I'm writing a greasemonkey script and want to call the start function only once after the page loads - it's for facebook. First I started with following code:
function start(){
alert("hello");
}
start();
The start() function was executed more then once. So I changed the code to following:
jQuery.noConflict();
window.stoop = 0;
jQuery(document).ready(function(){
if(window.stoop == 0){
start();
}
window.stoop = 55;
//or window.stoop++;
});
function start(){
alert("hello");
}
The problem is that the window.stoop value won't change.
I tried with
var stoop = 0;
stoop++;
and
var obj = {};
obj.stoop = 0;
obj.stoop++;
too, but these ways didn't work neither.
What am I doing wrong? I'm in Europe right now -it's night, so I will answer your questions later.
The issue is that your whole Greasemonkey script is executing more than once. Greasemonkey will run on iframes, just as though they were the main page -- if the iframe matches the #include, #exclude, and #match directives of your script.
There is no point in trying to track state like that, the function will only run once per script execution, unless you deliberately call it more than once (Which is not shown in the question). And scripts can't normally share information between execution instances (nor is that needed here).
Also, there is no need to use jQuery(document).ready() because, unless you are injecting the script into the target page, Greasemonkey fires at document.ready by default.
To solve the multiple run issue:
Tune your #include, #exclude, and #match directives to eliminate as many undesired iframes as you reasonably can.
Add code like this near the top of your script:
if (window.top != window.self) //-- Don't run on frames or iframes.
return;
I understand that JS is single threaded and synchronously executed. Therefore when i add a file to my browser head tag that file is executed as soon as its encountered. Then it goes to the next script tag & executes that file. My question is when I add a js file dynamically to an HTML head tag. How does the browser executes that file?
Is it like that the file is executed as soon as the file is loaded wherever the current execution is. Or is it that we can control how that file is executed?
When the script is loaded, it will be executed as soon as possible. That is, if some other javascript function is executing, like a clickhandler or whatever, that will be allowed to finish first - but this is a given because, as you say, in browsers JavaScript normally execute in a single thread.
You can't control that part of the script loading, but you could use this pattern - heavily inspired by JSONP:
inserted script:
(function () {
var module = {
init: function () {
/* ... */
}
}
ready(module); // hook into "parent script"
}());
script on main page:
function ready(o) {
// call init in loaded whenever you are ready for it...
setTimeout(function () { o.init(); }, 1000);
}
The key here is the ready function that is defined on your page, and called from the script you insert dynmaically. Instead of immediately starting to act, the script will only tell the parent page that it is loaded, and the parent page can then call back to the inserted scripts init function whenever it wants execution to start.
What happens when a JavaScript file is dynamically loaded ( very simplified, no checks ):
the file is loaded;
if there is function call e.g. doSomething() or (function(){...})(), the code is executed(of course you must have the definitions);
if there are only function definitions, nothing is happening until the function call.
See this example: 3 files are loaded, 2 are executed immediately, 1 is waiting the timeout.
Edit:
The script tag can be placed anywhere in the page. Actually it is better to be placed at the end of the page if the onload event is not used (yahoo speed tips).
With HTML5 JavaScript has web workers MDN MSDN wikipedia.
Considering a way to do this is
var js=document.createElement('script')
js.setAttribute("type","text/javascript")
js.setAttribute("src", filename)
document.getElementsByTagName("head")[0].appendChild(js);
// ^ However this technique has been pointed to be not so trusworthy (Read the link in the comment by Pomeh)
But answering your question
How does the browser executes that file?
As soon as the script is added to the DOM
Is it like that the file is executed as soon as the file is loaded wherever the current execution is?
Yes
Or is it that we can control how that file is executed?
Its better if you attach an onload event handler, rather than a nasty tricks.
Here is some code you can try to get an answer to your question.
<script>
var s = document.createElement('script'), f = 1;
s.src="http://code.jquery.com/jquery-1.7.2.js";
document.head.appendChild(s)
s.onload = function(){
console.log(2);
f = 0
}
while(f){
console.log(1);
}
</script>
This code should ideally print a 2 when the script loads, but if you notice, that never happens
Note: This WILL kill you browser!