Efficiently detecting a click in a large table

S

squash+go

For an abstract algebra class that I will be teaching,
I'm trying to convert a text-based game that I wrote
in Common Lisp to a graphical game in javascript. (I
know nothing of javascript, but I have three books
from the library that I have been s*l*o*w*l*y going
through.)

Currently I have a large, say, 30x20, table

<table id="GameTable" >
<tr> <td>A</td> <td>B</td> <td>C</td> ... </tr>
<tr> <td>D</td> <td>E</td> <td>F</td> ... </tr>
...
</table>

I'm accessing elements via

var game_table = document.getElementById('GameTable');
var curr_cell;

function game_node(x,y) {
curr_cell = game_table.rows[x].cells[y];
return(curr_cell.firstChild);
};

and I'm able to write/read to

game_node(x,y).nodeValue

just fine. But here's where I'm stuck:

I'd like, when the user clicks on, say, cell (3,5),
that a function-call

UpdateGame(3,5)

is invoked. Is there a way I can do this NEITHER
having an "onclick=" NOR an "id=" for each and every
<td>? I seek something like having a single "id=" and
"onclick=" for the entire table, e.g,

<table id="GameTable"
onclick="UpdateGame(this.rowIndex, this.colIndex)">

where -somehow- variables rowIndex and colIndex were
automagically set by the mouse-click.

Ta, Jonathan King Math dept, Univ. of Florida
 
P

P. Prikryl

You can use event.target/event.srcElement to determine the clicked
node and check if it is inside the table. Then you can use cellIndex
and rowIndex properties of the cell and row to get the coordinates.

Simple sample (function to handle the click must be set after the
element id="GameTable" is loaded):
document.getElementById("GameTable").onclick = function(e) {
e = e || window.event;

for (var td = e.target || e.srcElement; td && td != this; td =
td.parentNode)
if (td.tagName == "TD" && td.parentNode.parentNode.parentNode ==
this) {
UpdateGame(td.parentNode.rowIndex, td.cellIndex);
break;
}
}
 
P

Peter Michaux

T

Thomas 'PointedEars' Lahn

Peter said:
For Google food, this has been termed "event delegation" meaning a
parent element is watching for events that bubble up from child
elements.

http://yuiblog.com/blog/2007/01/17/event-plan/
http://developer.yahoo.com/yui/examples/event/event-delegation.html

However, the technically correct no-buzzword term is "Event bubbling"
(mentioned here before) or, more generally, "Event dispatch":

http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow
http://www.w3.org/TR/2007/WD-DOM-Level-3-Events-20071221


PointedEars
 
S

squash+go

You can use event.target/event.srcElement to determine ...

Thank you (dziekuje bardzo) P. Prikryl, Peter and Thomas.

The code works for me in FF under MacOSX, and there are a
few places where I don't know how/why it works. Currently,
inside <BODY></BODY> I have

<table id="GameTable">
<tr> <td>AA</td> <td>BB</td> <td>CC</td> </tr>
<tr> <td>DD</td> <td>EE</td> <td>FF</td> </tr>
</table>

<script language="JavaScript">
function UpdateGame(x,y) {alert("USER clicked (" + x + "," + y +
").")};

document.getElementById("GameTable").onclick = function(e) {
e = e || window.event;
for (var tdvar = e.target || e.srcElement;
tdvar && tdvar != this;
tdvar = tdvar.parentNode)
if (tdvar.tagName == "TD" &&
tdvar.parentNode.parentNode.parentNode == this) {
UpdateGame(tdvar.parentNode.rowIndex, tdvar.cellIndex);
break;
}
};
</script>

Q1: Why is "TD" capitalized? I understand that HTML is
case-insensitive whereas JS is sensitive. The ==
comparison is done at the JS level; how did you know to use
uppercase?

Q2: Why 3 levels of parentNode, rather than 2, in

tdvar.parentNode.parentNode.parentNode == this

? Is it that `tdvar' is bound to an event whose parent is
<TD>? Or is it that `tdvar' is bound to <TD>, but that an
implicit <TBODY> is being inserted into my innocent
<table>? --I am inferring the latter from this page:

http://htmlhelp.com/reference/html40/tables/tbody.html

I haven't looked at HTML for a l*o*n*g time, I don't
remember there even being a <TBODY> tag when I was
designing the few tables I use on my site.

Ta, Jonathan King Math dept, Univ. of Florida
 
Á

Álvaro G. Vicario

(e-mail address removed) escribió:
Q1: Why is "TD" capitalized? I understand that HTML is
case-insensitive whereas JS is sensitive. The ==
comparison is done at the JS level; how did you know to use
uppercase?

You've said it all, that's how JavaScript works:
true


Q2: Why 3 levels of parentNode, rather than 2, in

tdvar.parentNode.parentNode.parentNode == this

Again, you got the key yourself: TBODY.

In general, you have an HTML source code that is parsed by the browser
and converted into a DOM tree. What you can manipulate with JavaScript
is the resulting node tree. This becomes more evident if you have
invalid markup like "<div>Foo</p>".

I suggest you install the Firebug extension for Firefox and have a look
at the DOM tree. Once you get used to Firebug you can't code without it.
 
P

Peter Michaux

However, the technically correct no-buzzword term is "Event bubbling"
(mentioned here before) or, more generally, "Event dispatch":

