Is window.document ever null or undefined? - javascript

I've been doing some research on the window.document object in order to make sure one of my JavaScript solutions is reliable. Is there ever a case when the window.document object is null or undefined?
For the sake of discussion here's a non-relevant example piece of code. Are there any situations in which this piece of code will fail (aka, throw an exception)?
$(document).ready(function() {
var PageLoaded = (window.document.readyState === "complete");
});

Is there ever a case when the window.document object is null or undefined?
Yes, for JavaScript code that isn't in a document (e.g. node.js). But such code may not have a window object either (though it will have a global object).
For code in HTML documents in user agents compliant with the W3C DOM, no.
> Are there any situations in which this piece of code will fail (i.e. throw
> an exception)?
>
> [snip jQuery code]
It will fail where:
the jQuery ready function fails (likely in at least some browsers, though not the ones in popular use for desktop and some mobile devices),
there is no window object, or
there is no window.document object
To be confident of code working in a variety of hosts, you can do something like:
if (typeof window != 'undefined' && window.document &&
window.document.readyState == whatever) {
// do stuff
}
which isn't much extra to write, and likely only needs to be done once.
Alternatives:
(function (global) {
var window = global;
if (window.document && window.document.readyState == whatever) {
// do stuff
}
}(this));
and
(function (global) {
var window = global;
function checkState() {
if (window.document && window.document.readyState) {
alert(window.document.readyState);
} else {
// analyse environment
}
}
// trivial use for demonstration
checkState();
setTimeout(checkState, 1000);
}(this));

I think document is always defined, cause all that browser shows you is a html-document, even site is not available . More, document is readonly property
window.document = null;
console.log(window.document); //Document some.html#

Ignoring the fact that Javascript runs other places besides web browsers/user-agents, your pageLoaded test may fail on iframes (untested, but I know they get weird).
There may also be some question about what does "page loaded" mean. Are you trying to see if the DOM has been rendered and the elements are ready to be manipulated? Or are you checking to see if the page load is indeed complete, which includes having all of the other elements, such as graphics, loaded as well.
This discussion may be useful:
How to check if DOM is ready without a framework?

Because your javascript code must be written in a html document,so your code coudn't be executed out of document,in other word,no document,no javascript.

Related

How to only wait for document.readyState in IE and fire instantly for all other browsers?

