Best way of unobtrusive onload in plain JavaScript - javascript

What is the best unobtrusive way of invoking something after the page is being loaded in plain JavaScript? Of course in jQuery I would use:
$(document).ready(function(){...});
but I am not sure about the most reliable approach in plain js.
Clearly
window.onload = ...
is not proper solution, because it would overwrite previous declaration.
What I am trying to do is to insert an iframe into a div after the page is loaded, but maybe there are actually better ways of doing it. My plan is to do something like:
window.onload = function(divId){
var div = document.getElementById(divId);
div.innerHTML = "<iframe src='someUrl' .. >";
}
EDIT:
Apologies for not including all necessary details.
The script is not for my website - the idea is to show a part of my site (a form) on external web sites. The priority is to minimize the effort someone has to put to use my code. That is why I would like to keep everything in js file and absolutely nothing in <script> - except of <script src="http://my.website/code.js" />. If I change URL of an iframe or I would like to add some features, I would like to update the code on all other web sites without asking them to make any changes.
My approach might be wrong - any suggestions are very welcome.

//For modern browsers:
document.addEventListener( "DOMContentLoaded", someFunction, false );
//For IE:
document.attachEvent( "onreadystatechange", someFunction);
`attachEvent` and `addEventListener` allow you to register more than one event listener for a particular target.
See:
https://developer.mozilla.org/en/DOM/element.addEventListener
Also definitly worth looking at how jQuery does it:
http://code.jquery.com/jquery-1.7.js Search for bindReady.

Use window.addEventListener and the events load or DOMContentLoaded:
window.addEventListener('DOMContentLoaded',function(){alert("first handler");});
window.addEventListener('DOMContentLoaded',function(){alert("second handler");});
object.addEventListener('event',callback) will insert an event listener into a queue for that specific object event. See https://developer.mozilla.org/en/DOM/element.addEventListener for further information.
For IE5-8 use window.attachEvent('event',callback), see http://msdn.microsoft.com/en-us/library/ms536343%28VS.85%29.aspx. You can build yourself a little helper function:
function addEventHandler(object,szEvent,cbCallback){
if(typeof(szEvent) !== 'string' || typeof(cbCallback) !== 'function')
return false;
if(!!object.addEventListener){ // for IE9+
return object.addEventListener(szEvent,cbCallback);
}
if(!!object.attachEvent){ // for IE <=8
return object.attachEvent(szEvent,cbCallback);
}
return false;
}
addEventHandler(window,'load',function(){alert("first handler");});
addEventHandler(window,'load',function(){alert("second handler");});
Note that DOMContentLoaded isn't defined in IE lesser 9. If you don't know your recipient's browser use the event load.

Just put your script include at the very end of the document, immediately before or after the ending </body> tag, e.g.:
(content)
(content)
<script src="http://my.website/code.js"></script>
</body>
</html>
All of the markup above the script will be accessible via the usual DOM methods (reference). Obviously, not all ancillary resources (images and such) will be fully loaded yet, but presumably that's why you want to avoid the window load event (it happens so late).
The only real purpose of ready-style events is if you don't control where the script gets included (e.g., libraries) or you need to have something execute prior to the page load and something else after the page load, and you want to avoid having two HTTP requests (e.g., for two different scripts, one before load and one after).

Related

Div is not created before javascript run

