Uncaught exception when calling rowIndex.

M

meaneyedcat

When I call addField(), my table appears to be populated correctly
with a new row in the table with all the required fields. However,
when I call delete row on any new rows that have been created, I get
the following error:

Error: uncaught exception: [Exception... "Component returned failure
code: 0x80004002 (NS_NOINTERFACE)
[nsIDOMHTMLTableRowElement.rowIndex]" nsresult: "0x80004002
(NS_NOINTERFACE)" location: "JS frame ::
http://localhost:3000/form/edit_fields/1 :: removeField :: line 65"
data: no]

The error appears to happen when the last line of removeField() gets
called...specifically rowToDelete.rowIndex throws the exception.

I should mention that I am running this code on Firefox 1.0, however,
when I run it on IE6, it doesn't work either, but I don't know what
the error is (I just don't know how to get a trace of the JavaScript
errors).

thanks for your help!
Aaron.

<script type="text/javascript">
var newArray = new Array();
var exArray = new Array();

var delArr = new Array();

var tbCount=<%= @form.inputs.count %>;

function addField() {
newArray[tbCount]=document.createElement('INPUT');
table = document.getElementById('fieldTable');

row = document.createElement('tr');
row.id = 'n_' + (tbCount + 1);
fieldCell = document.createElement('td');
fieldCell.appendChild(document.createTextNode('Field ' +
(tbCount + 1) + ':'));

nameCell = document.createElement('td');
nameInput = document.createElement('input');
nameInput.id = 'nname' + (tbCount + 1);
nameInput.name = 'nname[' + (tbCount + 1) + ']';
nameInput.type = 'text';
nameInput.size = '30';
nameCell.appendChild(nameInput);

descCell = document.createElement('td');
descInput = document.createElement('input');
descInput.id = 'newdesc' + (tbCount + 1);
descInput.name = 'newdesc[' + (tbCount + 1) + ']';
descInput.type = 'text';
descInput.size = '30';
descCell.appendChild(descInput);

delCell = document.createElement('td');
delLink = document.createElement('a');
delLink.href = "#";
delLink.setAttribute('onClick', "removeField(n_" + (tbCount+1) +
"); return false");
delLink.appendChild(document.createTextNode('Delete'));
delCell.appendChild(delLink);

table.appendChild(row);
row.appendChild(fieldCell);
row.appendChild(nameCell);
row.appendChild(descCell);
row.appendChild(delCell);
tbCount++;
}

function removeField(rowToDelete) {
array = rowToDelete.id.split('_');
rowType = array[0];
rowId = array[1];

switch (rowType) {
case 'e':
break;
case 'n':
break;
}
table = document.getElementById('fieldTable');
table.deleteRow(rowToDelete.rowIndex);
}
</script>
 
M

Martin Honnen

when I call delete row on any new rows that have been created, I get
the following error:

Error: uncaught exception: [Exception... "Component returned failure
code: 0x80004002 (NS_NOINTERFACE)
[nsIDOMHTMLTableRowElement.rowIndex]" nsresult: "0x80004002
(NS_NOINTERFACE)" location: "JS frame ::
http://localhost:3000/form/edit_fields/1 :: removeField :: line 65"
data: no]

The error appears to happen when the last line of removeField() gets
called...specifically rowToDelete.rowIndex throws the exception.
function removeField(rowToDelete) {
array = rowToDelete.id.split('_');
rowType = array[0];
rowId = array[1];

switch (rowType) {
case 'e':
break;
case 'n':
break;
}
table = document.getElementById('fieldTable');
table.deleteRow(rowToDelete.rowIndex);
}

We need to see where you call the function, exactly what you pass in as
an argument, make sure it is really a row element object.
 
M

Michael Winter

[W]hen I call delete row on any new rows that have been created, I
get the following error:

Could you create a minimal example and provide a URL? It's much easier
to diagnose a problem when it can be seen in action.

[snip]
function addField() {
newArray[tbCount]=document.createElement('INPUT');
table = document.getElementById('fieldTable');

row = document.createElement('tr');
row.id = 'n_' + (tbCount + 1);
fieldCell = document.createElement('td');
fieldCell.appendChild(document.createTextNode('Field '
+ (tbCount + 1) + ':'));

I would seriously consider learning to apply the var keyword within
functions. It is not a good idea to dump loads of variables into the
global object.

[snip]
delLink.setAttribute('onClick', "removeField(n_" + (tbCount+1)
+ "); return false");

Isn't the argument to removeField supposed to be a string? You should
change that second argument to

'removeField("n_' + (tbCount + 1) + '"); return false;'

That said, this isn't a reliable way to create event listeners (though
it will certainly work in Mozilla). If you plan on using this code in
a wider environment, take the traditional approach:

delLink.onclick = function() {
removeField('n_' + (tbCount + 1));
return false;
};

