[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.javascript

Retrieve a select from a table cell

danca

7/14/2015 12:24:00 PM

Hi all, I'm trying to retrieve the value of a <select> in a table cell.
However, I noticed a different behaviour if i create the element via
HTML or if I create it via createElement() and then appendChild()

I create the table via HTML:

<TABLE id='dataTable' width='350px' border='1'>
<TR>
<TD><INPUT type='checkbox' name='chk'/></TD>
<TD><INPUT type='text' name='txt'/></TD>
<TD>
<SELECT name='datatype'>
<OPTION value='C'>Character</OPTION>
<OPTION value='M'>Memo</OPTION>
<OPTION value='N'>Numeric</OPTION>
</SELECT>
</TD>
</TR>
</TABLE>

Then I create a cell and its content via script:

function DOMcreasel(tableID){
var ns=document.createElement("SELECT")
var op=document.createElement("OPTION")
op.value="M"
op.text="Memo"
ns.appendChild(op)
op=document.createElement("OPTION")
op.value="N"
op.text="Numeric"
ns.appendChild(op)
op=document.createElement("OPTION")
op.value="C"
op.text="Character"
ns.appendChild(op)
var table = document.getElementById(tableID);
var lastrow=table.rows[(table.rows.length)-1]
var lastcell=lastrow.cells[(lastrow.cells.length)-1]
lastrow.insertCell(lastrow.cells.length)
lastrow.cells[(lastrow.cells.length)-1].appendChild(ns)
}

Now I put an onclick() on cells just to see the firstChild element of
the TD in the cell:

function putclick(tableID){
var table = document.getElementById(tableID);
for (var f=0;f<table.rows.length;++f){
for (var g=0;g<table.rows[f].cells.length;++g){
table.rows[f].cells[g].onclick=function(){
var fc=this.firstChild;
if (fc) {
alert(fc.type+" - "+fc.nodeName+" - "+fc.value+" - "+fc.nodeType)
}
}
}
}
}

When I click on the cell containing the first SELECT I have:
undefined - #text - undefined - 3
and I am not able to retrieve the selectedIndex for the SELECT.

while the second one gives:
select-one - select - M - 1
and I'm fine with firstChild.selectedIndex

Tried with Firefox, Opera, Konqueror.

What am I missing?

Dan

--
"Everybody should pay taxes with a smile"
I tried, but they want money.
21 Answers

Christoph M. Becker

7/14/2015 12:45:00 PM

0

danca (Daniele Campagna) wrote:

> Hi all, I'm trying to retrieve the value of a <select> in a table cell.
> However, I noticed a different behaviour if i create the element via
> HTML or if I create it via createElement() and then appendChild()

Well, remove all whitespace between the tags in the HTML version, and
most likely you get the same behavior.

> Now I put an onclick() on cells just to see the firstChild element of
> the TD in the cell:

Node.firstChild does not necessarily yield the the first child *element*
of the node. If you want to get that, use ParentNode.firstElementChild
(you may want to write a wrapper/fallback in case the method is not
supported).

Alternatively, you can retrieve all select elements with
Element.getElementsByTagName("select").

--
Christoph M. Becker

Thomas 'PointedEars' Lahn

7/14/2015 2:53:00 PM

0

Christoph M. Becker wrote:

> danca (Daniele Campagna) wrote:
>> Hi all, I'm trying to retrieve the value of a <select> in a table cell.
>> However, I noticed a different behaviour if i create the element via
>> HTML or if I create it via createElement() and then appendChild()
>
> Well, remove all whitespace between the tags in the HTML version, and
> most likely you get the same behavior.

ACK.

>> Now I put an onclick() on cells just to see the firstChild element of
>> the TD in the cell:
>
> Node.firstChild does not necessarily yield the the first child *element*
> of the node. If you want to get that, use ParentNode.firstElementChild
> (you may want to write a wrapper/fallback in case the method is not
> supported).
>
> Alternatively, you can retrieve all select elements with
> Element.getElementsByTagName("select").

You are writing interface names as if they would refer to objects. This is
confusing, and wrong if taken verbatim, because in recent DOM
implementations those names can and do refer to objects, but not to objects
that implement the interface, but objects that provide access to its
features via the prototype chain. Unfortunately, this mistake of
oversimplification is perpetuated by references such as MDN.

[I am writing Element::getElementsByTagName(â?¦) when I am referring to the
abstract method of the interface, and element.getElementsByTagName(â?¦) when I
am referring to the implemented method of the element object referred to by
â??elementâ? instead.]

The most compatible and most efficient approach here is to give the control
a name (â??nameâ? attribute), make it part of a form (descendant of a â??formâ?
element), and refer to it by the corresponding form object and control name
if necessary (always consider the â??thisâ? value first). Giving the control
an ID (â??idâ? attribute) and referring it by that using
document.getElementById(â?¦) is second to the former in backwards
compatibility; but maybe not in efficiency, particularly not if there are
several controls with the same name and only want to access one of them.

The FAQ Notes have more on this.

--
PointedEars
FAQ: <http://PointedEars.... | SVN: <http://PointedEars.de...
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-...
Please do not cc me. / Bitte keine Kopien per E-Mail.

Thomas 'PointedEars' Lahn

7/14/2015 2:55:00 PM

0

Christoph M. Becker wrote:

> danca (Daniele Campagna) wrote:
>> Hi all, I'm trying to retrieve the value of a <select> in a table cell.
>> However, I noticed a different behaviour if i create the element via
>> HTML or if I create it via createElement() and then appendChild()
>
> Well, remove all whitespace between the tags in the HTML version, and
> most likely you get the same behavior.

ACK.

>> Now I put an onclick() on cells just to see the firstChild element of
>> the TD in the cell:
>
> Node.firstChild does not necessarily yield the the first child *element*
> of the node. If you want to get that, use ParentNode.firstElementChild
> (you may want to write a wrapper/fallback in case the method is not
> supported).
>
> Alternatively, you can retrieve all select elements with
> Element.getElementsByTagName("select").

You are writing interface names as if they would refer to objects. This is
confusing, and wrong if taken verbatim, because in recent DOM
implementations those names can and do refer to objects, but not to objects
that implement the interface, but objects that provide access to its
features via the prototype chain. Unfortunately, this mistake of
oversimplification is perpetuated by references such as MDN.

[I am writing Element::getElementsByTagName(â?¦) when I am referring to the
abstract method of the interface, and element.getElementsByTagName(â?¦) when I
am referring to the implemented method of the element object referred to by
â??elementâ? instead.]

The most compatible and most efficient approach here is to give the control
a name (â??nameâ? attribute), make it part of a form (descendant of a â??formâ?
element), and refer to it by the corresponding form object and control name
if necessary (always consider the â??thisâ? value first). Giving the control
an ID (â??idâ? attribute) and referring it by that using
document.getElementById(â?¦) is second to the former in backwards
compatibility; but maybe not in efficiency, particularly not if there are
several controls with the same name and one only wants to access one of
them.

The FAQ Notes have more on this.

--
PointedEars
FAQ: <http://PointedEars.... | SVN: <http://PointedEars.de...
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-...
Please do not cc me. / Bitte keine Kopien per E-Mail.

Thomas 'PointedEars' Lahn

7/14/2015 7:37:00 PM

0

danca (Daniele Campagna) wrote:

> Hi all, I'm trying to retrieve the value of a <select> in a table cell.
> However, I noticed a different behaviour if i create the element via
> HTML or if I create it via createElement() and then appendChild()
>
> I create the table via HTML:
>
> <TABLE id='dataTable' width='350px' border='1'>
^^^^^
Not Valid â?? <http://validator.w... (p.)

> <TR>
> <TD><INPUT type='checkbox' name='chk'/></TD>
> <TD><INPUT type='text' name='txt'/></TD>
> <TD>
> <SELECT name='datatype'>
> <OPTION value='C'>Character</OPTION>
> <OPTION value='M'>Memo</OPTION>
> <OPTION value='N'>Numeric</OPTION>
> </SELECT>
> </TD>
> </TR>
> </TABLE>
>
> Then I create a cell and its content via script:
>
> function DOMcreasel(tableID){

â??creaselâ? should probably be â??createSelectâ?. The â??DOMâ? appears to be
superfluous.

> var ns=document.createElement("SELECT")
> var op=document.createElement("OPTION")

It is recommended to use lowercase element type names. You should return
early, before attempting to create the â??optionâ? element, if the parent
â??selectâ? element could not be created.

> [â?¦]
> }
>
> Now I put an onclick() on cells just to see the firstChild element of
> the TD in the cell:
>
> function putclick(tableID){
> var table = document.getElementById(tableID);
^
Your code style is inconsistent. You should either always use semicolons or
never (and be aware of the problems with the latter thanks to Automatic
Semicolon Insertion).

> for (var f=0;f<table.rows.length;++f){

As the number of rows of the table does not change during iteration, it is
much more efficient to store the value of table.rows.length in a variable
one time, and use that variable instead. Also, â??fâ? is an unusual variable
name for an index; it only makes reading the code harder. Consider

for (var i = 0, len = table.rows.length; i < len; ++i) {

instead. And if order does not matter, it is even more efficient to iterate
backwards:

for (var i = table.rows.length; i--;) {

> for (var g=0;g<table.rows[f].cells.length;++g){

See above.

> table.rows[f].cells[g].onclick=function(){
> var fc=this.firstChild;
> if (fc) {
> alert(fc.type+" - "+fc.nodeName+" - "+fc.value+" - "+fc.nodeType)
> }
> }

Note that creating functions in a loop is error-prone because a function
creates a closure; JSHint can warn you about this.

The loop and creating functions in a loop is even unnecessary here because
the â??clickâ? event bubbles: you can listen to it at the row (â??trâ?), row group
(â??theadâ? or â??tbodyâ? element), or the table (â??tableâ? element) instead,
requiring much less event listeners (down to only *one* at the table),
therefore much less memory (each function definition creates an object).
You are creating table.rows.length * table.rows[0].cells.length Function
instances here, assuming that each row of the table has the same number of
cells as the first one.

The â??targetâ? or â??srcElementâ? property of the event object would then yield
the reference to the event target. There is only one caveat: If the event
target is a descendent of a cell, you need to walk up the DOM tree until you
find the cell. But because the â??clickâ? event bubbles, you need to do that
in any case, and you neglected it here.

â?¦.onclick = function (e) {
var target = e.target || window.event.srcElement;

if (!target) return true;

while (target && target.tagName && target.tagName.toLowerCase() != "td")
{
target = target.parentNode;
}

if (target && target.tagName)
{
/* work with target */
}
};

> }
> }
> }
>
> [â?¦]
> What am I missing?

Among other things, â??document.forms["â?¦"].elements["datatype"]â?.

The question is, why are you adding a â??clickâ? event listener on the cell in
the first place?` And why do you think you need to refer to the form control
in this complicated way?

--
PointedEars
FAQ: <http://PointedEars.... | SVN: <http://PointedEars.de...
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-...
Please do not cc me. / Bitte keine Kopien per E-Mail.

Thomas 'PointedEars' Lahn

7/14/2015 7:41:00 PM

0

Thomas 'PointedEars' Lahn wrote:

> â?¦.onclick = function (e) {
> var target = e.target || window.event.srcElement;

Should be at least

var target = (e ? e.target : window.event.srcElement);

> if (!target) return true;

--
PointedEars
FAQ: <http://PointedEars.... | SVN: <http://PointedEars.de...
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-...
Please do not cc me. / Bitte keine Kopien per E-Mail.

JR

7/14/2015 7:44:00 PM

0

Em 14/07/2015 09:24, danca (Daniele Campagna) escreveu:
> Hi all, I'm trying to retrieve the value of a <select> in a table cell.
> However, I noticed a different behaviour if i create the element via
> HTML or if I create it via createElement() and then appendChild()
>
> I create the table via HTML:
>
> <TABLE id='dataTable' width='350px' border='1'>
> <TR>
> <TD><INPUT type='checkbox' name='chk'/></TD>
> <TD><INPUT type='text' name='txt'/></TD>
> <TD>
> <SELECT name='datatype'>
> <OPTION value='C'>Character</OPTION>
> <OPTION value='M'>Memo</OPTION>
> <OPTION value='N'>Numeric</OPTION>
> </SELECT>
> </TD>
> </TR>
> </TABLE>
>
> Then I create a cell and its content via script:
>
> function DOMcreasel(tableID){
> var ns=document.createElement("SELECT")
> var op=document.createElement("OPTION")
> op.value="M"
> op.text="Memo"
> ns.appendChild(op)
> op=document.createElement("OPTION")
> op.value="N"
> op.text="Numeric"
> ns.appendChild(op)
> op=document.createElement("OPTION")
> op.value="C"
> op.text="Character"
> ns.appendChild(op)
> var table = document.getElementById(tableID);
> var lastrow=table.rows[(table.rows.length)-1]
> var lastcell=lastrow.cells[(lastrow.cells.length)-1]
> lastrow.insertCell(lastrow.cells.length)
> lastrow.cells[(lastrow.cells.length)-1].appendChild(ns)
> }
>

DOMcreasel() may be simplified to:

function DOMcreasel(tableID) {
var ns = document.createElement("SELECT");
ns.options[ns.options.length] = new Option('Memo', 'M');
ns.options[ns.options.length] = new Option('Numeric', 'N');
ns.options[ns.options.length] = new Option('Character', 'C');
var lastrow = document.querySelector("#" + tableID
+ ">tbody>tr:last-child");
var i = lastrow.cells.length;
lastrow.insertCell(i).appendChild(ns);
}



> Now I put an onclick() on cells just to see the firstChild element of
> the TD in the cell:

You are adding onclick event handlers to every child of the TDs, and
fc.firstchild may be null depending on what you click (use Firefox /
Firebug to see it for yourself).

>
> function putclick(tableID){
> var table = document.getElementById(tableID);
> for (var f=0;f<table.rows.length;++f){
> for (var g=0;g<table.rows[f].cells.length;++g){
> table.rows[f].cells[g].onclick=function(){
> var fc=this.firstChild;
> if (fc) {
> alert(fc.type+" - "+fc.nodeName+" - "+fc.value+" - "+fc.nodeType)
> }
> }
> }
> }
> }
>
> When I click on the cell containing the first SELECT I have:
> undefined - #text - undefined - 3
> and I am not able to retrieve the selectedIndex for the SELECT.
>
> while the second one gives:
> select-one - select - M - 1
> and I'm fine with firstChild.selectedIndex
>
> Tried with Firefox, Opera, Konqueror.
>
> What am I missing?


So I refactored the code to:

function putclick(tableID) {
var tbl = document.getElementById(tableID);
var elems = tbl.querySelectorAll("input, select");
for (var i = 0, len = elems.length; i < len; i++) {
elems[i].addEventListener('click', cellClick, false);
}
}

function cellClick(e) {
var fc = e.target;
window.alert(fc.type + " - " + fc.nodeName
+ " - " + fc.value + " - "
+ fc.nodeType);
}

The caveat in my code is that querySelectorAll returns a static
NodeList. However you may use other methods to retrieve a live NodeList
if you need to.

--
Joao Rodrigues

JR

7/14/2015 8:03:00 PM

0

Em 14/07/2015 16:44, Joao Rodrigues escreveu:
> Em 14/07/2015 09:24, danca (Daniele Campagna) escreveu:
>> Hi all, I'm trying to retrieve the value of a <select> in a table cell.
>> However, I noticed a different behaviour if i create the element via
>> HTML or if I create it via createElement() and then appendChild()
>>
>> I create the table via HTML:
>>
>> <TABLE id='dataTable' width='350px' border='1'>
>> <TR>
>> <TD><INPUT type='checkbox' name='chk'/></TD>
>> <TD><INPUT type='text' name='txt'/></TD>
>> <TD>
>> <SELECT name='datatype'>
>> <OPTION value='C'>Character</OPTION>
>> <OPTION value='M'>Memo</OPTION>
>> <OPTION value='N'>Numeric</OPTION>
>> </SELECT>
>> </TD>
>> </TR>
>> </TABLE>
>>
>> Then I create a cell and its content via script:
>>
>> function DOMcreasel(tableID){
>> var ns=document.createElement("SELECT")
>> var op=document.createElement("OPTION")
>> op.value="M"
>> op.text="Memo"
>> ns.appendChild(op)
>> op=document.createElement("OPTION")
>> op.value="N"
>> op.text="Numeric"
>> ns.appendChild(op)
>> op=document.createElement("OPTION")
>> op.value="C"
>> op.text="Character"
>> ns.appendChild(op)
>> var table = document.getElementById(tableID);
>> var lastrow=table.rows[(table.rows.length)-1]
>> var lastcell=lastrow.cells[(lastrow.cells.length)-1]
>> lastrow.insertCell(lastrow.cells.length)
>> lastrow.cells[(lastrow.cells.length)-1].appendChild(ns)
>> }
>>
>
> DOMcreasel() may be simplified to:
>
> function DOMcreasel(tableID) {
> var ns = document.createElement("SELECT");
> ns.options[ns.options.length] = new Option('Memo', 'M');
> ns.options[ns.options.length] = new Option('Numeric', 'N');
> ns.options[ns.options.length] = new Option('Character', 'C');
> var lastrow = document.querySelector("#" + tableID
> + ">tbody>tr:last-child");
> var i = lastrow.cells.length;
> lastrow.insertCell(i).appendChild(ns);
> }
>
>
>
>> Now I put an onclick() on cells just to see the firstChild element of
>> the TD in the cell:
>
> You are adding onclick event handlers to every child of the TDs, and
> fc.firstchild may be null depending on what you click (use Firefox /
> Firebug to see it for yourself).
>
>>
>> function putclick(tableID){
>> var table = document.getElementById(tableID);
>> for (var f=0;f<table.rows.length;++f){
>> for (var g=0;g<table.rows[f].cells.length;++g){
>> table.rows[f].cells[g].onclick=function(){
>> var fc=this.firstChild;
>> if (fc) {
>> alert(fc.type+" - "+fc.nodeName+" - "+fc.value+" - "+fc.nodeType)
>> }
>> }
>> }
>> }
>> }
>>
>> When I click on the cell containing the first SELECT I have:
>> undefined - #text - undefined - 3
>> and I am not able to retrieve the selectedIndex for the SELECT.
>>
>> while the second one gives:
>> select-one - select - M - 1
>> and I'm fine with firstChild.selectedIndex
>>
>> Tried with Firefox, Opera, Konqueror.
>>
>> What am I missing?
>
>
> So I refactored the code to:
>
> function putclick(tableID) {
> var tbl = document.getElementById(tableID);
> var elems = tbl.querySelectorAll("input, select");
> for (var i = 0, len = elems.length; i < len; i++) {
> elems[i].addEventListener('click', cellClick, false);
> }
> }
>
> function cellClick(e) {
> var fc = e.target;
> window.alert(fc.type + " - " + fc.nodeName
> + " - " + fc.value + " - "
> + fc.nodeType);
> }
>

We can take advantage of the Bubbling Phase [1]. So let's rewrite simply to:

function putclick(tableID) {
var tbl = document.getElementById(tableID);
tbl.addEventListener('click', cellClick, false);
}

function cellClick(e) {
var fc = e.target;
if (/input|select/i.test(fc.tagName)) {
window.alert(fc.type + " - " + fc.nodeName
+ " - " + fc.value + " - "
+ fc.nodeType);
}
}

[1] <http://www.w3.org/TR/DOM-Level-3-Events/#even...


> The caveat in my code is that querySelectorAll returns a static
> NodeList. However you may use other methods to retrieve a live NodeList
> if you need to.
>


--
Joao Rodrigues

Thomas 'PointedEars' Lahn

7/14/2015 8:03:00 PM

0

Joao Rodrigues wrote:

> DOMcreasel() may be simplified to:
>
> function DOMcreasel(tableID) {
> var ns = document.createElement("SELECT");
> ns.options[ns.options.length] = new Option('Memo', 'M');
> [?]
> var lastrow = document.querySelector("#" + tableID
> + ">tbody>tr:last-child");
> var i = lastrow.cells.length;
> lastrow.insertCell(i).appendChild(ns);
> }

Yes, but then you would have to explain why you are combining the oldest
proprietary with the newest standards-compliant features in an error-prone
way.

> You are adding onclick event handlers to every child of the TDs, and
> fc.firstchild may be null depending on what you click (use Firefox /
> Firebug to see it for yourself).

Correct. But if that had been the reason here, the result would not have
been an alert message, but a TypeError exception as ?null as no properties?.

> [?]
>> var fc=this.firstChild;
>> if (fc) {
>> alert(fc.type+" - "+fc.nodeName+" - "+fc.value+" - "+fc.nodeType)
>> }
>> [?]
>>
>> When I click on the cell containing the first SELECT I have:
>> undefined - #text - undefined - 3
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>> [?]
>
> [?]
> var elems = tbl.querySelectorAll("input, select");
> for (var i = 0, len = elems.length; i < len; i++) {
> elems[i].addEventListener('click', cellClick, false);
> }

The ?click? event bubbles. Even though you are wasting much less memory
than the OP, the loop and all those event listeners are unnecessary,
especially if one assumes W3C DOM compatibility as you do.

> }
>
> function cellClick(e) {
> var fc = e.target;
> window.alert(fc.type + " - " + fc.nodeName
> + " - " + fc.value + " - "
> + fc.nodeType);

Insufficient, even by your own account: ?select? elements have child
elements.

.
.
.
.
.
.
--
PointedEars
FAQ: <http://PointedEars.... | SVN: <http://PointedEars.de...
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-...
Please do not cc me. / Bitte keine Kopien per E-Mail.

Thomas 'PointedEars' Lahn

7/14/2015 8:07:00 PM

0

Joao Rodrigues wrote:

> function cellClick(e) {
> var fc = e.target;
> if (/input|select/i.test(fc.tagName)) {
> window.alert(fc.type + " - " + fc.nodeName
> + " - " + fc.value + " - "
> + fc.nodeType);
> }
> }

Not only as insufficient as your first approach (see my other follow-up),
but even more inefficient and error-prone. If you are using a regular
expression, you need to anchor it to prevent false positives, at least
/^(input|select)$/i. But you can do without such an expensive RegExp here.
Finally, the â??inputâ? appears to be coming from your imagination only; read
again.

--
PointedEars
FAQ: <http://PointedEars.... | SVN: <http://PointedEars.de...
Twitter: @PointedEars2 | ES Matrix: <http://PointedEars.de/es-...
Please do not cc me. / Bitte keine Kopien per E-Mail.

JR

7/14/2015 8:28:00 PM

0

Em 14/07/2015 17:07, Thomas 'PointedEars' Lahn escreveu:
> Joao Rodrigues wrote:
>
>> function cellClick(e) {
>> var fc = e.target;
>> if (/input|select/i.test(fc.tagName)) {
>> window.alert(fc.type + " - " + fc.nodeName
>> + " - " + fc.value + " - "
>> + fc.nodeType);
>> }
>> }
>
> Not only as insufficient as your first approach (see my other follow-up),

The OP's putclick() function didn't seem to take care of the select's
options. She just added new selects with the DOMcreasel() function.

> but even more inefficient and error-prone. If you are using a regular
> expression, you need to anchor it to prevent false positives, at least
> /^(input|select)$/i.

More efficient, but unnecessary for the test case. Why do you always
repeat it?

> But you can do without such an expensive RegExp here.

Expensive for modern JavaScript Engines? Nah.

> Finally, the â??inputâ? appears to be coming from your imagination only; read
> again.
>

You should read again as you have forgotten the HTML table in the OP's code:

| <TD><INPUT type='checkbox' name='chk'/></TD>
| <TD><INPUT type='text' name='txt'/></TD>

--
Joao Rodrigues