I've been working on this for a few hours and am at the point where I feel like I might be trying to accomplish something unworkable. Any advice or insight is definitely appreciated!
The use case I'm targeting requires interrupting the browser's DOM parsing before it has a chance to draw the <body> element. To accomplish this, I've used this one-liner as the first script called in my <head> element:
document.replaceChild(document.createElement('html'), document.children[0]);
This successfully prevents the browser from calling any subsequent scripts, and the <body> element remains un-rendered. However, if the markup includes an onload attribute on the <body> element, that function or code is still called by the browser.
As the context which intercepts DOM parsing is in <head>, I can't use document.body.removeEventListener() to drop the event listener, because the body element doesn't exist yet. Setting window.onload to null has no effect, and neither does trying to intercept setting of window.onload using Object.defineProperty(window, 'onload', {...}). In each case, the onload function or statements still get called.
To preempt the obvious question as to why I can't just avoid using <body onload="...">, the use case is for developer tooling, so I'm trying to create contingency for certain absolute conditions. If it can't be done, it isn't killer to my project, but I'd sure like to know why it can't be done.
Thanks in advance!
You can use Document readystate at the interactive state, and overwrite the onload attribute.
document.onreadystatechange = () => {
if (document.readyState === 'interactive')
window.onload = null;
}
(Added your code from your comment for completeness.)
Related
I am using MS Edge in IE11 compatibility mode and have an HTML page in which I have:
<input id="onLoadAttributeHiddenId" type="hidden" value="schadenAuswahl.setCustomCheck();schadenAuswahl.createArrays();">
Further below, I have also:
<script language="JavaScript1.1" src="../js/table_head_uip.js"></script>
And in the table_head_uip.js, I have:
document.body.addEventListener('load', customOnLoadFunction, false);
function customOnLoadFunction(){
var onLoadAttributeFunctStr = document.getElementById("onLoadAttributeHiddenId").value;
var onLoadAttributeFunct = new Function(onLoadAttributeFunctStr);
onLoadAttributeFunct;
}
Now, when I put breakpoints in the table_head_uip.js, the line.
var onLoadFunct = document.body.addEventListener('load', customOnLoadFunction, false);
It gets executed, but the function customOnLoadFunction never gets completed. Also, I do not receive any errors in the console.
Any help will be appreciated.
load events only fire on elements which load things (like an <img> with a src attribute) and the window object (for when the entire document, including its dependencies, has loaded).
The body element doesn't load anything.
The equivalent of the obsolete onload attribute for the body is a load event that fires on the window object. That attribute only existed because, since window isn't an element, you couldn't put attributes on it.
Additionally, the statement onLoadAttributeFunct doesn't do anything. To be useful you need to, for example, put () after it to call it.
new Function(onLoadAttributeFunctStr) is effectively a time delayed eval and is considered dangerous, slow, and hard to debug. You should reconsider the design of your application if you need that.
I'm encountering a strange issue. I am developing a books application and using javascript onload. I read somewhere that its best to include your javascript at the end of the html. This works for most of the html loaded. However some complain that onload init() not found. This gets solved if i include the javascript in the html head. But than other htmls start behaving strangely. onload gets called before the page is fully loaded. i dont get the correct scroll width. Please suggest what could be worng. Whats the best way of including javascripts. Thanks
html is as follows
columizer id use css column-width which i've defined like this.
css style below
#columnizer
{
width:290px;
height:450px;
column-width:290px;
column-gap:10px;
word-wrap:break-word;
}
Javascript onload is defined like this.
function init()
{
docScrollWidth = document.getElementById('columnizer').scrollWidth;
document.getElementsByTagName('body')[0].style.width = docScrollWidth + "px";
window.external.notify(str);
}
Since the actual answer was in my comment, I'll add that to my answer:
My guess is that you're doing something like window.onload = init(); instead of window.onload = init; and the init function will have to be declared before you do that assignment. You assign function references without the parens. Using the parens causes it to get executed immediately.
You say you're using this code:
docScrollWidth = document.getElementsByTagName('body')[0].style.width
The main problem with this is that style.width ONLY reads a style attribute set directly on the body object. It doesn't get the width of the object as calculated by layout or CSS rules.
So, what you should use instead really depends upon what you're trying to do. The body width will nearly always be the same or more than the window width unless your content is entirely fixed width. So, that makes me wonder what you're trying to accomplish here? What you should use instead depends upon what you're really trying to do.
FYI, document.body is a direct reference to the body object so you don't need document.getElementsByTagName('body')[0].
First, let me define the problem. The window.onload event is used by programmers to kick-start their web applications. This could be something trivial like animating a menu or something complex like initialising a mail application. The problem is that the onload event fires after all page content has loaded (including images and other binary content). If your page includes lots of images then you may see a noticeable lag before the page becomes active. What we want is a way to determine when the DOM has fully loaded without waiting for all those pesky images to load also.
Mozilla provides an (undocumented) event tailor-made for this: DOMContentLoaded. The following code will do exactly what we want on Mozilla platforms:
// for Mozilla browsers
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init, false);
}
So what about Internet Explorer?
IE supports a very handy (but non-standard) attribute for the tag: defer. The presence of this attribute will instruct IE to defer the loading of a script until after the DOM has loaded. This only works for external scripts however. Another important thing to note is that this attribute cannot be set using script. That means you cannot create a script using DOM methods and set the defer attribute – it will be ignored.
Using the handy defer attribute we can create a mini-script that calls our onload handler:
<script defer src="ie_onload.js" type="text/javascript"></script>
The contents of this external script would be a single line of code to call our onload event handler:
init();
There is a small problem with this approach. Other browsers will ignore the defer attribute and load the script immediately. There are several ways round this. My preferred method is to use conditional comments to hide the deferred script from other browsers:
<!--[if IE]><script defer src="ie_onload.js"></script><![endif]-->
IE also supports conditional compilation. The following code is the JavaScript equivalent of the above HTML:
// for Internet Explorer
/*#cc_on #*/
/*#if (#_win32)
document.write("<script defer src=ie_onload.js><\/script>");
/*#end #*/
So far so good? We now need to support the remaining browsers. We have only one choice – the standard window.onload event:
// for other browsers
window.onload = init;
There is one remaining problem (who said this would be easy?). Because we are trapping the onload event for the remaining browsers we will be calling the init function twice for IE and Mozilla. To get around this we should flag the function so that it is executed only once. So our init method will look something like this:
function init() {
// quit if this function has already been called
if (arguments.callee.done) return;
// flag this function so we don't do the same thing twice
arguments.callee.done = true;
// do stuff
};
I’ve provided a sample page that demonstrates this technique.
In my HTML, I have a simple button defined, like so:
<button id="toggleButton">Stop</button>
I am trying to grab it with the following code:
buttonElement = document.getElementById("toggleButton");
with the goal of assigning an event to it, like so:
buttonElement.onclick = stopTextColor();
The problem is that the getElementById is returning null, even though I can see it in the DOM. What am I doing wrong here?
For clarity, I posted the full code at http://cdpn.io/sqEuH
The problem, probably, is that you're including the JS in the head. What's happening there is the JS is running before the page gets loaded, so the button doesn't show up. Move it to right before the </body> tag, and this problem will be solved, or wrap it with a window.onload() event.
The code you post will work unless the javascript cannot access the given DOM element.
The main possibilities:
The javascript runs before the DOM is parsed (IE if you run it in the head of the document without any code to instruct it to wait till the DOM is ready)
You can usually get around this by placing your script at the bottom of the body rather than in the head or midway through the body. The essential thing to understand here though is that JS can't access an element till the browser has parsed the DOM. The browser parses HTML top-down, and JS scripts run top down, so if you run the JS before the element is parsed, it won't be available to the javascript function yet.
The javascript runs in a context where it can't access the element (inside an iFrame for instance). In this case it would be a question of whether the element is really under the "document" object that you're referring to. If the element is inside an iFrame it will be underneath the iFrame's document object.
Try putting your script just before closing your <body> tag. The DOM is probably not fully loaded when your script is run.
Also, I think you have an error in your Javascript. It should be
buttonElement.onclick = stopTextColor;
instead of
buttonElement.onclick = stopTextColor();
Altough it shouldn't throw any error, it's good practice.
If you want to keep your Javascript before <body>, you can use a listener to wait for the DOM to be loaded and then execute your script, like this :
window.addEventListener("DOMContentLoaded", function() {
buttonElement = document.getElementById("toggleButton");
buttonElement.onclick = stopTextColor;
}, false);
[edit]
The snippet above doesn't work in IE < 9. If you need to support it, use document.load instead, it should give the same result, like so :
document.onload = function() {
buttonElement = document.getElementById("toggleButton");
buttonElement.onclick = stopTextColor;
}
The differece between both, besides browser compatibility, is that window.addEventListener("DOMContentLoaded", function() {...} will fire when the DOM is loaded, but window.load will fire when the DOM AND all other resources (images, stylesheets, etc.) are loaded (slower, and not necessary in your case).
This question is so basic, I'm certain in must be a duplicate of something, even though I've looked for something similar.
My question is basically: Where is the best place to initially register event handlers for HTML elements?
The easiest way to register an event handler is obviously to just do it inline:
<div id = "mybutton" onclick = "doSomething()">Click me</div>
But this goes against the overwhelming march towards separation of logic and content in modern web development. So, in 2012, all logic/behavior is supposed to be done in pure Javascript code. That's great, and it leads to more maintainable code. But you still need some initial hook that hooks up your HTML elements with your Javascript code.
Usually, I just do something like:
<body onload = "registerAllEventHandlers()">
But... that's still "cheating", isn't it - because we're still using inline Javascript here. But what other options do we have? We can't do it in a <script> tag in the <head> section, because at that point we can't access the DOM since the page hasn't loaded yet:
<head>
<script type = "text/javascript">
var myButton = document.getElementById("mybutton"); // myButton is null!
</script>
</head>
Do we place a <script> tag at the bottom of the page or something? Like:
<html>
<body>
...
...
<script type = "text/javascript">
registerAllEventHandlers();
</script>
</body>
</html>
What is the best practice here?
You can use window.onload:
<script type = "text/javascript">
window.onload = registerAllEventHandlers;
</script>
Or if you use jquery:
$(registerAllEventHandlers);
Using onload works because it registers onload event immediately but fires it when DOM is ready.
I had a similar answer to this but was about JavaScript in general. But the idea is still the same - load scripts before closing the body.
Take advantage of libraries that abstract the window.onload and the DOM ready event. That way, you can load the scripts as soon as the DOM is ready.
Personally, I have no problems with adding onlclick="doSomething();" to elements. No logic, just a function call.
All logic is where it should be: in the function defined in the HEAD or a separate file.
Tell me what the difference is when you add href="somepage.html" or even href="somepage.html#someanchor" to an A tag.
You should register your event handlers as soon as the DOM is ready. Detecting this across all browsers hasn't always been easy, although with the notable exception of IE 8 (and earlier) most widely used browsers now support the DOMContentLoaded event (thanks to gengkev for pointing that out in the comments).
This is essentially equivalent to calling your registerAllEventHandlers function at the end of your body, but it has the advantage that you don't need to add any JavaScript to your HTML.
It is significantly better than using window.onload because that isn't executed until all of the page's assets (images, CSS etc.) have loaded.
If you're using one of the major JavaScript frameworks, then you can very easily detect when the DOM is ready, even in older versions of IE. With jQuery you would use the ready event:
jQuery(document).ready(function () {
// Your initialisation code here
});
Or the shorthand:
jQuery(function() { … });
With Prototype you would use the dom:loaded event:
document.observe("dom:loaded", function() {
// Your initialisation code here
});
I'm having problems with debugging DOM changes introduced by some JavaScript code I'm running. Somewhere in the code an element's class is changed, and I'm trying to pinpoint where exactly. Unfortunately, the new class name is so generic that searching through all the JS code gives too many results to be a viable option.
I've tried debugging a bit with Firebug, but despite the nice "Break on Attribute Change" feature, I can't get it to work in a way I would want. The Firebug demo works correctly, but it's a post load situation.
The problem seems to be that I want to watch for mutations before the page is fully loaded. I assume that the changes occur somewhere in $(document).ready(), so it's in the DOM, but I can't select elements for UI breakpoints as would be the case with the demo (after page load).
Is there some way to debug this kind of situation other than grepping/going through the code by hand?
I propose that you remove the target element where its classname is changed. With some luck, you may be able to generate an error in the JavaScript code, so you will find where is your problem.
Otherwise, if it's done by JQuery (addClass), you may want to modify the JQuery code itself just to figure out the callstack.
The code would look like this (make sure this code is the first code called after JQuery inclusion):
(function () {
var addClass = jQuery.fn.addClass;
jQuery.fn.addClass = function (value) {
for (var i = 0, l = this.length; i < l; i++) {
// Here you put your method to start the debugger if you get your right element
if (this[i].id === "abc") {
debugger;
}
}
addClass(value);
}
})();
Hope that helps.
This answer may sound pretty lame, but I honestly think the best solution for bugs like this is "deconstructing" your program. Just make a copy of the entire project, then rip it apart. Take out chunks of code one by one. Turn function calls into stub functions or whatever to keep things running. Find the minimal amount of code that triggers the bug. Then the solution should be obvious.
Have you considered adding a mutation event? The event I think you want, DOMAttrModified, is not supported in webkit, so you might have to test with Firefox or Opera. In fact it is deprecated in DOM level 3.
There are two jQuery plugins for mutation events here (documentation) and here but since you want to do this before page load they might not be the answer.
You are probably best writing your own JavaScript bind for this event - there is an example in the answer to is there an alternative to DOMAttrModified that will work in webkit
I hope this helps.
If you want to use the "Break on Attribute Change" feature to debug, you can do the following:
Comment out all JS in the page (which is hopefully all in the head) except for the base jQuery load.
Load the page and set your watch points.
Add a button, which fires the JS, to the HTML. Or, optionally fire it from the console.
Trigger the JS load/fire. Hopefully your watch and break points will fire as desired.
For example, suppose your page loads/has:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript"></script>
<script src="/Library_X.js" type="text/javascript"></script>
<script src="/MyJS.js" type="text/javascript"></script>
Then you could run this code in the console after setting the watch points:
function addJS_Node (text, s_URL)
{
var scriptNode = document.createElement ('script');
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
document.head.appendChild (scriptNode);
}
addJS_Node (null, '/Library_X.js');
//-- Possible pause here.
addJS_Node (null, '/MyJS.js');
// etc.
Or temporarily code a button that fires the same JS, into the page's HTML.