Dynamically building objects

S

sirsean

Hi all. I'm trying to dynamically build menus and objects after my page
loads. Data is stored in an XML file and is parsed at runtime into
Javascript objects. At the moment, I'm working on creating menu items
from these objects. The parsing works fine (using Sarissa), and Firefox
builds the menu no problem. IE, however, does not. The functionality of
the menu will be a single onclick event. It seems that Firefox allows
me to set the onclick event handler for something built dynamically,
but IE won't.

Does anyone have any information on this, or a possible alternate
solution? Thanks.
 
R

Richard Cornford

Hi all. I'm trying to dynamically build menus and objects
after my page loads. Data is stored in an XML file and is
parsed at runtime into Javascript objects.

That is a bad idea. XML is sub-optimal as a vehicle for transmitting
data to an ECMAScript enabled client. JSON is much better as the
ECMAScript interpreter can parse that directly to JS objects.
At the moment, I'm working on creating menu items from
these objects. The parsing works fine (using Sarissa), and
Firefox builds the menu no problem. IE, however, does not. The
functionality of the menu will be a single onclick event. It
seems that Firefox allows me to set the onclick event handler
for something built dynamically, but IE won't.

IE provides two independent mechanisms for attaching event handlers to
dynamically created DOM elements.
Does anyone have any information on this, or a possible alternate
solution? Thanks.

Stop using the - setAttribute - method to attach event handlers as when
that works it is just an unspecified side effect. Attach event handlers
by assigning function references to the event handling properties of the
DOM elements, or use a branching W3C - addEventListener -/ IE -
attachEvent - approach.

Richard.
 
S

sirsean

Interesting. I hadn't thought of using addEventListener. However, I
have now tried it, and am using the "click" event for these menu items,
but it does not work. It fires off the event immediately (the function
gets called as the page loads), which it shouldn't do, and then
clicking on the item does nothing. Is there some trick to using
addEventListener? Haven't tried attachEvent yet for IE, and won't until
I get it working in Firefox first.

As for XML and JSON, I'm afraid I have to use XML for this particular
project. It's a contract with another software company, and they want
to use XML.

Thanks.
 
M

Matt Kruse

Richard said:
That is a bad idea.

That is a ridiculously unjustified conclusion.
XML is sub-optimal as a vehicle for transmitting
data to an ECMAScript enabled client.

Perhaps if you define sub-optimal by only considering the size of the data
delivered.

Other factors which may contribute to picking the "optimal" solution
include:
1) Ease of interacting with an existing system
2) Ease of interacting with other developers/companies
3) Use of company-approved technologies or components
4) The requirement to validate the structure of the data using a DTD

Most developers are probably familiar with XML and the tools used to
manipulate it.
Most developers have probably never heard of JSON (non of my co-workers had
when I suggested it recently).

Therefore, saving an extra few kb of data transmission by using JSON might
not be worth it when you consider all the other real-world factors.
JSON is much better as the
ECMAScript interpreter can parse that directly to JS objects.

Which may not be the desired functionality, in which case you need to write
code to parse the objects.
 
R

Richard Cornford

Interesting. I hadn't thought of using addEventListener.
However, I have now tried it, and am using the "click"
event for these menu items, but it does not work. It
fires off the event immediately (the function gets
called as the page loads), which it shouldn't do, and
then clicking on the item does nothing. Is there some
trick to using addEventListener?

You are using a CallExpression for the assignment where you should be
using a MemberExpression.

(
CallExpression production (ECMA 262 3rd edition; section 11.2.3):-

CallExpression : MemberExpression Arguments

MemberExpression production (ECMA 262 3rd edition; section 11.2):-

MemberExpression:
PrimaryExpression
FunctionExpression
MemberExpression [ Expression ]
MemberExpression . Identifier
new MemberExpression Arguments
)
Haven't tried attachEvent yet for IE, and won't
until I get it working in Firefox first.

As for XML and JSON, I'm afraid I have to use XML for this
particular project. It's a contract with another software
company, and they want to use XML.

A pity you couldn't involve anyone with pertinent technical skills in
the negotiation of the contract. That is a very risky approach to
contracts because you may find yourself contracted to do the impossible,
and so unable to deliver.

Richard.
 
S

sirsean