I have written a CSS and Javascript lazyloader to dynamically load resources for seperate pagelets (in the way that Facebook renders a page with it's BigPipe technology).
In short an HTML frame is rendered first, then separate parts of the page are all generated asynchronously by the server. When each pagelet arrives the pagelets css is loaded first, then its innerHTML is set, then finally we load any required javascript for this pagelet and initialise it.
Everything works perfectly and perceived load time is pretty much instantaneous for any given page.
However in IE, I occasional I get Method does not support method or property when initialising the scripts.
I have solved this by checking for document.readyState before loading the scripts.
Now this isn't a huge issue but it adds on average 170ms to a pageload in chrome or firefox. Which is not needed.
function loadScripts(init){
// ensure document readystate is complete before loading scripts
if( doc.readyState !== 'complete'){
setTimeout(function(){
loadScripts(init);
}, 1 );
}
else{
complete++;
if(complete == instance.length){
var scripts = checkJS(javascript);
if(scripts.length) {
LazyLoad.js(scripts, function(){
runPageletScript();
for (var i = 0; i < scripts.length; ++i) {
TC.loadedJS.push(scripts[i]);
}
});
}
else{
runPageletScript();
}
}
}
}
What I am looking for is a modification to this script which will only implement the 'wait' in IE, if it is any other browser it will just fire straight away. I cannot use a jQuery utility like $.Browser and need it to be the tiniest possible method. I hate to use any form of browser detection but it appears as though its my only solution. That said if anyone can come up with another way, that would be fantastic.
Any suggestions would be gratefully received.
You could use JScript conditional compilation, which is only available in IE browsers (up to IE10).
Because it's a comment, it's best to place it inside new Function as minifiers might remove it, changing your code. Though in general you should avoid using new Function, in this case there's not really any other way to prevent minifiers from removing it.
Example:
var isIE = !(new Function('return 1//#cc_on &0')());
However, it seems that your main issue is that the DOM hasn't loaded yet -- make sure that it has loaded before running any loader using the DOMContentLoaded event (IE9+):
document.addEventListener('DOMContentLoaded', function () {
// perform logic here
});
Here is just another solution as the solution from Qantas might not always work. For instance on UMTS connections it could happen that providers remove comments to save bandwith (maybe they preserve conditional comments):
if(navigator.appName == 'Microsoft Internet Explorer'
&& doc.readyState !== 'complete'){
...
}

Issue with retrieving object data on IE 8 on Windows XP or 2003

This is an interesting problem that I am facing with JavaScript and IE8 on Windows XP and Windows 2003. I create an object on the page and then retrive information about that object (for example, its version). When trying to get the version, I am running this code:
var myObject = document.getElementById(objectId);
console.log(myObject.version);
What is interesting is that this code works on every single browser except IE8 on Windows XP and 2003. I've done some debugging and this is where things get interesting.
myObject is not null but myObject.version is undefined. So what I did is I added an alert in between so the code is now as follows:
var myObject = document.getElementById(objectId);
alert(myObject.version);
console.log(myObject.version);
The alert results in "undefined", however, the console.log is now resulting in the actual version. If I add an alert before this alert of anything (let's say alert("something")) then the second alert has the actual version now. I am assuming this is a timing issue (for some reason the object needs sometime to be able to provide the data stored in it?) but I am not sure what kind of timing issue this is or how to approach it.
Sorry for the long description but any help is appreciated.
document.getElementById doesn't return an object. It returns a DOM element. So, you expect to see a .version property in a DOM element, which by the official W3C specification is missing (or at least I don't know about this).
I'm not sure what you are expecting to see in .version, but if it is something custom then you should create a custom object like that:
var o = { version: "..." }
console.log(o);
You said that this may be a time issue. If that's true then I'll suggest to try to access the .version property after the DOM is fully loaded. You can use jQuery for the purpose:
$(document).ready(function() {
var myObject = document.getElementById(objectId);
alert(myObject.version);
console.log(myObject.version);
});
You can add a setTimeout in your function till the .version property is there.
var f = function(callback) {
var check = function() {
var myObject = document.getElementById(objectId);
alert(myObject.version);
console.log(myObject.version);
if(typeof myObject.version !== "undefined") {
callback(myObject.version);
} else {
setTimeout(check, 1000);
}
}
setTimeout(check, 1000);
}
What happens if you put the <script>...</script> tag with the js code at the end of the html file? In my opinion, the code is executed when the DOM is not ready. If you put it in the end, then it will be executed after it's loaded.

'Date' is undefined in IE9 in javascript loaded by FacePile

I'm currently getting an error within Facebook's FacePile code, and I'm baffled by the cause.
facepile.php loads a script which, among other things, has these lines (when pretty-printed):
...
o = document.createElement('script');
o.src = l[n];
o.async = true;
o.onload = h;
o.onreadystatechange = function() {
if (o.readyState in c) {
h();
o.onreadystatechange = null;
}
};
d++;
a.appendChild(o);
...
(a == document.body, d++ is irrelevant here)
This code loads a script with src = http://static.ak.fbcdn.net/rsrc.php/v1/yW/r/pmR8u_Z_9_0.js or something equally cryptic (the filename changes occasionally).
In that script, there are these lines at the very top (also when pretty-printed):
/*1331654128,176820664*/
if (window.CavalryLogger) {
CavalryLogger.start_js(["\/8f24"]);
}
window.__DEV__ = window.__DEV__ || 0;
if (!window.skipDomainLower && document.domain.toLowerCase().match(/(^|\.)facebook\..*/))
document.domain = window.location.hostname.replace(/^.*(facebook\..*)$/i, '$1');
function bagofholding() {
}
function bagof(a) {
return function() {
return a;
};
}
if (!Date.now)
Date.now = function now() {
return new Date().getTime();
};
if (!Array.isArray)
Array.isArray = function(a) {
return Object.prototype.toString.call(a) == '[object Array]';
};
...
And I'm getting an error which says "SCRIPT5009: 'Date' is undefined", right at the if (!Date.now) portion. Debugging near that point reveals that Date, Array, Object, Function, etc are all undefined.
Er... how? window exists, as does document (though document.body is null) and a handful of others, but plenty of pre-defined objects aren't. Earlier versions of IE don't seem to have this problem, nor do any other browsers, but multiple machines running IE9 (including a clean VM) all have the same issue.
I doubt I can do anything about it, but I'm very curious how this is happening / what the underlying problem is. Does anyone know, or can they point me to something that might help?
-- edit:
Prior to posting this question, I had found this site: http://www.guypo.com/technical/ies-premature-execution-problem/
While it seemed (and still does) like it might be the source of the problem, I can't replicate it under any smaller circumstances. All combinations I've tried still have Date, etc defined ; which isn't too surprising, as otherwise I'm sure others would be seeing many more problems with IE.
If you step through with a javascript debugger at the first point any JS gets run. At the same time add a watch for Date/Array etc. and note when it goes to null. Might be slow and laborious but I can't see why it wouldn't work.
You may want to try adding the script in a document.ready function. In other words, insure that the FB script is processed only after the DOM is ready. But, based on the link you give to Guy's Pod (great article, by the way), it seems you're right in the assertion that IE is downloading and executing the script pre-maturely (hence my suggestion to add a wrapper so that it only executes after the DOM ready event). IE9 is probably sandboxing the executing script (outside the document/window scope).

Failure to override Element's addEventListener in Firefox

I'm trying to override the Element object's addEventListener method, in a cross-browser manner. The purpose is so that I can load some 3rd party scripts asynchronously, and those scripts call this method prematurely.
I created an HTML file that works perfectly in Chrome, but on Firefox I get this exception:
"Illegal operation on WrappedNative prototype object" nsresult: "0x8057000c (NS_ERROR_XPC_BAD_OP_ON_WN_PROTO)"
If you comment out the lines in the file that change the INSTANCE methods, it works. But I need to do it on the "class type" (i.e. prototype).
Any suggestions would be appreciated.
Thanks,
Guypo
Here's the file I created
<html><body>
<img id="testImg" src="http://www.blaze.io/wp-content/themes/Blaze/images/header_logoB.png">
<script>
function myLog(msg) { "undefined" != typeof(console) && console.log("Log: " + msg); }
function customListener(type, event, useCapture) {
// Register the event
myLog('Registering event');
this._origListener.apply(this, arguments);
}
// Also tried HTMLImageElement
Element.prototype._origListener = Element.prototype.addEventListener;
Element.prototype.addEventListener = customListener;
var img = document.getElementById("testImg");
// Uncommenting these lines works - but in the real case I can't access these objects
//img._origListener = img.addEventListener;
//img.addEventListener = customListener;
img.addEventListener('load',function() { myLog('load callback'); }, false);
</script>
</body>
</html>
Like Marcel says, this has to do with the way host objects are exposed. The problem is that if you override a predefined property of an interface, it doesn't get changed in the interfaces that inherit from it (at least in Firefox).
Although I agree with Marcel's remarks, there is in fact a way to do this. You should override the property of the lowest possible interface of the object you want to do this for. In your case this would be the HTMLImageElement.
This will do the trick:
(function() {
var _interfaces = [ HTMLDivElement, HTMLImageElement /* ... (add as many as needed) */ ];
for (var i = 0; i < _interfaces.length; i++) {
(function(original) {
_interfaces[i].prototype.addEventListener = function(type, listener, useCapture) {
// DO SOMETHING HERE
return original.apply(this, arguments);
}
})(_interfaces[i].prototype.addEventListener);
}
})();
Please use a valid Doctype, preferably one that cast browsers into (Almost) Standards mode (<!DOCTYPE html> is fine).
typeof is an operator, you don't need the parentheses (but it doesn't hurt)
Most important: don't try to manipulate DOM Element's prototype: those are host objects and you can't rely on host objects exposing any of the core JavaScript functionality of an object. Worse, there may be a performance penalty and you might break other things. Stay away from that technique. For more information, read What’s wrong with extending the DOM.
What is it that you want to achieve, that you want your event listeners to be called automatically when using 3rd party scripts?

