Targeting an ID that is rendered by a script - javascript

I'm using a nifty little script called Tabifier (http://www.barelyfitz.com/projects/tabber/)
Now, long story short, this script, which I run in my head tag, creates a <ul> with <li>s containing <a>s. Also in the head tag it creates IDs for these <a>s. When I inspect the loaded site I can clearly see the ID tags present. However, I cannot call them using getElementById. I've been using
<script>
document.getElementById('rightpanelnav1').style.padding='200px';
</script>
as a sample script in different parts of my code but to no avail. I'm wondering wether it's the placement or order in which these things are defined in my code that's causing it not to recognize the ID. What do you think?
EDIT: I recieved a great answer below, but I still can't get 'rightpanelnav1' to register onclick events...? It's an , there shouldn't be a problem, right? And when I click it, the entire page has been loaded for several seconds...

Firstly, in order to access an element in the DOM, the element must be a part of the DOM (document). So if you place your <script> with getElementById in the page at a place prior to where the element is loaded, it will not see the element in the DOM.
Secondly, it is highly probable that this library you use does its modification on page load, which would mean that no matter where you place your <script> it would have no chance of seeing these elements before running.
As a result, you should have your script wait as well, and do this:
window.onload = function(){
document.getElementById('rightpanelnav1').style.padding='200px';
};
Or for a click event
window.onload = function(){
document.getElementById('rightpanelnav1').onclick = function(){
alert("clicked!");
};
};

Related

Race condition when hooking appendChild