http://www.w3.org/TR/DOM-Level-2-Ev...w3.org/TR/2007/WD-DOM-Level-3-Events-20071221

"Event bubbling" is the mechanics of the chain of responsibility
pattern implemented in the DOM. It is just the fact that an event
does bubble.

"Event delegation" is an application-level concept of how a bubbling
event is used. If a ancestor node is handling an event on behalf of a
node that would normally handle the event itself then that is event
delegation. Event bubbling is what makes event delegation possible.

Peter
 
T

Thomas 'PointedEars' Lahn

Thank you (dziekuje bardzo) P. Prikryl, Peter and Thomas.

You are welcome.
[...]
<script language="JavaScript">

Must be at least

<script type="text/javascript">

The `language' attribute is deprecated, the `type' attribute is required:

http://www.w3.org/TR/html4/interact/scripts.html#edef-SCRIPT
http://validator.w3.org/
function UpdateGame(x,y) {alert("USER clicked (" + x + "," + y +
").")};

document.getElementById("GameTable").onclick = function(e) {

One wants to avoid such Reference Worms[tm]. First test whether the method
exists, then assign the reference to a variable/property, then test it if
it refers to an object, then test whether the supposed property exists (if
possible), and only then access the property. Especially when you are
dealing with host objects and when you are mixing DOMs, as here (the method
is a Web standard, the property of the object is not).

function isMethod(o, p)
{
return o && /\b(function|object|unknown)\b/i.test(typeof o[p])
&& o[p];
}

if (isMethod(document, "getElementById"))
{
var o = document.getElementById("GameTable");
if (o)
{
o.onclick = function(e) {
// ...
};
}
}

Better, because not crossing DOM levels, would be

var o = document.getElementById("GameTable");
if (o)
{
o.addEventListener("click",
function(e) {
// ...
},
false);
}

But then you get into trouble with MSHTML. Hence one usually uses a wrapper
method here: <http://PointedEars.de/scripts/dhtml.js>, _addEventListener().

While you should also call the method that is assigning to `onclick' on load
the document/body, not before --

<body onload="...">

-- there is no need for the error-prone `onclick' assignment above:

<script type="text/javascript
function tableClick(e)
{
var t = e.target || e.srcElement;
if (t)
{
// ...
}
}
</script>

<table onclick="if (typeof event != 'undefined') tableClick(event)">
...
</table>

So in order to do work with event bubbling, you only have to adapt your
original approach slightly. (The local `event' is AFAIK proprietary as
well, but I have yet to see a UA where it does not work, while there are
UAs where addEventListener() does not work.)
e = e || window.event;

if (!e && typeof window.event != "undefined") e = window.event;
if (e)
{

is more efficient and less error-prone.
for (var tdvar = e.target || e.srcElement;
tdvar && tdvar != this;
tdvar = tdvar.parentNode)
if (tdvar.tagName == "TD" &&
tdvar.parentNode.parentNode.parentNode == this) {
UpdateGame(tdvar.parentNode.rowIndex, tdvar.cellIndex);
break;
}
};
</script>

Q1: Why is "TD" capitalized? I understand that HTML is
case-insensitive whereas JS is sensitive. The ==
comparison is done at the JS level; how did you know to use
uppercase?

See <http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-822762427>.

However, as XHTML may also be served as text/html in which case it may be
parsed as HTML, I consider case-insensitive matching to be less error-prone:

if (tdvar.tagName.toLowerCase() == "td" ...)
Q2: Why 3 levels of parentNode, rather than 2, in

tdvar.parentNode.parentNode.parentNode == this

?
Is it that `tdvar' is bound to an event whose parent is
<TD>?

No, you are not making sense.
Or is it that `tdvar' is bound to <TD>,

I would consider "bound to" as ranging from confusing to wrong here.
`tdvar' would *refer to an object* implementing the HTMLTableCellElement
interface that represents the TD element in the DOM node tree.
but that an implicit <TBODY> is being inserted into my innocent
<table>?

Correct. However, as there is no public standard that requires element
nodes to be inserted in the DOM tree for required elements with optional
start and end tags (here: TBODY), it would appear to be best not to rely
on that behavior and to find the target element either manually through
iteration or with XPath (depending on which method is supported).

http://developer.mozilla.org/en/docs/XPath


HTH

PointedEars
 
T

Thomas 'PointedEars' Lahn

Peter said:
"Event bubbling" is the mechanics of the chain of responsibility
pattern implemented in the DOM. It is just the fact that an event
does bubble.

"Event delegation" is an application-level concept of how a bubbling
event is used. If a ancestor node is handling an event on behalf of a
node that would normally handle the event itself then that is event
delegation. Event bubbling is what makes event delegation possible.

Read again.


PointedEars
 
S

squash+go

On May 19, 12:35 pm, Thomas 'PointedEars' Lahn <[email protected]>
wrote:
....
Must be at least

<script type="text/javascript">

The `language' attribute is deprecated, the `type' attribute is required:

Ah; ta,
See <http://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-822762427>.

However, as XHTML may also be served as text/html in which case it may
be parsed as HTML, I consider case-insensitive matching to be less
error-prone:

if (tdvar.tagName.toLowerCase() == "td" ...)

Good; I'm going to make this my std practice.

I haven't yet processed your more technical comments; I'm still
reading
the textbooks. Thanks again.

Jonathan King Math dept, Univ. of Florida
 

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,995
Messages
2,570,236
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top