How do I get the Window object from the Document object?

I can get window.document but how can I get document.window? I need to know how to do this in all browsers.
You can go with document.defaultView if you’re sure its a window and its okay to skip Microsoft browsers before IE 9.
A cross browser solution is complicated, here's how dojo does it (from window.js::get()):
// In some IE versions (at least 6.0), document.parentWindow does not return a
// reference to the real window object (maybe a copy), so we must fix it as well
// We use IE specific execScript to attach the real window reference to
// document._parentWindow for later use
if(has("ie") && window !== document.parentWindow){
/*
In IE 6, only the variable "window" can be used to connect events (others
may be only copies).
*/
doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
//to prevent memory leak, unset it after use
//another possibility is to add an onUnload handler which seems overkill to me (liucougar)
var win = doc._parentWindow;
doc._parentWindow = null;
return win; // Window
}
return doc.parentWindow || doc.defaultView; // Window
has("ie") returns true for IE (and false otherwise)
Well, this is the solution I went with. It works, but I hate it.
getScope : function(element) {
var iframes = top.$$('iframe');
var iframe = iframes.find(function(element, i) {
return top[i.id] ? top[i.id].document == element.ownerDocument : false;
}.bind(this, element));
return iframe ? top[iframe.id] : top;
}
I opted to inject the DOCUMENT token from #angular/platform-browser:
import { DOCUMENT } from '#angular/platform-browser'
and then access the parent:
constructor(#Inject(DOCUMENT) private document: any) {
}
public ngOnInit() {
// this.document.defaultView || this.document.parentWindow;
}
first off let's be clear. this sort of thing is often necessary when you are working with frames, iframes, and multiple windows, and so "the window is just the global object" is an unsatisfying answer if all you have a handle to is a document from another window than the one you are in.
second, unfortunately there is no direct way of getting at the window object. there are indirect ways.
the primary mechanism to use is window.name. when creating a window or a frame from some parent window, you can usually give it a unique name. any scripts inside that window can get at window.name. any scripts outside the window can get at the window.name of all its child windows.
to get more specific than that requires more info about the specific situation. however in any situation where the child scripts can communicate with parent scripts or vice versa, they can always identify each other by name, and this is usually enough.
The Window object is the top level object in the JavaScript hierarchy, so just refer to it as window
Edit:
Original answer before Promote JS effort. JavaScript technologies overview on Mozilla Developer Network says:
In a browser environment, this global object is the window object.
Edit 2:
After reading the author's comment to his question (and getting downvotes), this seems to be related to the iframe's document window. Take a look at window.parent and window.top and maybe compare them to infer your document window.
if (window.parent != window.top) {
// we're deeper than one down
}

Categories

Resources