I have a question about javascript/html.
First, I have this:
var post = document.body.getElementsByClassName("post");
var x=post[i].getElementsByClassName("MyDiv")[0].innerHTML;
I get from the debugger that x is not defined, it doesn't exists.
This javascript function runs onload of the body. I am sure that I gave the right classnames in my javascript, so it should find my div.
So, I read somewhere that sometimes javascript does not find an element because it is not yet there, it is not yet created in the browser ( whatever that means).
Is it possible that my function can't find the div with that classname because of this reason?
Is there a solution?
So, I read somewhere that sometimes javascript does not find an element because it is not yet there, it is not yet created in the browser ( whatever that means).
Browsers create the DOM progressively as they get the markup. When a script element is encountered, all processing of the markup stops (except where defer and async have an effect) while the script is run. If the script attempts to access an element that hasn't been created yet (probably because its markup hasn't been processed yet) then it won't be found.
This javascript function runs onload of the body.
If that means you are using something like:
<body onload="someFn()"...>
or perhaps
<script>
window.onload = function() {
someFn();
...
}
</script>
then when the function is called, all DOM nodes are available. Some, like images, may not be fully loaded, but their elements have been created.
If it means you have the script in the body and aren't using the load event, you should move the script to the bottom of the page (e.g. just before the closing body tag) and see if that fixes the issue.
Okay, instead of calling functions with
body onload, use jQuery's ready() function, or, if you don't want to use jQuery, you can use pure javascript, but this is up to you:
// jQuery
$(document).ready(function() {
var post = document.getElementsByClassName("post"),
x = post[i].getElementsByClassName("MyDiv")[0].innerHTML;
});
// JavaScript
window.onload = function initialization() {
var post = document.getElementsByClassName("post"),
x = post[i].getElementsByClassName("MyDiv")[0].innerHTML;
}
A few side notes, I don't know what the use of innerHTML
is, and also if you're doing a for loop with i then definitely
post that code, that's kind of important.
After some discussion, my answer seems to have worked for you, but you can also place your script at the end of your body tag as #RobG has suggested.

How to execute code before window.load and after DOM has been loaded?

Here is the circumstance:
I have 2 pages:
1 x html page
1 x external Javascript
Now in the html page, there will be internal Javascript coding to allow the placement of the window.onload, and other page specific methods/functions.
But, in the external Javascript I want certain things to be done before the window.onload event is triggered. This is to allow customized components to be initialized first.
Is there a way to ensure initialization to occur in the external Javascript before the window.onload event is triggered?
The reason I have asked this, is to attempt to make reusable code (build once - use all over), to which the external script must check that it is in 'order/check' before the Javascript in the main html/jsp/asp/PHP page takes over. And also I am not looking for a solution in jQuery #_#
Here are some of the links on Stack Overflow I have browsed through for a solution:
Javascript - How to detect if document has loaded (IE 7/Firefox 3)
How to check if page has FULLY loaded(scripts and all)?
Execute Javascript When Page Has Fully Loaded
Can someone help or direct me to a solution, your help will be muchness of greatness appreciated.
[updated response - 19 November 2012]
Hi all, thanks for you advice and suggested solutions, they have all been useful in the search and testing for a viable solution.
Though I feel that I am not 100% satisfied with my own results, I know your advice and help has moved me closer to a solution, and may indeed aid others in a similar situation.
Here is what I have come up with:
test_page.html
<html>
<head>
<title></title>
<script type="text/javascript" src="loader.js"></script>
<script type="text/javascript" src="test_script_1.js"></script>
<script type="text/javascript" src="test_script_2.js"></script>
<script type="text/javascript">
window.onload = function() {
document.getElementById("div_1").innerHTML = "window.onload complete!";
}
</script>
<style type="text/css">
div {
border:thin solid #000000;
width:500px;
}
</head>
<body>
<div id="div_1"></div>
<br/><br/>
<div id="div_2"></div>
<br/><br/>
<div id="div_3"></div>
</body>
</html>
loader.js
var Loader = {
methods_arr : [],
init_Loader : new function() {
document.onreadystatechange = function(e) {
if (document.readyState == "complete") {
for (var i = 0; i < Loader.methods_arr.length; i++) {
Loader.method_arr[i]();
}
}
}
},
load : function(method) {
Loader.methods_arr.push(method);
}
}
test_script_1.js
Loader.load(function(){initTestScript1();});
function initTestScript1() {
document.getElementById("div_1").innerHTML = "Test Script 1 Initialized!";
}
test_script_2.js
Loader.load(function(){initTestScript2();});
function initTestScript2() {
document.getElementById("div_2").innerHTML = "Test Script 2 Initialized!";
}
This will ensure that scripts are invoked before invocation of the window.onload event handler, but also ensuring that the document is rendered first.
What do you think of this possible solution?
Thanking you all again for the aid and help :D
Basically, you're looking for this:
document.onreadystatechange = function(e)
{
if (document.readyState === 'complete')
{
//dom is ready, window.onload fires later
}
};
window.onload = function(e)
{
//document.readyState will be complete, it's one of the requirements for the window.onload event to be fired
//do stuff for when everything is loaded
};
see MDN for more details.
Do keep in mind that the DOM might be loaded here, but that doesn't mean that the external js file has been loaded, so you might not have access to all the functions/objects that are defined in that script. If you want to check for that, you'll have to use window.onload, to ensure that all external resources have been loaded, too.
So, basically, in your external script, you'll be needing 2 event handlers: one for the readystatechange, which does what you need to be done on DOMready, and a window.onload, which will, by definition, be fired after the document is ready. (this checks if the page is fully loaded).
Just so you know, in IE<9 window.onload causes a memory leak (because the DOM and the JScript engine are two separate entities, the window object never gets unloaded fully, and the listener isn't GC'ed). There is a way to fix this, which I've posted here, it's quite verbose, though, but just so you know...
If you want something to be done right away without waiting for any event then you can just do it in the JavaScript - you don't have to do anything for your code to run right away, just don't do anything that would make your code wait. So it's actually easier than waiting for events.
For example if you have this HTML:
<div id=one></div>
<script src="your-script.js"></script>
<div id=two></div>
then whatever code is in your-script.js will be run after the div with id=one but before the div with id=two is parsed. Just don't register event callbacks but do what you need right away in your JavaScript.
javascript runs from top to bottom. this means.. if you include your external javascript before your internal javascript it would simply run before the internal javascript runs.
It is also possible to use the DOMContentLoaded event of the Window interface.
addEventListener("DOMContentLoaded", function() {
// Your code goes here
});
The above code is actually adding the event listener to the window object, though it's not qualified as window.addEventListener because the window object is also the global scope of JavaScript code in webpages.
DOMContentLoaded happens before load, when images and other parts of the webpage aren't still fully loaded. However, all the elements added to the DOM within the initial call stack are guaranteed to be already added to their parents prior to this event.
You can find the official documentation here.

