PDA

View Full Version : OO JS:How to use DOM properties/methods inside an user defined object.


appquest
05-20-2009, 11:09 PM
I'm new to Object Oriented JavaScript programming.

I was trying to create an user defined object, that would take in an element (eg. <td>).
I could then attach Event Handlers, change style, do anything with that element within that object. However, when I try to do the above, I get an error in both IE and FF.
(Will list the error after the code block).


function Cell (cell)
{
this.cell = cell;
this.id = cell.id;

this.changeBorder = function(mode)
{
if(mode == "select")
{
this.cell.style.borderColor = "black";
}
else
{
this.cell.style.border = "none";
}
};
}

var td = document.getElementById("r3c3");
var newcell = new Cell(td);
newcell.changeBackground("select");


According to the error on IE and FF, this.cell.style is null or not an object.

Since this.cell points to the element passed to the Cell constructor, shouldn't I be able to access all the DOM properties/methods of that element through this.cell?

I know I'm doing something fundamentally wrong here, just can't figure out what.
Any help would be great!

Also, since this is my first post here, please do let me know if I didn't post in the correct format.

Thanks!!

adios
05-20-2009, 11:37 PM
Works fine, as long as you call the 'changeBorder' method (defined) and not the 'changeBackground' method (undefined). And call the constructor after the TD is loaded.

btw you can already extend the HTMLElement Class in some browsers (FF, Opera, Safari), adding custom properties and methods, making it unnecessary to use a wrapper.

