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

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
}

Related

Try Catch Errors many script components - JavaScript

I have a page that contains many script components (50+) and I am getting an error when using IE at some random instance (doesn't happen in Chrome or Firefox).
"Out of Memory at line: 1"
I've done some google search too and that reveals issues with IE handling things differently to Chrome and FF. I would like to catch this error and know exactly what the cause of that script error is.
What would be the best way to use a global try-catch block on that many script components? All these script components are on the same page. Looking forward to your suggestions.
You might want to try window.onerror as a starting point. It will need to be added before the <script> tags that load the components.
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
If that fails, you might try reducing the components loaded by half until the error no longer occurs. Then, profile the page (you may have to reduce further due to the demand of profiling). Look for a memory leak as #Bergi suggested. If there is in fact a leak, it will likely occur in all browsers, so you can trouble-shoot in Chrome, as well.
If that still fails to yield anything interesting, the issue may be in one particular component that was not in the set of components you were loading. Ideally, anytime that component is included you see the issue. You could repeatedly bisect the loaded components until you isolate the culprit.
Finally, forgot to mention, your home-base for all of this should be the browser's developer tools, e.g. Chrome dev tools, or if it is unique to Edge, Edge debugger.
And FYI, Edge is the browser that crashes, but that does not mean the issue is not present in Chrome or FF.
One important thing that is missing in your question is if the error happens during the page loading or initialization or if it happens after some time while you browse the page.
If it's during loading or initialization, it's probably caused by the fact that your page contains too many components and uses much more memory than the browser is willing to accept (and IE is simply the first one to give up).
In such case there is no helping but reduce the page size. One possible way is to create only objects (components) that are currently visible (in viewport) and as soon as they get out of the viewport remove them from JS and DOM again (replacing the with empty DIVs sized to the size of the components).
In case the error happens while browsing the page, it may be caused by a memory leak. You may use Process Explorer to watch the memory used by your browser and check if the memory constantly increase - which would indicate the memory leak.
Memory leak in Internet Explorer may happen because it contains 2 separate garbage collectors (aka GC): one for DOM objects and other for JS properties. Other browsers (FF, Webkit, Chromium, etc.; not sure about the Edge) contains only one GC for both DOM and JS.
So when you create circular reference between DOM object and JS object, IE's GC cannot correctly release the memory and creates a memory leak.
var myGlobalObject;
function SetupLeak()
{
myGlobalObject = document.getElementById("LeakDiv");
document.getElementById("LeakDiv").expandoProperty = myGlobalObject;
//When reference is not required anymore, make sure to release it
myGlobalObject = null;
}
After this code it seems the LeakDiv reference was freed but LeakDiv still reference the myGlobalObject in its expandoProperty which in turn reference the LeakDiv. In other browsers their GC can recognize such situation and release both myGlobalObject and LeakDiv but IE's GCs cannot because they don't know if the referenced object is still in use or not (because it's the other GC's responsibility).
Even less visible is a circular reference created by a closure:
function SetupLeak()
{
// The leak happens all at once
AttachEvents( document.getElementById("LeakedDiv"));
}
function AttachEvents(element)
{
//attach event to the element
element.attachEvent("onclick", function {
element.style.display = 'none';
});
}
In this case the LeakedDiv's onclick property references the handler function whose closure element property reference the LeakedDiv.
To fix these situations you need to properly remove all references between DOM objects and JS variables:
function FreeLeak()
{
myGlobalObject = null;
document.getElementById("LeakDiv").expandoProperty = null;
}
And you may want to reduce (or remove completely) closures created on DOM elements:
function SetupLeak()
{
// There is no leak anymore
AttachEvents( "LeakedDiv" );
}
function AttachEvents(element)
{
//attach event to the element
document.getElementById(element).attachEvent("onclick", function {
document.getElementById(element).style.display = 'none';
});
}
In both cases using try-catch is not the option because the Out of memory may happen on random places in code and even if you find one line of code where it's happened the next time it may be elsewhere. The Process Explorer is the best chance to find the situations when the memory increase and and trying to guess what may be causing it.
For example: if the memory increase every time you open and close the menu (if you have one) then you should look how it's being opened and closed and look for the situations described above.
You could check your localStorage before and after any components called.
Something like:
function getLocalStorage() {
return JSON.stringify(localStorage).length;
}
function addScript(src, log) {
if(log){
console.log("Adding " + src + ", local storage size: " + getLocalStorage());
}
var s = document.createElement( 'script' );
s.setAttribute( 'src', src );
document.body.appendChild( s );
}
function callFunction(func, log){
if(log){
console.log("Calling " + func.name + ", local storage size: " + getLocalStorage());
}
func();
}
try {
addScript(src1, true);
addScript(src2, true);
callFunction(func1, true);
callFunction(func2, true);
}
catch(err) {
console.log(err.message);
}
I hope it helps you. Bye.

How to access sidebar Bookmarks Panel page in SeaMonkey

I'm trying to convert my overlay add-on to restartless.
I can't access the bookmarks panel (on the sidebar) in SeaMonkey, in order to load my overlay UI.
Specifically, I need to do load my overlay to the bm-panel.xul similar to the following:
myListener.document.loadOverlay("chrome://myBookmarksPanelOverlay.xul");
For that, I need the window of bm-panel.xul but I only have the main window of the browser.
SeaMonkey has a different structure from Firefox, so the following example
var sidebarPanels = window.document.getElementById('sidebar');
which is in documentation, does not work for SeaMonkey.
I can see the bm-panel.xul window in the Dom Inspector, but I can't get to it with Javascript.
I was able to access only the sidebar panels but that's as far as I can go:
var sidebarPanels = window.document.getElementById('sidebar-panels');
How do I access the bookmarksPanel page itself?
I'm not 100% certain if your question is really limited to just finding the bookmarksPanel. Thus, this answer contains generic information about accessing the bookmarksPanel, viewing the DOM, accessing the sidebar from an overlay or restartless extension, and obtaining a window reference.
Accessing the bookmarksPanel
The following should get you a reference to the <page id="bookmarksPanel">:
var sidebarDocument = document.getElementById("sidebar").contentDocument;
var bookmarksPanelElement = sidebarDocument.getElementById("bookmarksPanel")
Note that you need to use the getElementById() in the sidebarDocument, not the main window.document.getElementById() which will not search into the sidebar.
Viewing the DOM
If you are having issues with knowing what the DOM structure is for a particular element, I would suggest that you install DOM Inspector (for SeaMonkey) (to view the DOM) and Element Inspector (which allows you to shift-right-click on an element and open the DOM Inspector on that element).
This is an example of the DOM Inspector viewing a Bookmark Sidebar in Firefox:
Accessing the Sidebar From an Overlay or Restartless Extension
Quoting from MDN: "Sidebar: Accessing the sidebar from a browser.xul script":
Accessing the sidebar from a browser.xul script
The sidebar content is always in a document separate from the main browser document (the sidebar is actually implemented as a XUL browser element). This means you can't directly access the sidebar content from a script referenced from a browser.xul overlay.
To access your sidebar's window or document objects, you need to use the contentWindow or contentDocument properties of document.getElementById("sidebar") respectively. For example the code below calls a function defined in the sidebar's context:
var sidebarWindow = document.getElementById("sidebar").contentWindow;
// Verify that our sidebar is open at this moment:
if (sidebarWindow.location.href ==
"chrome://yourextension/content/whatever.xul") {
// call "yourNotificationFunction" in the sidebar's context:
sidebarWindow.yourNotificationFunction(anyArguments);
}
Depending on how the current code you are running was started (e.g. UI button), you may need to obtain the current browser window.
Copying significantly from another answer of mine, you can obtain that by:
Obtaining a reference to the most recent window:
Firefox add-ons generally run in a scope where the global window object is not defined (if it is defined depends on how the portion of your code that is currently running was entered). Even if it is defined, it is often not defined as the window which you are expecting (the window of the current tab). You will probably need to obtain a reference to the window object for the most recently accessed window/tab.
If a browser window exists (in some instances you could be running where no browser window exists, yet, e.g. at start-up), you can obtain a reference to the most recent browser window, document, and gBrowser with:
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add/remove a "/" to comment/un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
//* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
if (typeof document === "undefined") {
//If there is no document defined, get it
var document = window.content.document;
}
if (typeof gBrowser === "undefined") {
//If there is no gBrowser defined, get it
var gBrowser = window.gBrowser;
}
If you are running the code in response to an event (e.g. a button command event), you can obtain the current window with:
var window = event.view
The lack of having the global window object available, or having it reference something other than what you are expecting, is something that many people encounter as a problem when writing Firefox add-ons.
Note: If you are wanting to be natively compatible with multi-process Firefox (Electrolysis, or e10s), then gaining access to the contents of the current document is more complex. There are shims in place which should make your code continue to work with multi-process Firefox for some time, but they may/will eventually go away.
References:
nsIWindowMediator
Working with windows in chrome code
SDK: window/utils
SDK: windows
Multiprocess Firefox
Working with multiprocess Firefox
Large portions of this were copied from my earlier answers, including this link.
This may not be the only way or the best, but here is what I am doing:
Find the sidebar element (sidebar-panels)
search the sidebar childNodes for the element with attribute src=chrome://communicator/content/bookmarks/bm-panel.xul
Searching the childNodes must be delayed until the relevant frames are loaded, and the search itself is a recursive iteration over all elements.
following is a minimal stripped code:
var sidebarE = domWindow.document.getElementById('sidebar-panels');
setTimeout(function() {
var sbPanelE = searchChildNodes(sidebarE);
}, 350);
function searchChildNodes (aElement) {
var stackNew = [];
var current;
var i, lenArr;
iterate(aElement);
function iterate(current) {
var childrenE = current.childNodes;
for (var i = 0, lenArr = childrenE.length; i < lenArr; i++) {
iterate(childrenE[i]);
foundE = checkElement(childrenE[i]);
if (e.nodeType == 1){ // Element node
if (e.getAttribute('src') == loc){
stackNew.push({ //pass args via object or array
element: childrenE[i],
});
return;
}
}
}
}
for (i=0;i<stackNew.length ;i++ ){
var itm = stackNew[i].element;
return itm;
}
} // searchChildNodes

In a Firefox Add-on how to resolve the error "window is not defined" when adding an eventlistener to the overall browser window?

I try to add an eventListener to my extension. I want to execute a function everytime a tab is active (got clicked by the user) or is new loaded.
I tried this:
window.addEventListener("DOMContentLoaded", checkHost(), false);
It gives me the error
Uncaught Reference Error: window is not defined
It drives me cracy, i can't find examples on the web. Please help me.
For those wanting to use the window object, you can create it using this code:
var { viewFor } = require("sdk/view/core");
var window = viewFor(require("sdk/windows").browserWindows[0]);
This code can be found on MDN at: https://developer.mozilla.org/en-US/Add-ons/SDK/High-Level_APIs/windows
Via your current status: http://builder.addons.mozilla.org/package/206579/latest
The online builder is an online IDE for developing with the Addon-SDK, where window isn't in global scope -- it's not any specific window.
You can include the tabs module and listen for ready events or activate (a tab is now focused) events, which may be what you want.
let tabs = require('sdk/tabs');
tabs.on('ready', function (tab) {
console.log(tab.url + ' is ready!');
});
The issue is that Firefox extensions do not run in the context of any particular window. As such, they often do not have the window object defined, or it is defined as something which you are not expecting if you are not familiar with writing extension code. This is particularly true if you are approaching this from the point of view of writing JavaScript for use within an HTML page. Extensions operate in a significantly larger context which includes the entire browser and all windows and tabs. Thus, there is no automatically appropriate window to use as the window object. In the context of an extension, each HTML page is just a part of the whole.
You can obtain each primary browser window through the use of nsIWindowMediator. The following function, from MDN, will run the function you pass to it once for each open window:
Components.utils.import("resource://gre/modules/Services.jsm");
function forEachOpenWindow(todo) // Apply a function to all open browser windows
{
var windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements())
todo(windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow));
}
You will often want to find the window for the most recent browser/tab which was accessed by the user. The following code will define and set the window variable to the most recently used browser/tab. It will work either in the Add-on SDK, or in overlay/bootstrap extensions depending on which portion you un-comment.
For more information about using windows in a Firefox extension, you should see Working with windows in chrome code.
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
Alternately using Services.jsm to access nsIWindowMediator:
/* Overlay and bootstrap:
Components.utils.import("resource://gre/modules/Services.jsm");
//*/
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window = Services.wm.getMostRecentWindow("navigator:browser");
//*/
}
You have to put the eventlistener inside a script referenced in the browser.xul overlay:
Try it like this:
window.addEventListener("DOMContentLoaded", function() {checkHost();}, false);

Is window.document ever null or undefined?

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.

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?

Categories

Resources