In my Firefox Addon's overlay.xul, can I access it's DOM in javascript? I can't figure out how.
Thanks in advance.
An overlay is merged with the DOM of the document that it applies to, it doesn't have a DOM of its own. So you don't access the DOM of "the overlay", you access the DOM of the document that you overlaid. And that is being done the usual way, e.g. via document.getElementById(). You have to consider one thing however: never access the DOM before the document finished loading, this will cause various issues (like other overlays failing to apply). So if your overlay includes a script you can write:
window.addEventListener("load", function() {
// Window finished loading, now we can do something
var button = document.getElementById("my-extension-button");
button.style.backgroundColor = "green";
}, false)
Related
Even though it is not recommended, JavaScript allows you to access elements by their id without the use of getElementById.
<iframe id="myframe" />
<script>
var i = window.myframe;
</script>
This presents a problem for extensions that need to access said elements when they themselves are being accessed. The way to do this is to call Object.defineProperty() on the prototype for getElementById so that when it is called, you can then access the element. But when referencing an element directly getElementById is not called so consequently, they are a blind spot for the extension. I've tried the following but without any luck.
Iterating over the DOM in the content script doesn't seem to work because the full DOM isn't loaded yet.
Setting a listener for 'DOMContentLoaded' isn't an option because by that time the page's scripts would have already run and potentially accessed the elements.
Attempting to insert the script before the first script on the page but again, at that point nothing really exists yet.
Does anyone know the internal mechanism that JavaScript uses when an element is referenced like this? I'm curious if there is another function that it calls internally which could possibly be hooked like getElementById. Or is there a way to ensure the content script code is run after the DOM loads but before the page scripts runs?
Or is there a way to ensure the content script code is run after the DOM loads but before the page scripts runs?
A problem is that page scripts can run before the DOM is fully loaded. For example, below:
<div id="foo"></div>
<script>
foo.textContent = 'foo';
</script>
<div id="bar"></div>
the script will run before the bar element is created (before the DOM is loaded).
It's possible to find all elements with IDs currently in the DOM with [id], and it's possible to reassign those properties on the window by simply reassigning the property - or, if you want to run code when the element is retrieved, you can use Object.defineProperty to turn it into a getter. You need to be able to find the IDs and reassign them before scripts load, which can be accomplished with a subtree MutationObserver - right before a <script> is inserted, iterate over the [id]s in the DOM, and reassign them:
console.log('(empty JS block inserted by Stack Snippet editor)');
<script>
new MutationObserver((mutations) => {
// Check to see if a script was inserted
if (mutations.every(
mutation => [...mutation.addedNodes].every(
addedNode => addedNode.tagName !== 'SCRIPT'
)
)) {
// No scripts inserted
return;
}
console.log('id reassignment running');
for (const elm of document.querySelectorAll('[id]')) {
Object.defineProperty(window, elm.id, {
get() {
console.log('Custom code running');
return elm;
},
configurable: true
});
}
})
.observe(document.body, { childList: true, subtree: true });
</script>
<div id="foo"></div>
<script>
console.log('Lower script about to reference foo');
foo.textContent = 'foo';
console.log('Lower script has finished referencing foo');
</script>
<div id="bar"></div>
It can benefit from some polishing, but that's the general idea.
If in a web page I load some content after the initial page load and DOM ready event, when are the inserted nodes "ready", in the sense of having their sizes computed, taking CSS rules into consideration and all?
$.ajax({
url: ajaxUrl,
success: function (data) {
var page = $(data).find('.page').first();
page.appendTo(pages_container);
// if I try to get the width of an element here, I get 0
console.log(page.width()); // -> 0
// but if I do it, let's say 500ms later, now it is computed
setTimeout(function () {
console.log(page.width()); // -> correct width, non zero
}, 500);
}
});
Is there any kind of event, like "on inserted dom ready" or something to be able to execute a function after the sizes/layout of the ajax inserted content has been computed?
Your problem should not occur as you outline. Appending elements into a page is not an asynchronous operation. Javascript DOM manipulation is not multi-threaded. It simply returns after the DOM changes have been applied.
The DOM should be ready to use as soon as your append completes (including size properties):
page.appendTo(pages_container);
The usual cause is accessing the properties before the jQuery object is inserted, or that the new jQuery object is visually hidden and revealed later (fadein etc), but I see no transitions in your code.
Questions/thoughts:
Can you mock up a JSFiddle to replicate your problem?
Also check for other plugins etc that could modify your DOM after insertion.
Is there JS code in the inserted page?
Also what browser did this occur with?
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.
I have a div which contains a number of images. I would like to check if all the images in this div are loaded before executing some code. I can't work it off the entire document as there are other things on the page which may take much longer to load.
How can I do this? jquery is available for use.
You should sue the load event
$('img').load(function(){
//here do what you need to do when the img is loaded
});
there are some caveats
Caveats of the load event when used with images
A common challenge developers attempt to solve using the .load()
shortcut is to execute a function when an image (or collection of
images) have completely loaded. There are several known caveats with
this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache
You can do sth like this:
var files = ['a.jpg','b.png']; //defines all assets needed
$.each(files,function(){
var tmp = new Image();
tmp.src = this; //creates a new dummy object
tmp.on('load',function(){
var i = files.indexOf(this);
files.splice(i,1); //when dummy object has loaded it gets removed from array
if (!files.length){ //when array is empty we're all set
alert('Preloading done!');
}
});
});
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).