HTMLElement.prototype (http://books.google.com/books?id=GgJN2CC_2s4C&pg=PA84&lpg=PA84&dq=HTMLElement.prototype&source=bl&ots=7w1bhW7hLJ&sig=0BCiTz0AvczeWfgJkybf_grH3WE&hl=en&ei=CnYUSqS6MKOctgOGo4DpDQ&sa=X&oi=book_result&ct=result&resnum=9#PPA84,M1)

Good post! Welcome.

http://docstore.mik.ua/orelly/webprog/jscript/ch08_05.htm

rnd me
05-21-2009, 12:05 AM
function Cell (cell){
var that=this;
that.cell = cell;
that.id = cell.id;

that.changeBorder = function(mode)
{
if(mode == "select")
{
that.cell.style.borderColor = "black";
}
else
{
that.cell.style.border = "none";
}
};
return that;
}

var td = document.getElementById("r3c3");
var newcell = new Cell(td);
newcell.changeBackground("select");



pay attention to this and that.

ShadowIce
05-21-2009, 12:11 AM
Better yet: function Cell (cell){
var that=this;
that.cell = cell;
that.id = cell.id;

that.changeBorder = function(mode)
{
if(mode == "select")
{
that.cell.style.borderColor = "black";
}
else
{
that.cell.style.border = "none";
}
};
return that;
}

function getelementid(){

var mydiv = "r3c3";

var thediv = document.getElementById(mydiv);

return thediv;

}

var td = getelementid();
var newcell = new Cell(td);
newcell.changeBackground("select");

Good Luck!

~SI~

adios
05-21-2009, 07:32 AM
Q for rnd me: Just curious, but is it considered good form, when creating a closure, to then change all references to the owning object within the constructor, even if scope isn't an issue? Replace all references to 'this' with the function-scope var? I've generally reserved that for assigning properties where scope is an issue - typically, but not solely, HTML event handlers. With many custom objects, it's not an issue at all. Just curious if there's a reason, or a convention ...thanks.

rnd me
05-21-2009, 08:20 AM
Q for rnd me: Just curious, but is it considered good form, when creating a closure, to then change all references to the owning object within the constructor, even if scope isn't an issue? Replace all references to 'this' with the function-scope var? I've generally reserved that for assigning properties where scope is an issue - typically, but not solely, HTML event handlers. With many custom objects, it's not an issue at all. Just curious if there's a reason, or a convention ...thanks.

for the example's, it might not be needed.
you could really just use cell, as it should close the argument.
you could then even use variables and just name the method this.changeBorder.

but it's not very intuitive to most coders.


I added the "that" for clarity and future expansion.

it can be confusing for me trying keeping track of this at times.

It's probably most noticeable when dealing with any kind of late-bound or tree-shaped structure, sub objects and methods of them, etc.


I find the practice of duping this into that makes it easier to understand exactly what's happening where inside of constructors and object-bound functions (aka methods).

also, it allows wider options of .apply or .call 'ing the methods, as you can use both this and that inside them.

for me, it often means .map 'ping arrays with a second argument, while referring to the "main object" as that.

adios
05-21-2009, 08:45 AM
As long as I've got you here ....:cool: (sorry to the OP for the hijack); but, another Q:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>untitled</title>
<style type="text/css">
td {
width:50px;
height:40px;
padding: 16px;
border:3px red double;
font-size: smaller;
}
</style>
<script type="text/javascript">

/**************************************/

function Element(elementObj)
{
var obj = this;
obj.element = elementObj;
obj.id = obj.element.id;
obj.addListener = function(evt, handler)
{
if (obj.element.addEventListener)
{
obj.element.addEventListener(evt, handler, false);
}
else if (obj.element.attachEvent)
{
obj.element.attachEvent('on' + evt, handler);
}
}

obj.changeStyle = function(prop, val)
{
obj.element.style[prop]= val;
}

}

/**************************************/

var els = [];
els.add = function(id)
{
this[id] = new Element(document.getElementById(id));
}

window.onload = function()
{
els.add('r1c1');
els.add('r1c2');
els['r1c1'].addListener('click', function(){els['r1c1'].changeStyle('background', 'pink')});
els['r1c2'].addListener('click', function(){els['r1c2'].changeStyle('background', 'beige')});
}

</script>
</head>
<body>
<table cellspacing="4">
<thead>
<tbody>
<tr>
<td id="r1c1">r1c1<br /><br />click me</td>
<td id="r1c2">r1c2<br /><br />click me</td>
</tr><tr>
<td id="r2c1"><input type="button" value="change border" onclick="els['r1c1'].changeStyle('borderColor','black')" /></td>
<td id="r2c2"><input type="button" value="change text" onclick="els['r1c2'].changeStyle('color','blue')" /></td>
</tr>
</tbody>
</thead>
</table>
</body>
</html>

Is there any way to pass a handler function to the Element.addListener method that will put the custom object in its scope chain (so it isn't necessary to specify the object in the handler itself)? Tried wrappers here and there and it seemed never to be defined. Hope this isn't a stupid question, but you seem to eat this stuff for breakfast ...

rnd me
05-21-2009, 09:15 AM
function Element(elementObj)
{
var obj = this;
obj.element = elementObj;
obj.id = obj.element.id;


obj.addListener = function(evt, handler) {

function handler2(e){
e=e||window.event;
return handler.call(this, e, obj.element, obj )
}

if (obj.element.addEventListener) {
obj.element.addEventListener(evt, handler2);
} else if (obj.element.attachEvent) {
obj.element.attachEvent('on' + evt, handler2);
} else {
obj.element["on"+evt]=handler2;
}
}//end obj.addListener()

obj.changeStyle = function(prop, val)
{
obj.element.style[prop]= val;
}

}



handler2 let's us use a bonus arguments in the actual handler functions.

for example:

els['r1c2'].addListener('click', function(e, ob){ ob.changeStyle('background', 'beige')});

the first argument is the event object, guaranteed in all browsers.

the second argument is the element firing the event.

the third argument is the whole array, obj.

this refers to different things in different browsers (either the main object or the element), so use the arguments for specificity.

i didn't homogenize this, because doing so would prevent future .apply and .call possibilities from fully functioning.

does that help you out, or am i misunderstanding something ?


EDIT:
it's not all that much short however.
if you are going to be reuing a lot of parameters, wrapping cuts code length a lot:

function addClick(id, prop, val){
els[id].addListener('click', function(e, ob){ ob.changeStyle(prop, val);});
}

now, it's just:
addClick("r1c1", 'background', 'pink' );
addClick("r1c2", 'background', 'beige' );
to upgrade each element, which is a bit shorter than the original:
els['r1c1'].addListener('click', function(){els['r1c1'].changeStyle('background', 'pink')});
els['r1c2'].addListener('click', function(){els['r1c2'].changeStyle('background', 'beige')});

appquest
05-21-2009, 11:15 PM
Thanks so much everybody for your replies.
Changing this to that did help me understand better as to what goes on in the constructor.

Also, thanks adios for posting the question on the Event Handlers. I was going to ask that question next.:) Got my answer and my code now works!

Also thanks for the links on OO JavaScript - those were very helpful.

Thanks again! Great forum!