FireFox/IE behavior diff during onload

K

KPS

I'm attempting to create a simple treeview-like behavior in JavaScript.
The desired behavior happens in IE but I cannot get the same to happen
in FireFox.

The primary thing I want to accomplish is to set the id and href of
some objects dynamically during the onload of the page instead of
hard-coding it in the HTML. I want to set the id/href so when I add new
entries I don't have to remember to increment the id's.

In FireFox, the href of the oAnchor and the id's for the oImage and
oList objects never get set (but the id for the oSpan object does).

Here is the code:

<head>
<style>
ul {
list-style : none;
}
a {
text-decoration : none;
}
img {
border-style : none;
margin: 0px 5px 1px 0px;
}
..closed ul {
display : none;
}
</style>
<script language="JavaScript" type="text/javascript">
function toggle(id) {
var oSpan = document.getElementById(id);
var oImage = document.getElementById("img" + id);
var oList = document.getElementById("list" + id);

if (oSpan.className == 'closed') {
oSpan.className = 'open';
oImage.src = "minus.gif";
oList.style.display = 'block';
}
else {
oSpan.className = 'closed';
oImage.src = "plus.gif";
oList.style.display = 'none';
document.getElementById("expandAll").checked = false;
}
}
function expandAll() {
var classname = "open";

if (document.getElementById("expandAll").checked) {
classname = "closed";
}

var oSpan = document.getElementsByTagName("span");
for (var i=0; i<oSpan.length; i++) {
if (oSpan.className == classname) {
toggle(oSpan.id);
}
}
}
onload = function() {
var classname = "closed"
var oSpan = document.getElementsByTagName("span");
for (var i=0; i<oSpan.length; i++) {
if (oSpan.className == classname) {
oSpan.id = i;
var oAnchor = oSpan.firstChild;
oAnchor.href = "javascript:toggle(" + i + ")"
var oImage = oAnchor.firstChild;
oImage.id = "img" + i;
var oList = oSpan.lastChild;
oList.id = "list" + i;
}
}
}
</script>
</head>

<table>
<tr>
<td>
<input type="checkbox" id="expandAll" onclick="expandAll();"
/>Expand All
</td>
</tr>
<tr>
<td>
<ul>
<li>
<span class="closed">
<a><img src="plus.gif" />TEST</a>
<ul>
<li><a href="http://www.google.com"><img src="square.gif"
/>FOO</a></li>
<li><a href="http://www.google.com"><img src="square.gif"
/>BAR</a></li>
</ul>
</span>
</li>
</ul>
</td>
</tr>
</table>
 
R

RobG

KPS said on 15/03/2006 10:03 AM AEST:
I'm attempting to create a simple treeview-like behavior in JavaScript.
The desired behavior happens in IE but I cannot get the same to happen
in FireFox.

Because of how you are trying to reference elements, see below.

The primary thing I want to accomplish is to set the id and href of
some objects dynamically during the onload of the page instead of
hard-coding it in the HTML. I want to set the id/href so when I add new
entries I don't have to remember to increment the id's.

You don't have to create IDs for them anyway, what's the point? You
don't seem to use them for anything.

In FireFox, the href of the oAnchor and the id's for the oImage and
oList objects never get set (but the id for the oSpan object does).

The reason is that the DOM tree is not as you expect - use the DOM
Inspector. Firefox (and other Gecko-based browsers) insert #text nodes
in the DOM to preserve whitespace in the source HTML. It is these nodes
that are messing with your firstChild/lastChild references.
Here is the code:

<head>
<style>

A type attribute is required.

ul {
list-style : none;
}
a {
text-decoration : none;
}
img {
border-style : none;
margin: 0px 5px 1px 0px;
}
.closed ul {
display : none;
}
</style>
<script language="JavaScript" type="text/javascript">

The language attribute is deprecated, drop it and keep type.