Well, I've fixed the problem I was just having with addEventListener
(that being that I was attempting, foolishly, to test it out using
"alert" and it wasn't working because I had to write a separate
function that is called when the event is fired).

Unfortunately, now the problem I've come across is that when the event
is fired, it is passed exactly one argument, which is an object (do you
perchance know what that object is and what's in it?). I need to know
the id of the menu item that is clicked, so I tried to use this.id in
the event function. This works fine in Firefox, but in IE, this.id is
apparently undefined. What is some way to get around that?

And I've read into JSON a bit, and it doesn't seem that it's perfect
for every scenario. We get the data out of a database (that we the
developers know nothing about) in the form of an XML file that is
validated against a well-formed XML Schema definition. JSON doesn't do
that, nor can the database in question easily output data in JSON
format. The parsing is either on the server or the client, and we don't
get to know anything about the server in this case. So XML it is.

Thanks,
SEAN
 
R

Richard Cornford

Matt said:
That is a ridiculously unjustified conclusion.


Perhaps if you define sub-optimal by only considering
the size of the data delivered.

Who is only considering the size of the download? The processing of that
XML on the client into a form that can be employed is a significant
factor. With JSON you are 100% guaranteed to have a native-code
client-side parser available whenever you are going to be in a position
to do something with the data.
Other factors which may contribute to picking the
"optimal" solution include:

Is this you usual marketing-speak employment of the term 'solution' or
its normal English meaning? (it is difficult to tell with you)
1) Ease of interacting with an existing system
2) Ease of interacting with other developers/companies

XML sent to a client has to satisfy same origin policy so the output
format for the client is not limited by data interchange formats. The
only exception that I can think of pre-existing web services using SOAP,
as a reason for preferring XML.
3) Use of company-approved technologies or components

Only significant if the person doing the approving doesn't have the
sense to see through the buzzwords and understand why they are choosing
technologies.
4) The requirement to validate the structure of the
data using a DTD

And how often are you going to be doing that on the client? And if you
were you would be better off validating against a Schema as that avoids
having yet another client-side parser to interpret the DTD.
Most developers are probably familiar with XML and the
tools used to manipulate it.

Such as writing an XSLT to output JSON from it.
Most developers have probably never heard of JSON
(non of my co-workers had when I suggested it recently).

Server-side programmers in other languages have no reason to know about
JSON. That doesn't mean they cannot be persuaded that it is the best
format for sending date to client-side ECMAScript. If they understand
XML they are not going to have much trouble understanding comma
separated name value pairs wrapped in braces.
Therefore, saving an extra few kb of data transmission
by using JSON might not be worth it when you consider
all the other real-world factors.

You are only considering the size of the download. Once downloaded it is
still necessary to process the data into a useable form. The tools for
that with JSON are native to all ECMAScript interpreters, for XML the
browser may or may not provide some (and if it does they will likely
differ between browsers) and if it does not you have to download and
execute an XML parser implemented in client-side code.
Which may not be the desired functionality, in which case
you need to write code to parse the objects.

What are you talking about? If the objects are not already in the
desired form (and with JSON they can be in exactly the required form,
which is unlikely to ever be true of an XLM DOM) all you have to do is
read the pertinent data from the JS objects. That is no worse than
extracting the data from XML nodes, and potentially much better.

Richard.
 
R

Richard Cornford

Well, I've fixed the problem I was just having with
addEventListener (that being that I was attempting,
foolishly, to test it out using "alert" and it wasn't
working because I had to write a separate
function that is called when the event is fired).