[snip]

Mike
 
M

meaneyedcat

I don't have a webserver in which I can deploy my code, but I can give
you the full page source if that will help.

When I add a row to a table through JavaScript, is the internal
representation of the table and rows still accessable as if the rows
where there when the page was rendered initially? I noticed that when I
add a row using table.insertRow(table.rows.length) and then
alert(table.rows.length), the number is always the same. So, if I had 3
rows rendered initially, the alert would display three. When I append a
couple more rows using the code below, the row count would still be
three. That leads me to believe that A) The rows are appended in some
other structure, or B) I have no clue what I am doing! I am willing to
accept B over A at this point. =-)

As for the parameter to the move method being wrapped in quotes. If I
do that, it actually returns a string, but when I leave the quotes out,
it returns the HTMLTableRowElement, which works nice because I should
be able to get the rowIndex from it (but I am not, I am getting this
exception!).

Anyway, thanks for your help and quick replies! Below is the full
source for the page.
Thanks again!
AR.

<html>
<head>
<title></title>

<script type="text/javascript">
var newArray = new Array();
var exArray = new Array();
var delArr = new Array();

var tbCount=<%= @form.inputs.count %>;

function addField() {
table = document.getElementById('fieldTable');

row = table.insertRow(table.rows.length);
row.id = 'n_' + (tbCount + 1);
fieldCell = document.createElement('td');
fieldCell.appendChild(document.createTextNode('Field ' + (tbCount +
1) + ':'));

nameCell = document.createElement('td');
nameInput = document.createElement('input');
nameInput.id = 'nname' + (tbCount + 1);
nameInput.name = 'nname[' + (tbCount + 1) + ']';
nameInput.type = 'text';
nameInput.size = '30';
nameCell.appendChild(nameInput);

descCell = document.createElement('td');
descInput = document.createElement('input');
descInput.id = 'newdesc' + (tbCount + 1);
descInput.name = 'newdesc[' + (tbCount + 1) + ']';
descInput.type = 'text';
descInput.size = '30';
descCell.appendChild(descInput);

delCell = document.createElement('td');
delLink = document.createElement('a');
delLink.href = "#";
delLink.setAttribute('onClick', "removeField(n_" + (tbCount+1) + ");
return false");
delLink.appendChild(document.createTextNode('Delete'));
delCell.appendChild(delLink);

table.appendChild(row);
row.appendChild(fieldCell);
row.appendChild(nameCell);
row.appendChild(descCell);
row.appendChild(delCell);
tbCount++;
}

function removeField(rowToDelete) {
array = rowToDelete.id.split('_');
rowType = array[0];
rowId = array[1];

switch (rowType) {
case 'e':
break;
case 'n':
break;
}
alert(rowToDelete.rowIndex);
table = document.getElementById('fieldTable');
table.deleteRow(rowToDelete.rowIndex);
}
</script>

</head>
<body>


<%= start_form_tag( {:action => "save_fields", :id => @form.id}, "id"
=> "fieldForm") %>

<table id="fieldTable">
<% i = 0
@form.inputs.each do |field|
i = i + 1
%>
<tr id="e_<%= i %>">
<td>
Field <%= i %>:
</td>
<td>
<%= text_field "name", field.id, "value" => field.name %>
</td>
<td>
<%= text_field "description", field.id, "value" => field.description
%>
</td>
<td>
<a href="#" onClick="removeField(<%= 'e_' + i.to_s %>); return
false;">Delete</a>
<!-- %= link_to("Delete", :action => "delete_field", :id => field.id
) % -->
</td>
</tr>
<% end %>
</table>
<input type="submit" value="Save"/>
<%= hidden_field "field_count", "count", "value" => @form.inputs.count
%>
<%= end_form_tag %>
<a href="#" onClick="addField(); return false;">Add Field</a>

</body>
</html>
 
M

Michael Winter

I don't have a webserver in which I can deploy my code, but I can
give you the full page source if that will help.

In it's current form, no. Please show the *output* of the server-side
code (preferably with only a minimal set of input data).
I noticed that when I add a row using
table.insertRow(table.rows.length)

You can use

table.insertRow(-1);

to append a row.
and then alert(table.rows.length), the number is always the same.

In which browser? Both IE6 and Firefox 1.0 update the row count. I'd
expect Opera to as well.
As for the parameter to the move method being wrapped in quotes. If
I do that, it actually returns a string, but when I leave the
quotes out, it returns the HTMLTableRowElement [...]

My apologies. I should have double-checked. However, you shouldn't
rely on that behaviour. Obtaining a reference to an element through
its id attribute value alone is not well supported (and in my opinion,
a horrible feature to implement).

On the subject of sending a reference though, this can easily be
achieved with a slight modification to the code I posted:

function addField() {
/* ... */

var row = table.insertRow(-1);

/* ... */

delLink.onclick = function() {
removeField(row);
return false;
};

/* ... */
}

For that to work the variable, row, *must* be local (as above).

[snip]

Mike
 
M

meaneyedcat

Thanks Michael, you have given me some things to think about. Listed
below is the output of the rendered HTML page.

Thanks again!
AR.

<html>
<head>
<title></title>

<script type="text/javascript">
var newArray = new Array();
var exArray = new Array();
var delArr = new Array();

var tbCount=1;

function addField() {
table = document.getElementById('fieldTable');

row = table.insertRow(table.rows.length);
row.id = 'n_' + (tbCount + 1);
fieldCell = document.createElement('td');
fieldCell.appendChild(document.createTextNode('Field ' + (tbCount +
1) + ':'));

nameCell = document.createElement('td');
nameInput = document.createElement('input');
nameInput.id = 'nname' + (tbCount + 1);
nameInput.name = 'nname[' + (tbCount + 1) + ']';
nameInput.type = 'text';
nameInput.size = '30';
nameCell.appendChild(nameInput);

descCell = document.createElement('td');
descInput = document.createElement('input');
descInput.id = 'newdesc' + (tbCount + 1);
descInput.name = 'newdesc[' + (tbCount + 1) + ']';
descInput.type = 'text';
descInput.size = '30';
descCell.appendChild(descInput);

delCell = document.createElement('td');
delLink = document.createElement('a');
delLink.href = "#";
delLink.setAttribute('onClick', "removeField(n_" + (tbCount+1) + ");
return false");
delLink.appendChild(document.createTextNode('Delete'));
delCell.appendChild(delLink);

table.appendChild(row);
row.appendChild(fieldCell);
row.appendChild(nameCell);
row.appendChild(descCell);
row.appendChild(delCell);
tbCount++;
}

function removeField(rowToDelete) {
array = rowToDelete.id.split('_');
rowType = array[0];
rowId = array[1];

swit:h (rowType) {
case 'e':
break;
case 'n':
break;
}
alert(rowToDelete.rowIndex);
table = document.getElementById('fieldTable');
table.deleteRow(rowToDelete.rowIndex);
}
</script>

</head>
<body>


<form action="/form/save_fields/1" id="fieldForm" method="post">

<table id="fieldTable">

<tr id="e_1">

<td>
Field 1:
</td>
<td>
<input id="name_1" name="name[1]" size="30" type="text" value="ID"
/>
</td>
<td>
<input id="description_1" name="description[1]" size="30"
type="text" value="Serial Number" />
</td>

<td>
<a href="#" onClick="removeField(e_1); return false;">Delete</a>
<!-- %= link_to("Delete", :action => "delete_field", :id => field.id
) % -->
</td>
</tr>

</table>
<input type="submit" value="Save"/>
<input id="field_count_count" name="field_count[count]" type="hidden"
value="1" />
</form>
<a href="#" onClick="addField(); return false;">Add Field</a>

</body>
</html>
 
M

meaneyedcat

OK, I figured out why the table.rows.length wasn't updating. If you
look at my code, I first do a

row = table.insertRow(XXXX);

then later in the code, I manually add that row to the table
again....some funky stuff must have been happening behind the scenes.

AR.
 
M

Martin Honnen

function addField() {
table = document.getElementById('fieldTable');

It was already suggested to make sure your function uses local variables
e.g.
var table = ...
row = table.insertRow(table.rows.length);
var row = ...
and so on. That also means you can use
delLink.setAttribute('onClick', "removeField(n_" + (tbCount+1) + ");
return false");

delLink.onclick = function (evt) {
removeField(row);
return false;
};
here instead of what you have, setAttribute is unfortunately implemented
in different ways in different browsers.

table.appendChild(row);

Don't do that if you have called insertRow above to create the row.
Indeed even if you used createElement to create a row don't call
appendChild on the table itself, instead make sure you insert into a tbody.

function removeField(rowToDelete) {
table = document.getElementById('fieldTable');
table.deleteRow(rowToDelete.rowIndex);

You could simply do
rowToDelete.parentNode.removeChild(rowToDelete);
for those two lines.

<table id="fieldTable">

<tr id="e_1">

<td>
Field 1:
</td>
<td>
<input id="name_1" name="name[1]" size="30" type="text" value="ID"
/>
</td>
<td>
<input id="description_1" name="description[1]" size="30"
type="text" value="Serial Number" />
</td>

<td>
<a href="#" onClick="removeField(e_1);

Don't rely on elements being accessible by variables of the name of the
id attribute, that works in IE and some other browsers (unfortunately
even in Firefox 1.0 in quirks mode) but for cross-browser support you
should use document.getElementById e.g.
<a onclick="var row;
if (document.getElementById && (row =
document.getElementById('e_1'))) {
removeField(row);
}
return false;"
 

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

Latest Threads

Top