I've encountered a really weird race condition when hooking appendChild.
consider this code:
var RealAppend = IframeWindow.Element.prototype.appendChild;
IframeWindow.Element.prototype.appendChild = function(){
RealAppend.apply(this, arguments); //some more code here --- >};
I'm hooking some iframe appendchild that's why I'm doing "IframeWindow.Element.prototype.appendChild" with the IframeWindow as the iframe content window.
Now after I'm hooking the appendchild then I start to append nodes to the iframe , one of the nodes is a simple div element with id="somediv" , and another node is a javascript element and as soon as the javascript is executed it will use document.createElement to create an iframe and set its attributes and than it will execute document.getElementById('somediv').appendChild(CreatedIframe);
which will append the created iframe to the "somediv" , my goal here is to intercept the last appendchild and hook the appendchild of that iframe also.
now a first I thought that maybe I need to actually override the appendchild like this:
someDiv.appendChild = function(){.....
but after more tests i can see that sometimes my code does succeed and hooks the last iframe and sometimes the SomeDiv.appendChild is the native code and my code fails.
Any ideas why?
EDIT:
I think its might related to the fact that i'm using
document.createRange().createContextualFragment(strHTML);
}
to parse html as string and then append the nodes to the iframe so maybe the div (somediv) is getting a clean appendChild from there? but why sometimes it does work and sometimes not?
one other thing is that after the page loads and i'm using dev tools to get the somediv.appendchild i'm getting the hooked code every time so the problem happens only when the javascript code executes
So i managed to figure out what was the problem, so if anyone will ever encounter this here is the solution:
When i was parsing the html string to nodes so i can append them to the iframe i was first using the "DOMParser" API and i missed the fact that the DOMParser uses the document from the caller contentWindow so instead of calling DOMParser like this:
new DOMParser();
i needed to call it like that:
new HookedIframeContentWindow.DOMParser();

How can Javascript in the head access elements in the body?

I often see some JavaScript libraries that are called in the head tag of an html page. However, if these libs call an element in the body tag, there'll be a response (still from the head tag). When I try to call an element from the head, there is no response - why?
Thanks in advance. :)
The browser parses the html page from top to bottom, executing any <script> blocks in place as it finds them. Which means if the JavaScript attempts to access elements on the page it can only see those included higher in the page because the ones lower down have not been parsed yet.
There are two ways to deal with this:
Put your <script> block at the bottom, just before the closing </body> tag (or at least put it after the elements it needs to reference), and/or
Use an onload (or, if you like jQuery, a $(document).ready()) handler.
One way to setup an onload handler is like this:
<head>
<script>
window.onload = function() {
// this function will be called by the browser after
// the entire page has loaded and thus code in the function
// can access any element on the page.
};
</script>
</head>
<body>
... various elements ...
</body>
You may have also seen something like this:
<body onload="someFunction();">
Where someFunction() can be defined in a <script> block in the <head>. It's basically the same thing, but it's so 1990s to do it with an attribute in the html like that. (Actually even the window.onload() is out of date now, but it works and I don't have time to explain the .addEventListener() method.)
That's beacause you propably are trying to call elements before they exist, ie. the HTML isn't parsed yet. To avoid this, you have to wait untill the page is completed, and then execute your script.
To achieve this, you have to assign your function into a onload-event. For example:
<body onload="your_func">

'document' vs. 'content.document'

I'm trying to write a Firefox extension that adds elements to the loaded page. So far, I get the root element of the document via
var domBody = content.document.getElementsByTagName("BODY").item(0);
and create the new elements via
var newDiv = content.document.createElement("div");
and everything worked quite well, actually. But the problems came when I added a button with on onclick attribute. While the button is correctly displayed, I get an error. I already asked asked here, and the answer with document.createElement() (without content) works.
But if I remove the 'content.' everywhere, the real trouble starts. Firstly, domBody is null/undefined, no matter how I try to access it, e.g. document.body (And actually I add all elements _after_the document is fully loaded. At least I think so). And secondly, all other elements look differently. It's seem the style information, e.g., element.style.width="300px" are no longer considered.
In short, with 'content.document' everything looks good, but the button.onclick throws an error. with only 'document' the button works, but the elements are no longer correctly displayed. Does anybody see a solution for that.
It should work fine if you use addEventListener [MDN] (at least this is what I used). I read somewhere (I will search for it) that you cannot attach event listener via properties when creating elements in chrome code.
You still should use content.document.createElement though:
Page = function(...) {
...
};
Page.prototype = {
...
addButton : function() {
var b = content.document.createElement('button');
b.addEventListener('click', function() {
alert('OnClick');
}, false);
},
...
};
I would store a reference to content.document somewhere btw.
The existing answer doesn't have a real explanation and there are too many comments already, so I'll add another answer. When you access the content document then you are not accessing it directly - for security reasons you access it through a wrapper that exposes only actual DOM methods/properties and hides anything that the page's JavaScript might have added. This has the side-effect that properties like onclick won't work (this is actually the first point in the list of limitations of XPCNativeWrapper). You should use addEventListener instead. This has the additional advantage that more than one event listener can coexist, e.g. the web page won't remove your event listener by setting onclick itself.
Side-note: your script executes in the browser window, so document is the XUL document containing the browser's user interface. There is no <body> element because XUL documents don't have one. And adding a button won't affect the page in the selected tab, only mess up the browser's user interface. The global variable content refers to the window object of the currently selected tab so that's your entry point if you want to work with it.

Can I just put my javascript right next to my <div>

If I have some javascript/jQuery acting on a particular div inside tags (for an animation), can I just put the javascript (in a src="link-to-my-js.js" file) right next to my div?
I mean something like the following:
<body>
<div id="special">Some html</div>
<script type="text/javascript">javascript related to my div tag above...</script>
</body>
whereever you add your code wrap it with document .ready.
It will wait till all the dom is ready, so you will be safe.
<script type="text/javascript">
$(document).ready(function() {
});
</script>
You can put them anywhere you want, but
For performance reasons, it's best to put them at the bottom of the page.
DOM manipulation before the page has loaded can cause problems, especially with IE, for example this google maps problem.
You can add the tag anywhere you please. Remember though that if your script tries to act on an element that has not yet been loaded it will fail.
So if your load your script tag before your div and the script isn't activated by an onload event or something similar element will not be found.
On the contrary if the tag appears after the element you can manipulate it as normal.
It does not matter where you put it if you wrap your code with the $(function(){/*code here*/})
reference: http://api.jquery.com/jquery/#jQuery3

Manipulate href of anchor based on class in Prototype

I'm trying to manipulate some anchors after the document is loaded to alter their href attribute using Prototype. Based on the class of an anchor, its href has to be rewritten to be used by a tracking software.
I hacked some things together but can't quite figure out why it doesn't work. It always returns undefined, not matter what I search for.
<script type="text/javascript">
var elements = $$(".replace");
for (el in elements) {
el.href = "other_href.html";
}
</script>
Link
When I alert(el) in the loop, it returns either undefined or (when I search for a.replace an extraordinary number of elements that don't even exist on my page.
Where is my thinking error in here?
Untested:
<script type="text/javascript">
document.observe('dom:loaded', function() {
$$(".replace").each(function(a) {
a.writeAttribute('href', 'other_href.html');
});
});
</script>
Link
I'm guessing your JavaScript was loaded and executed before the HTML it was supposed to manipulate was loaded by the browser. It can't manipulate what doesn't exists yet. That's where observing events comes in handy. In this case, we're waiting for the DOM to finish loading, but before the page is rendered by the browser, to make our changes.
I also took advantage of Prototype's each() functionality. It's a great way to loop through arrays and any enumerables. And writeAttribute() is a good cross-browser way to modify element attributes.

Categories

Resources