At some point it might occur to you that if you post some code people
might have some idea of what you are talking about, and you might get
responses a little more specific to the situation.
Unfortunately, now the problem I've come across is that
when the event is fired, it is passed exactly one argument,
which is an object (do you perchance know what that object
is and what's in it?).

It is the event object.
I need to know the id of the menu item that is clicked,
so I tried to use this.id in the event function.

Please don't tell us that you are going to use the ID with
getElementById to look up a reference to the Element.
This works fine in Firefox, but in IE,
this.id is apparently undefined.
<snip>

In what sense do you mean 'in IE'? IE doesn't support -
addEventListener - at all.
What is some way to get around that?

Almost certainly.

I bet the best solution will be my first suggestion of assigning a
function reference to the onclick property of the dynamically created
DOM element. That gives (near [1]) identical behaviour on the two
browsers you appear to be interested in (and many others) .

Richard.

[1] The difference being that Mozilla passes the event object to the
event handler, while IE makes it available as a global variable.
 
S

sirsean

Maybe you should have read the rest of the thread before you got pissy.
The whole issue was that your suggestion of using the DOM onclick
property *doesn't work*. So I was looking for alternate ways to do it.

Here's some code:
....
var a = document.createElement("A");
a.setAttribute("onclick", "alert(1);");
....

That works fine in Mozilla, but not in IE. The function is never
called. It was suggested that I try using addEventListener (attachEvent
in IE, look up a few posts, that's what I "meant" by "in IE").

That explanation should catch you up. Too bad I had to waste my time
explaining that when you could have read it. If you want to tell me the
same thing, point me to where on this page it says a viable alternate
solution to this problem. Please. Do it. No?

Fine, then I'll tell you why I need the ID. It's not to use
getElementById to look up a reference, because that not only is
completely unnecessary, but also pretty dumb. Part of the ID is a
number that I need to extract and use. It's of the form "button_XXX"
where XXX is some number. I simply extract the XXX out and have the
number I need. "In IE" it doesn't work ("in IE" meaning "using Internet
Explorer as my web browser"), because this.id has no value in the
function called by addEventListener/attachEvent. Mozilla handles it
fine.

And drop the JSON vs XML crap, it's not relevant.
 
R

Richard Cornford

Maybe you should have read the rest of the thread before
you got pissy. The whole issue was that your suggestion
of using the DOM onclick property *doesn't work*.

Yes it does. It is the most widely supported and reliable approach
available.
So I was looking for alternate ways
to do it.

Here's some code:
...
var a = document.createElement("A");
a.setAttribute("onclick", "alert(1);");
...
Too bad I had to waste my time explaining that when
you could have read it. If you want to tell me the
same thing, point me to where on this page

This page?
it says a viable
alternate solution to this problem.

Perhaps you should go back and re-read the thread.
Please. Do it. No?
<snip>

Make me. ;-)

Richard.
 
R

RobG

Maybe you should have read the rest of the thread before you got pissy.
The whole issue was that your suggestion of using the DOM onclick
property *doesn't work*. So I was looking for alternate ways to do it.

Here's some code:
...
var a = document.createElement("A");
a.setAttribute("onclick", "alert(1);");
...

var a = document.createElement("A");
a.onclick = function() {alert('1');};
...

Will do the trick, or:

function sayHi(el) {
alert( ( el.id )? el.id : 'I have no ID' );
}

...
var a = document.createElement('A');
a.onclick = function () {sayHi(this)} ;
...

or:

function sayHi(e) {
e = e || window.event;
var el = e.target || e.srcElement;
alert( ( el.id )? el.id : 'I have no ID' );
}

...
var a = document.createElement('A');
a.onclick = sayHi;
...

Please advise which of the above does not work in both IE and any other
common UA?


[...]
 
V

VK

There was an extensive discussion on the event matter here:
<http://groups-beta.google.com/group...9630b81dee9/cd81fab7ffdfadd7#cd81fab7ffdfadd7>

They tried so hard, to convince me that there was NO problem of event
source traking in programmed handlers, so I almost believed it :)

This works on any nesting deep, even with links inside:

function addClickListener(obj) {
if (obj.addEventListener) {
// To avoid multiple bubbles in FF,
// we're capturing events on the up->down (capturing) phase,
// so the bubbles history doesn't matter to us.
// Also we're using function wrapper to pass the object
// to the event handler:
obj.addEventListener('click', function(){myFunction(obj);}, true);
}
else if (obj.attachEvent) {
// We're using function wrapper to pass the object
// to the event handler:
obj.attachEvent('onclick', function(){myFunction(obj);});
}
else {
// Some under-done wannabe browser, just skip on it ?
}
}


function myFunction(obj) {
// Do what you want with obj, this is the right one - 100% guarantee
:)
}

If you need to handle the event object as well (say, cancel it), you
need to update it as follow:

obj.addEventListener('click', function(e){myFunction(obj, e);}, true);
obj.attachEvent('onclick', function(){myFunction(obj, null);});

