DOM children & events

D

DaRik

Hi,

I'm having some difficulties with a menu I'm making. I build up the
menu through DOM. I append childnodes to a tree. 2 types of children
are possible: url (a hyperlink) and sub (a submap).

The tree is thus of a structure

main
|-url1
|-url2
|-submap
|--url21
|--url22

To open up the submaps I set the onclick event to a function I
created:

itemnode.onclick=function() { openMap(itemnode) }

The problem now is that 1 click in a submap results in several times
the openmap function. A click on the url21 node is in fact a click on
a child of submap so the function in the onclick event of this submap
is called. But it is also a click on a child of main, so this onclick
function is also called.

How can you limit that the onclick event handler is only called on the
1 node (ic. submap) where you actually clicked on submap instead of
its urls?

Full code listing below.

Thanks in advance

Rick


---------------------------------

menu.js



/* file: <title>menu.js</title> */
var content = [];
var this_menu;
show_menu();
function show_menu(){
init();
}
function init(){
var format = TREE_NODES.format;
var top = format.top;
var left = format.left;
var width = format.width;
var height = format.height;
var background_color = format.background_color;
var node = document.getElementsByTagName("body").item(0);
var root = document.createElement('div');
root.style.position = "absolute";
root.style.top = top;
root.style.left = left;
root.style.width = width;
root.style.height = height;
root.style.backgroundColor = background_color;
node.appendChild(root);
content = TREE_NODES.sub;
for (var i=0; i < content.length; i++){
var contentNode = add(content);
root.appendChild(contentNode);
}
}
function add( item ){
var html = (item.html)?item.html:0;
var url = (item.url)?item.url:0;
var sub = (item.sub)?item.sub:0;
var itemnode;
itemnode = document.createElement('div');
itemnode.style.position = "relative";
itemnode.style.left = 10;

if (url){
var pic = document.createElement('img');
pic.setAttribute("src", "page.gif");
itemnode.appendChild(pic);

var urlnode = document.createElement('a');
urlnode.setAttribute("target", "main");
itemnode.appendChild(urlnode);
}
if (html){
var textNode = document.createTextNode(html)

if (sub) {
var pic = document.createElement('img');
pic.setAttribute("src", "open.gif");
itemnode.appendChild(pic);
}
if (urlnode) {
urlnode.appendChild(textNode);
}
else {

var mapnode = document.createElement('a');
itemnode.appendChild(mapnode);
itemnode.onclick=function() { openMap(itemnode) }
mapnode.appendChild(textNode);
}
if (url) urlnode.setAttribute('href',url);
// if (url)
// itemnode.onclick=function() { alert("test"); }

}
if (sub){
for (var j=0; j < sub.length; j++){
var dit = sub[j];
var subNode = add(dit);
itemnode.appendChild(subNode);
}
}
return itemnode;
}



function openMap(node){
alert("openMap");

// alert(node.childNodes.item(0).nodeName); // IMG of the map
// alert(node.childNodes.item(1).nodeName); // A of the map

try {
for(i=2 ; i < node.childNodes.length ; i++) {
if (node.childNodes.item(i).style.display == "none" ) {
node.childNodes.item(0).src = "open.gif";
node.childNodes.item(i).style.display="block";
}
else {
node.childNodes.item(0).src = "closed.gif";
node.childNodes.item(i).style.display="none";
}
}
}
catch (ex) {
alert("error: "+ex);
}
}



-------------------------------------

structure.js


/*
file: <title>structure.js</title>
This file contains the menustructure to be displayed with node
children.
*/
var TREE_NODES={
format:{
left: 10,
top: 10,
width: 200,
height: 300,
o_image: "open.gif",
c_image: "closed.gif",
p_image: "page.gif",
background_color: "#ffffff",
main_background_color: "#ffffff",
animation:1,
padding:2,
dont_resize_back:1
},
sub:
[ {html: 'x-html',
sub:[
{html:'w3c', url:'http://www.w3.org/TR/xhtml2/'},
{html:'dom', url:'http://www.w3.org/DOM/'},
{html:'submap',
sub:[
{html:'test1', url:'http://www.w3.org/DOM/'},
{html:'test2', url:'http://www.w3.org/DOM/'},
{html:'submap2',
sub:[
{html:'test21', url:'http://www.w3.org/DOM/'}
]}
]}]
},
{html: 'xml',
sub:[
{html:'xml', url:'http://www.w3.org/XML/'},
{html:'xslt',
sub:[
{html:'main page', url:'http://www.w3.org/Style/XSL/'},
{html:'tutorial', url:'http://www.renderx.com/tutorial.html'}
]}]}]}
 
M

Martin Honnen

DaRik said:
I'm having some difficulties with a menu I'm making. I build up the
menu through DOM. I append childnodes to a tree. 2 types of children
are possible: url (a hyperlink) and sub (a submap).

The tree is thus of a structure

main
|-url1
|-url2
|-submap
|--url21
|--url22

To open up the submaps I set the onclick event to a function I
created:

itemnode.onclick=function() { openMap(itemnode) }

The problem now is that 1 click in a submap results in several times
the openmap function. A click on the url21 node is in fact a click on
a child of submap so the function in the onclick event of this submap
is called. But it is also a click on a child of main, so this onclick
function is also called.

How can you limit that the onclick event handler is only called on the
1 node (ic. submap) where you actually clicked on submap instead of
its urls?

Events bubble up the tree from child nodes to parent nodes. DOM Level 2
as implemented in Mozilla, Netscape 6/7, Opera 7 allows you to stop the
propagation of an event e.g.
itemnode.onclick = function (evt) {
openMap(itemnode);
if (evt.stopPropagation) {
evt.stopPropagation();
}
}
IE doesn't support that but has a property called cancelBubble that you
can set to true:
itemnode.onclick = function (evt) {
openMap(itemnode);
if (evt.stopPropagation) {
evt.stopPropagation();
}
if (window.event) {
window.event.cancelBubble = true;
}

Alternatively you could only assign an event handler to the parent
element and then check the event.srcElement/evt.target in your code.
 
M

Mike

The problem you are describing is the result of "event bubbling". Event
bubbling allows parent elements to respond to events fired by their
desendents.

In your case your onclick routine needs to be slightly more intelligent and
only process the node when necessary. Here is the processing logic I would
implement:

1. get a reference to the source element (the element that fired the event)
2. if the source element is not an A element it must be a subnode so
open/close the subnode
3. cancel the bubble
4. exit

for IE the code would look like

function openMap(node){

if (event.srcElement.tagName != "A"){

try {
for(i=2 ; i < node.childNodes.length ; i++) {
if (node.childNodes.item(i).style.display == "none" ) {
node.childNodes.item(0).src = "open.gif";
node.childNodes.item(i).style.display="block";
}
else {
node.childNodes.item(0).src = "closed.gif";
node.childNodes.item(i).style.display="none";
}
}
catch (ex) {
alert("error: "+ex);
}
}

event.cancelBubble = true;
return;

}
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top