jquery: exclude external resources from $(window).load()

I need to execute some scripts when all the resources on my domain and subdomain are loaded, so I did this:
$(window).load(function(){
// al my functions here...
}
The problem is that there are some external resources (not on my domain and subdomain) that sometimes take longer to load. Is there a way to exclude external resources from the load event?
EDIT:
I was hoping to do something like:
$(window).not(".idontcare").load(function()
but it's not working
I guess your external resources rely on a src attribute.
If so, in your page source code you could set the src attribute of the resources you don't want to wait for, not as src but as external_src.
Then you could easily do:
$(document).ready(function(){
$(window).load(function(){
// all your functions here...
});
$('[external_src]').each(function() {
var external_src = $(this).attr("external_src");
$(this).attr("src", external_src); // now it starts to load
$(this).removeAttr("external_src"); // keep your DOM clean
//Or just one line:
//$(this).attr("src", $(this).attr("external_src")).removeAttr("external_src");
});
});
This way the external resources should start loading as soon as just the DOM is ready, without waiting for the full window load.
I have almost same case. But in my case, I want to exclude all iframes that load content from another site (e.g. youtube, vimeo etc). Found a work around, so the scenario is hide 'src' attribute from all iframes when DOM is ready and put it back when window is finish load all another content.
(function($){
//DOM is ready
$(document).ready(function(){
var frame = $('iframe'),
frameSrc = new Array();
if( frame.length ){
$.each( frame, function(i, f){
frameSrc[i] = $(f).attr('src');
//remove the src attribute so window will ignore these iframes
$(f).attr('src', '');
});
//window finish load
$(window).on('load',function(){
$.each( frame, function(a, x){
//put the src attribute value back
$(x).attr('src', frameSrc[a]);
});
});
}
});
})(jQuery);
You can mark all elements in your site that load external resources by adding a special class, and change the iframe with $('.special_class') or something like that. I dont know if this is the best way but at least it works great in my side :D
Unfortunately, the window.onload event is very strict. As you might know it will fire when all und every resource was transfered and loaded, images, iframes, everything. So the quick answer to your question is no, there is no easy-to-use way to tell that event to ignore external resources, it makes no difference there.
You would need to handle that yourself, which could be a tricky thing according to how those resources are included and located. You might even need to manipulate the source code before it gets delivered to accomplish that.
As far as I know, there is an async - tag for script tags. You can your includes to:
<script src="script_path" async="true"></script>
This will not include them to the event.
maybe
$(document).ready(...)
instead of $(window).load() will help?
The document ready event executes already when the HTML-Document is loaded and the DOM is ready, even if all the graphics haven’t loaded yet.

How do I know when DOM “body” element is available?

As soon as body DOM node is available, I'd like to add a class to it with JavaScript.
I want this to happen as soon as possible, before any of body's children are loaded.
Right now, I'm using an inline script right after opening body tag. Is there a less obtrusive way?
Might be a bit late to the party but...
You can just tap into the browser rendering cycle. Thus you don't have to deal with timeouts which leak memory (if improperly used).
var script = document.createElement('script');
script.src = '//localhost:4000/app.js';
(function appendScript() {
if (document.body) return document.body.appendChild(script);
window.requestAnimationFrame(appendScript);
})();
I would imagine this will differ between browsers.
One solution may be to test for it by placing a script immediately inside the opening <body> tag, then running your code at an interval to add the class.
<body>
<script>
function add_class() {
if(document.body)
document.body.className = 'some_class';
else
setTimeout(add_class, 10); // keep trying until body is available
}
add_class();
</script>
<!-- rest of your elements-->
</body>
jQuery does something similar internally to deal with a particular IE bug.
There isn't a guarantee that the descendant elements won't be loaded though, since again it will depend on when the particular implementation makes the body available.
Here's the source where jQuery takes a similar approach, testing for the existence of the body in its main jQuery.ready handler, and repeatedly invoking jQuery.ready via setTimeout if the body isn't available.
And here's an example to see if your browser can see the <body> element in a script at the top of the element, before the other elements. (Open your console)
Here's the same example without needing the console.

Generating unobtrusive JS

I have read quite a bit about unobtrusive JS and how to generate it and all that jazz...
My problem is this: I have a website that heavily relies on mod_rewrite, so essentially all the pages requests are sent to index.php that generates the main structure of the page and then includes the appropriate page. Now, there are different sections in the site and each section uses different Javascript functions (e.g. for different AJAX requests).
Now, if I just were to attach a function to the onload of the page obviously the thing would not work, as I do not have to initialise the same things for each page... so what is the best way to handle this situation?
I hope the situation is clear, I'll be happy to clarify if needed
You can use addEventListener (standard) or attachEvent in the HTML generated by the subsidiary PHP pages.
The syntax is simple. E.g.
document.addEventListener("load", someFunction, false);
This allows you to generate the full body tag in index.php but run different load handlers for each page. Also note that you can use this multiple times on the same element.
Nico, I would suggest creating a custom javascript code with each included page (doesn't matter where on the page you include the script tag) and, as Matthew suggested, after you define a function to run on page load, use the addEventListener to load that custom function on "load"
Let's say you define a function pageinit() somewhere in the body of the included document
function pageinit(){..
}
window.addEventListener("load", function() { pageinit(); }, false);
Does that make sense for your project?
I would simply put it in a .js file.
mysite_common.js - site wide common utils and functions
mysite_page_a.js - unique functionality for page a
mysite_page_b.js - unique functionality for page b
for page b you include b.js while on page a you would include a.js
Then in your respective unique.js you can wrap your functionality in a ondomready or similar.
Keep it separate from your PHP, then it is much less of an annoyance later, it also means that you can rely on caching for your js to keep your page loads slimmer.
You can also look at things like YUI loader which allows you to do much more complex things like ondemand loading of bits of js functionality.
You can use event delegation to provide different functionality depending on context.
Basically it works by attaching an event listener to a container element which captures clicks on child elements. You can then do away with individual event listeners alltogether, as well as look at hints from the parent.
say:
<div id='container' class='page_a'>
...
<input name='somename'>
...
</div>
Then
var attachDelegates = function(container){
container.onclick = function(e) {
e = e || window.event;
var t = e.target || e.srcElement;
//Your logic follows
if(t.name === 'somename'){
dosomething(t);
}
if(t.className === 'someclass'){
... something else ...
}
};
and onload = function(){attachDelegates('container');};
The attachDelegates function could be different for each page, or you could have a monolithic one and simple attach hints to the container or be selective about which classes you attach.
These are much more coherent explanations and examples:
http://cherny.com/webdev/70/javascript-event-delegation-and-event-hanlders
http://blog.andyhume.net/event-delegation-without-javascript-library
Personally I use YUI3
http://developer.yahoo.com/yui/3/examples/node/node-evt-delegation.html
as it gives me CSS3 style selectors and is pretty hassle free so far.

Categories