Bug in IE and Safari when using removeChild

M

mikez302

Hi. I am trying to make a Javascript program that keeps track of
prices, and I'm encountering a bug. Each row in my table has a
number: the top row is numbered 0, the next one is 1, etc. From the
user's point of view, the numbers begin from 1, but that is
irrelevant. Each row has an input whose name attribute contains the
row number: "monthly[0][fee]", "monthly[1][fee]", etc. Each row also
has a button to remove the row. Clicking the remove button is
supposed to call a function so that when a row is removed, the rows
are are renumbered sequentially. For example, when you remove row 1,
row 2 becomes the new row 1, row 3 becomes row 2, etc.

It works fine in Firefox 2.0.0.11, but I am encountering a bug in IE
7.0 and Safari. If I remove the top row, then remove the top row
again, I get a Javascript error in IE saying "'undefined' is null or
not an object.". From then on, I get lots of weird problems, and the
form can't be submitted properly. From my experimentation, it seems
that IE is getting confused about the name attribute of one of the
input boxes immediately after removing the row. Why is this
happening?

You can see it in action at http://eliaszamaria.info/IEBugDemo1.html.
My actual code is more complicated, but this seems to be the simplest
situation where I run into this problem. Click the top remove button,
then click it again, and look at the status bar. I also have a second
page, http://eliaszamaria.info/IEBugDemo2.html, which is exactly the
same, but with alerts showing the type of the 2nd text input (named
"monthly[1][fee]") immediately before and after removing the row (but
before renumbering the text input). In Firefox, it shows up as
"[object HTMLInputElement]" the whole time. However, in IE, after
clicking the remove button for the 2nd time, it shows up as "[object]"
before removing the row, and "undefined" after, even though removing
the 1st row shouldn't touch the 2nd text input. Safari gives similar
results. Can anyone figure out why this is happening, or suggest a
way to work around the problem? I looked on Google and found lots of
info about Javascript bugs in IE, but nothing helpful for my specific
situation.