function toggle(id) {
var oSpan = document.getElementById(id);
var oImage = document.getElementById("img" + id);
var oList = document.getElementById("list" + id);

if (oSpan.className == 'closed') {
oSpan.className = 'open';
oImage.src = "minus.gif";
oList.style.display = 'block';

Do you really want 'block' or just not 'none'? You can set it to ''
(empty string) if you like to get the default style.

Since you change the class of the outer SPAN, there is no need to set
the display property of the UL anyway, you've already done that with CSS
(just remove the oList.style.display lines here and below and it's done).

}
else {
oSpan.className = 'closed';
oImage.src = "plus.gif";
oList.style.display = 'none';
document.getElementById("expandAll").checked = false;
}
}
function expandAll() {
var classname = "open";

if (document.getElementById("expandAll").checked) {
classname = "closed";
}

var oSpan = document.getElementsByTagName("span");

Rather than sift through all the span elements in the entire document,
get a reference to some element that is an ancestor of all the spans you
want, then use its getElementsByTagName() method. You should get a much
smaller list of spans.

for (var i=0; i<oSpan.length; i++) {
if (oSpan.className == classname) {


And if you add a second class to any of the elements? Maybe you never
will, but it's safer to use say:

var classRE = new RegExp('\\b' + classname + '\\b');
var span;
for (var i=0, len=oSpan.length; i<len; i++){
span = oSpan;
if (span.className && classRE.test(oSpan.classname){

toggle(oSpan.id);
}
}
}
onload = function() {
var classname = "closed"
var oSpan = document.getElementsByTagName("span");
for (var i=0; i<oSpan.length; i++) {
if (oSpan.className == classname) {


See above.

oSpan.id = i;


Setting an element's id to a number is not valid HTML (though most
browsers will tolerate it). An id can contain numbers, it just can't
start with a number.

var oAnchor = oSpan.firstChild;


You need to be careful here. *Any* whitespace between the SPAN and A
tags in the source HTML will introduce a #text node as the first child
of the SPAN in Gecko browsers (e.g. Firefox). IE does not do that, it
removes such whitespace completely.

oAnchor.href = "javascript:toggle(" + i + ")"

Do not use javascript in the href attribute, it causes unwanted
behaviour. Use an onclick attribute instead (or don't use an A element
at all)

var oImage = oAnchor.firstChild;

Given that there is no whitespace between your A and IMG tags, this
should work but you likely oAnchor is not a reference to the A element
that you expect.

oImage.id = "img" + i;

Here you will get an error message in the JavaScript console:

oImage has no properties


Which is because a #text node doesn't have an id attribute.

var oList = oSpan.lastChild;


Since there is whitespace after the closing UL tag, Gecko browsers will
add a #text node, so that is what oList will refer to.
oList.id = "list" + i;

a #text node can't have an ID attribute[1], the DOM object referenced by
oList may be assigned an id, but I'm not sure it will do you any good.

}
}
}
</script>
</head>

<table>
<tr>
<td>
<input type="checkbox" id="expandAll" onclick="expandAll();"
/>Expand All

Is this really XHTML? If not, don't use '/>' to close the tags of empty
elements.

</td>
</tr>
<tr>
<td>
<ul>
<li>
<span class="closed">

Use the DOM Inspector, you'll see a #text node here.
<a><img src="plus.gif" />TEST</a>

And here.


And here.

<li><a href="http://www.google.com"><img src="square.gif"
/>FOO</a></li>

And here, etc.
<li><a href="http://www.google.com"><img src="square.gif"
/>BAR</a></li>
</ul>
</span>
</li>
</ul>
</td>
</tr>
</table>

The trivial solution is to remove the whitespace between the opening
SPAN tag and the first A tag, and between the closing UL and SPAN tags.
But I think you can do better. Consider this simpler version:

<style type="text/css">
.closed ul { display : none; }
.open ul { display :list-item ;}
</style>

<script type="text/javascript">
function toggleClass(el)
{
el.className = (el.className == 'open')?'closed':'open';
}
</script>

<ul>
<li class="closed" onclick="toggleClass(this);"><a><img
src="plus.gif">TEST</a>
<ul>
<li><a href="http://www.google.com"><img
src="square.gif">FOO</a></li>
<li><a href="http://www.google.com"><img
src="square.gif">BAR</a></li>
</ul>
</li>
</ul>
<ul>
<li class="closed" onclick="toggleClass(this);"><a><img
src="plus.gif">TEST</a>
<ul>
<li><a href="http://www.google.com"><img
src="square.gif">FOO</a></li>
<li><a href="http://www.google.com"><img
src="square.gif">BAR</a></li>
</ul>
</li>
</ul>





1. #text nodes have no attributes, just a value which is the
content and one splitText method from DOM interface Text.

<URL:http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1312295772>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,739
Latest member
Clint8040

Latest Threads

Top