I have a problem, which is not easily described. I'm writing a web application that makes strong usage of jQuery and AJAX calls. Now I don't have a lot of experience in Javascript archicture, but I realize that my program has not a good structure. I think I have too many identifiers referring to the same (at least more or less) thing.
Let's have an look at an arbitrary exemplary UI widget that makes up a tiny part of the application: The widget may be a part of a window and the window may be a part of a window manager:
- The eventhandlers use DOM elements as parameters. The DOM element represents a widget in the browser.
- A lot of times I use jQuery objects (Basically wrappers around DOM elements) to do something with the widget. Sometimes they are used transiently, sometimes they are stored in a variable for later purposes.
- The AJAX function calls use string identifiers for these widgets. They are processed server side.
- Beside that I have a widget class whose instances represent a widget. It is instantiated through the new operator.
Now I have somehow four different object identifiers for the same thing, which needs to be kept in sync until the page is loaded anew. This seems not to be a good thing.
Any advice?
EDIT:
@Will Morgan: It's a form designer that allows to create web forms within the browser. The backend is Zope, a python web application server. It's difficult to get more explicit as this is a general problem I observe all the time when doing Javasscript-development with the trio jQuery, DOM tree and my own prototyped class instances.
EDIT2:
I think it would helpful to make an example, albeit an artificial one. Below you see a logger widget that can be used to add a block element to a web page in which logged items are displayed.
makeLogger = function(){
var rootEl = document.createElement('div');
rootEl.innerHTML = 'Logged items:';
rootEl.setAttribute('class', 'logger');
var append = function(msg){
// append msg as a child of root element.
var msgEl = document.createElement('div');
msgEl.innerHTML = msg;
rootEl.appendChild(msgEl);
};
return {
getRootEl: function() {return rootEl;},
log : function(msg) {append(msg);}
};
};
// Usage
var logger = makeLogger();
var foo = document.getElementById('foo');
foo.appendChild(logger.getRootEl());
logger.log('What\'s up?');
At this point I have a wrapper around the HTMLDivElement (the hosted object). With having the logger instance (the native object) at hand I can easily work with it through the function logger.getRootEl().
Where I get stuck is when I only have the DOM element at hand and need to do something with the public API returned by function makeLogger (e.g. in event handlers). And this is where the mess starts. I need to hold all the native objects in a repository or something so that I can retrieve again. It would be so much nicer to have a connection (e.g. a object property) from the hosted object back to my native object.
I know it can be done, but it has some drawbacks:
- These kind of (circular) references are potentially memory leaking up to IE7
- When to pass the hosted object and when to pass the native object (in functions)?
For now, I do the back referencing with jQuery's data() method. But all in all I don't like the way I have to keep track of the relation between the hosted object and its native counterpart.
How do you handle this scenario?
EDIT3:
After some insight I've gained from Anurag's example..
@Anurag: If I've understood your example right, the critical point is to set up the correct (what's correct depends on your needs, though) execution context for the event handlers. And this is in your case the presentation object instance, which is done with Mootool's bind() function. So you ensure that you're ALWAYS dealing with the wrapper object (I've called it the native object) instead of the DOM object, right?
A note for the reader: You're not forced to use Mootools to achieve this. In jQuery, you would setup your event handlers with the $.proxy() function, or if you're using plain old Javascript, you would utilize the apply property that every function exposes.