function myFunction(obj, evt) {
// Do what you want with obj, this is the right one - 100% guarantee
:)
// Do what you want with evt event, just remember that in FF
// this event is captured at the downfall phase:
// 1. You have to use stopPropagation() instead of cancelBubble to
cancel the event
// 2. If you do so, any underlaying elements like links will never
receive the event
}

Faced quod potui, faciant meliora potentes...
 
R

Richard Cornford

RobG wrote:
function sayHi(e) {
e = e || window.event;
var el = e.target || e.srcElement;
alert( ( el.id )? el.id : 'I have no ID' );
}

...
var a = document.createElement('A');
a.onclick = sayHi;
...
<snip>

As functions assigned to event handling properties of DOM elements are
executed as methods of those DOM elements there is no need to worry
about the event's target in order to get a reference back to the DOM
element to which the handler is attached. That reference will always be
available as the - this - keyword. Hence preferring this approach over
the IE attachEvent method, where the handler is executed in the global
context and so special handling is required for IE.

Richard.
 
R

RobG

Richard said:
RobG wrote:


<snip>

As functions assigned to event handling properties of DOM elements are
executed as methods of those DOM elements there is no need to worry
about the event's target in order to get a reference back to the DOM
element to which the handler is attached. That reference will always be
available as the - this - keyword. Hence preferring this approach over
the IE attachEvent method, where the handler is executed in the global
context and so special handling is required for IE.

In other words...

function sayHi() {
alert( ( this.id )? this.id : 'I have no ID' );
}

...
var a = document.createElement('A');
a.onclick = sayHi;
...


will suffice. Thanks.
 
V

VK

an updated variant taking into account the newly discovered array
referencing bug... sorry... "closure's feature" of both browsers. The
event attacher is moved out of the main loop.


<html>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">

<script type="text/javascript">
<!--

function init() {
var arr = document.getElementsByTagName('DIV');
for (i=0; i<arr.length; i++) {
addClickListener(arr);
}
}

function addClickListener(obj) {
if (obj.addEventListener) {
obj.addEventListener('click', function(e){clickHandler(obj,e);},
true);
}
else if (obj.attachEvent) {
obj.attachEvent('onclick', function(){clickHandler(obj,null);});
}
else {
// Some under-done browser.
}
}

function test(e) {
alert(this);
}

function clickHandler(obj, evt) {
var e = (evt!=null)? evt : event;
var t = (evt!=null)? e.target : e.srcElement;

// if we need to disable underlaying links / form elements:
(evt!=null)? e.preventDefault() : e.returnValue=false;

alert('Click captured by '+ obj.id.toUpperCase() + '\n\n'+
'This click originated from ' + t.id.toUpperCase());
}
//-->
</script>
</head>

<body bgcolor="#FFFFFF" onload="init()">
<div id="div1">DIV1 <span id="span1">SPAN1 <a id="a1"
href="bogus1.htm"> A1 </a></span></div>
<div id="div2">DIV2 <span id="span2">SPAN2 <a id="a2"
href="bogus2.htm"> A2 </a></span></div>
<div id="div3">DIV3 <span id="span3">SPAN3 <a id="a3"
href="bogus3.htm"> A3 </a></span></div>
</body>
</html>
 
R

Richard Cornford

RobG said:
Richard Cornford wrote:
In other words...

function sayHi() {
alert( ( this.id )? this.id : 'I have no ID' );
}

...
var a = document.createElement('A');
a.onclick = sayHi;
...

will suffice. Thanks.

Yes. But there is still the question of what the ID is going to be used
for. It is a bit depressing to recall the number of times I have seen
people reading - this.id - and then using the ID to look up a reference
to the element with - document.getelementById -, without ever seeing
that - this - was (and must be) the value they wanted in the first
place.

Richard.
 
S

sirsean

Actually that question is now moot. I only needed to be able to get the
id because I needed the number in it. Rob's post helped tremendously,
and now I can use the DOM onclick property (who would have thought
wrapping it in function(){} would have done the trick?). Therefore, the
id isn't needed any more and everything is working swimmingly.

Thanks for the help, all.
SEAN
 

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,774
Messages
2,569,596
Members
45,143
Latest member
SterlingLa
Top