Here is the code I'm using:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://
www.w3.org/TR/html4/loose.dtd">
<html><head>
<meta http-equiv='Content-Type' content='text/html;
charset=ISO-8859-1'>
<meta http-equiv='Content-Language' content='en-us'>
<title>IE removeChild bug demo</title></head>
<body>
<script type='text/javascript'>var monthlyFees = [{"fee":"250.00"},
{"fee":"195.00"},{"fee":"125.00"}];
function removeFee(rowNum) {
var i, testBody = document.getElementById('fees').tBodies[0];
testBody.removeChild(testBody.rows[rowNum]);
for (i = rowNum; i < testBody.rows.length; i++) {
testBody.rows.cells[0].innerHTML = "<span class='rowNumber'>" +
(parseInt(i) + 1) + "</span> <input type='button' value='Remove'
class='remove' onclick='removeFee(" + i + ")'>";
document.edit.elements['monthly[' + (parseInt(i) + 1) + ']
[fee]'].name = document.edit.elements['monthly[' + i + '][fee]'];
}
}
</script>
<form method='post' action='' name='edit'>
<table id='fees'>
<tr><td><span class="rowNumber">1</span> <input value="Remove"
onclick="removeFee(0)" type="button"></td><td><input name="monthly[0]
[fee]" size="11" maxlength="11" value="250.00" type="text"></td></tr>
<tr><td><span class="rowNumber">2</span> <input value="Remove"
onclick="removeFee(1)" type="button"></td><td><input name="monthly[1]
[fee]" size="11" maxlength="11" value="195.00" type="text"></td></tr>
<tr><td><span class="rowNumber">3</span> <input value="Remove"
onclick="removeFee(2)" type="button"></td><td><input name="monthly[2]
[fee]" size="11" maxlength="11" value="125.00" type="text"></td></tr></
table></form>
</body></html>

I appreciate your help.

Elias
 
R

Richard Cornford

mikez302 wrote:
document.edit.elements['monthly[' + (parseInt(i) + 1) +
'][fee]'].name =
document.edit.elements['monthly[' + i + '][fee]'];
<snip>

This is very wrong. What you are doing (when it doe not error out) is
assigning a reference to an object (of from control) to the - name -
property of a form control. With form controls the expectation would be
that such an assignment would type-convert the reference to the form
control assigned into the string primitive type that is expected as
the - name - property of a from control. The result of such a
type-conversion is unlikely to be a unique name, and on IE will likely
always be "[object]".

Incidentally, because your - i - variable is a number and happens to be
an integer your - parseInt(i) - calls are pointless and very inefficient
as they require that the - i - argument be type-converted into a string
to be parsed back into a number.

Richard.
 
R

RobG

Hi. I am trying to make a Javascript program that keeps track of
prices, and I'm encountering a bug. Each row in my table has a
number: the top row is numbered 0, the next one is 1, etc. From the
user's point of view, the numbers begin from 1, but that is
irrelevant. Each row has an input whose name attribute contains the
row number: "monthly[0][fee]", "monthly[1][fee]", etc. Each row also
has a button to remove the row. Clicking the remove button is
supposed to call a function so that when a row is removed, the rows
are are renumbered sequentially. For example, when you remove row 1,
row 2 becomes the new row 1, row 3 becomes row 2, etc.

It seems to me that you should write a generic re-numbering function
using the number of contols left in the form.

Then you don't care which one is removed, you just re-number
accordingly, e.g.

<script type="text/javascript">

function removeFee(el) {
var p = el.parentNode;
var f = el.form;

while (p && p.tagName.toLowerCase() != 'tr') {
p = p.parentNode;
}

if (!p) return;

p.parentNode.removeChild(p);
reNumber(f);
}

function reNumber(form) {

if (!form) return;

var controls = form.elements;
var control, name, c = 0;

for (var i=0, len=controls.length; i<len; i++) {
control = controls;
name = control.name;
if (name && (name).match('monthly')) {
control.name = 'monthly[' + c++ + '][fee]';
}
}
}

</script>

<form action='#'>
<table>
<tr>
<td><span class="rowNumber">1</span>
<input value="Remove"
onclick="removeFee(this)" type="button">
<td><input name="monthly[0][fee]" size="11"
maxlength="11" value="250.00" type="text"
onclick="alert(this.name);">
<tr>
<td><span class="rowNumber">2</span>
<input value="Remove"
onclick="removeFee(this)" type="button">
<td><input name="monthly[1][fee]" size="11"
maxlength="11" value="195.00" type="text"
onclick="alert(this.name);">
<tr>
<td><span class="rowNumber">3</span>
<input value="Remove"
onclick="removeFee(this)" type="button">
<td><input name="monthly[2][fee]" size="11"
maxlength="11" value="125.00" type="text"
onclick="alert(this.name);">
</table>
</form>
<p>Click on an input to see it's name</p>


You may want to also change the index number in the first cell of the
row. That would be made easier if you remove the span element and put
the style on the td element, then you can re-number it as for the
control using innerHTML rather than some more complex scheme.
 
M

mikez302

Oops! I posted the wrong version of my code. The line was supposed
to be:
document.edit.elements['monthly[' + (parseInt(i) + 1) + '][fee]'].name
= 'monthly[' + i + '][fee]';

With the line like that, I was still getting the exact same problem.


Here is my code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://
www.w3.org/TR/html4/loose.dtd">
<html><head>
<meta http-equiv='Content-Type' content='text/html;
charset=ISO-8859-1'>
<meta http-equiv='Content-Language' content='en-us'>
<title>IE removeChild bug demo</title></head>
<body>
<script type='text/javascript'>var monthlyFees = [{"fee":"250.00"},
{"fee":"195.00"},{"fee":"125.00"}];
function removeFee(rowNum) {
var i, testBody = document.getElementById('fees').tBodies[0];
testBody.removeChild(testBody.rows[rowNum]);
for (i = rowNum; i < testBody.rows.length; i++) {
testBody.rows.cells[0].innerHTML = "<span class='rowNumber'>" +
(parseInt(i) + 1) + "</span> <input type='button' value='Remove'
class='remove' onclick='removeFee(" + i + ")'>";
document.edit.elements['monthly[' + (parseInt(i) + 1) + ']
[fee]'].name = 'monthly[' + i + '][fee]';
}
}
</script>
<form method='post' action='' name='edit'>
<table id='fees'>
<tr><td><span class="rowNumber">1</span> <input value="Remove"
onclick="removeFee(0)" type="button"></td><td><input name="monthly[0]
[fee]" size="11" maxlength="11" value="250.00" type="text"></td></tr>
<tr><td><span class="rowNumber">2</span> <input value="Remove"
onclick="removeFee(1)" type="button"></td><td><input name="monthly[1]
[fee]" size="11" maxlength="11" value="195.00" type="text"></td></tr>
<tr><td><span class="rowNumber">3</span> <input value="Remove"
onclick="removeFee(2)" type="button"></td><td><input name="monthly[2]
[fee]" size="11" maxlength="11" value="125.00" type="text"></td></tr></
table></form>
</body></html>

I updated the IEBugDemo1.html and IEBugDemo2.html pages accordingly.

Elias
 
E

Ed

Oops! I posted the wrong version of my code. The line was supposed
to be:
document.edit.elements['monthly[' + (parseInt(i) + 1) + '][fee]'].name
= 'monthly[' + i + '][fee]';

With the line like that, I was still getting the exact same problem.

I think the problem is explained here:

http://msdn2.microsoft.com/en-us/library/ms534184.aspx

"Microsoft JScript allows the name to be changed at run time. This
does not cause the name in the programming model to change in the
collection of elements ..."
 
M

mikez302

Argh, stupid IE. Is there any way to work around this problem? What
is the "programming model" anyway? Does this mean that the names I
use to access the form controls from within Javascript commands may be
different from the names that are submitted with the form?

I remember working on something kind of similar, where I had to
provide the ability to delete a row, and renumber all the ones below
it. I spent countless hours figuring out how to make it work in IE.
The only thing I could come up with that worked involved keeping a
copy of all the form values, removing the row to be deleted and all
the rows below it, then recreating the rows below the deleted ones by
creating new rows and cells and filling them with innerHTML commands,
using the copied form values. It worked, but it was kind of slow, and
the code was complex and hard to maintain.

I am willing to try something like that again if I have to, but I'm
looking for a simpler and more efficient way. My actual code for this
project has a bunch of fields and form controls on each row, each
containing its own row number. Doing something like I did last time
would be possible, but kind of ugly and slow.

This may be a little off-topic, but I have use parseInt function
because I was having trouble earlier putting the loop counter in the
element names. Without it, I would try to add 2 + 1, and the script
would interpret the 2 as a string, and come up with 21 instead of 3.
I have changed the script many times since I put the parseInt call, so
it may not have this problem. I am choosing to leave it in for now
just to have one less thing to confuse me as to why my script isn't
working. When (if?) I get it working, I'll experiment and see if it
will still work without the parseInt calls.

Elias
 
M

mikez302

I fixed the problem. In case anyone cares, I made my script so that
whenever the user deletes a row, it copies the data from the row below
the one to be deleted to the position of the deleted row. It then
keeps copying data from each row to the one before it, then removes
the last row of the table. I also have a way of inserting a row by
adding a blank row at the end of the table, then copying the data from
each row to the next row, starting with the next-to-last, and ending
with the row at the intended position. I then populate the "new" row
with default data. It is fast and it works in IE. I still haven't
tested it in Safari.

Elias
 

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

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top