Dynamically building objects
|
|
Thread rating:  |
sirsean@gmail.com - 29 May 2005 22:38 GMT 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.
Richard Cornford - 29 May 2005 23:01 GMT > 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.
sirsean@gmail.com - 29 May 2005 23:48 GMT 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.
Richard Cornford - 30 May 2005 01:04 GMT > Interesting. I hadn't thought of using addEventListener. > However, I have now tried it, and am using the "click" [quoted text clipped - 3 lines] > 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.
sirsean@gmail.com - 30 May 2005 01:34 GMT 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
Richard Cornford - 30 May 2005 02:19 GMT > 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.
sirsean@gmail.com - 30 May 2005 03:04 GMT 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.
Richard Cornford - 30 May 2005 03:31 GMT > 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. [quoted text clipped - 4 lines] > a.setAttribute("onclick", "alert(1);"); > ... <snip>
> 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.
RobG - 30 May 2005 06:04 GMT > 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 [quoted text clipped - 5 lines] > 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?
[...]
 Signature Rob
Richard Cornford - 30 May 2005 13:31 GMT <snip>
> function sayHi(e) { > e = e || window.event; [quoted text clipped - 6 lines] > 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.
RobG - 30 May 2005 14:02 GMT > <snip> > [quoted text clipped - 18 lines] > 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.
 Signature Rob
Richard Cornford - 30 May 2005 14:45 GMT <snip>
>> As functions assigned to event handling properties ... <snip>
> In other words... > [quoted text clipped - 8 lines] > > 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.
sirsean@gmail.com - 31 May 2005 01:15 GMT 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
Matt Kruse - 30 May 2005 00:34 GMT > 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.
 Signature Matt Kruse http://www.JavascriptToolbox.com
Richard Cornford - 30 May 2005 02:02 GMT >> That is a bad idea. > [quoted text clipped - 5 lines] > 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.
>> 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. 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.
VK - 30 May 2005 08:39 GMT There was an extensive discussion on the event matter here: <http://groups-beta.google.com/group/comp.lang.javascript/browse_frm/thread/083ed 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...
VK - 30 May 2005 14:11 GMT 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[i]); } }
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>
|
|
|