calculating in table made by createElement and appendChild

K

kie

hello,

i have a table that creates and deletes rows dynamically using
createElement, appendChild, removeChild.
when i have added the required amount of rows and input my data, i
would like to calculate the totals in each row.

when i try however, i receive the error:

"Error: 'elements[...]' is null or not an object"

i have tried looping through all elements in the form and this gives
me the name & value of each element.
when i call the element by name however, i receive the above error.

it is easier to explain by cuting and pasting the source of my html
file below:

i'd greatly appreciate any pointers, here's the code.


<script language="JavaScript" type="text/JavaScript" >
<!--

var InternetExplorer = navigator.appName.indexOf("Microsoft") != -1;
var globalChanged = false;
var globalNewRowChanged = false;
var globalClaimLnsRowNum = 1;

function ex_amount(s_cur){
switch (s_cur)
{
case '1':
return '1.2';
break;
case '2':
return '0.6';
break;
case '3':
return '1.5';
break;
case '4':
return '2';
break;
case '5':
return '1';
break;
default:
return 0;
}
}

function detail_row(s_goods_id,s_unit_price,s_currency_id,s_quantity,s_uplift,s_total_line,s_incl_uplift,s_incl_uplift_euro)
{
var body=document.body;
var Table,theRow,aCell,aTextBox,tablebody2,rowSec2;
var theTable;
var rowSec1,currenttext;
var opt1,sel2;
var cellSec1,cellSec2,cellSec3,cellSec4,cellSec5;
var cellSec6,cellSec7,cellSec8,cellSec9,cellSec10;
theTable=document.getElementById('formation').getElementsByTagName('tbody')[0];
tablebody2 = document.createElement('TBODY');
rowSec2=document.createElement('TR');
cellSec2=document.createElement('TD');
sel2=document.createElement('select');
sel2.name='2d_cmb_goods__' + globalClaimLnsRowNum;
opt1=document.createElement('option');
opt1.value='9';
if (opt1.value==s_goods_id){opt1.setAttribute('selected',
true);}
opt1.innerHTML='Airconditioners';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='5';
if (opt1.value==s_goods_id){opt1.setAttribute('selected',
true);}
opt1.innerHTML='Audio Goods';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='13';
if (opt1.value==s_goods_id){opt1.setAttribute('selected',
true);}
opt1.innerHTML='Video Recorder';
sel2.appendChild(opt1);
cellSec2.appendChild(sel2);
rowSec2.appendChild(cellSec2);

cellSec3=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_unit_price);
aTextBox.size = '10';
aTextBox.name = '2d_txt_unit_price__' + globalClaimLnsRowNum;
cellSec3.appendChild(aTextBox);
rowSec2.appendChild(cellSec3);

cellSec4=document.createElement('TD');
sel2=document.createElement('select');
sel2.name='2d_cmb_currency__' + globalClaimLnsRowNum;
opt1=document.createElement('option');
opt1.value='1';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',
true);}
opt1.innerHTML='US$';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='2';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',
true);}
opt1.innerHTML='」STG';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='3';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',
true);}
opt1.innerHTML='A$';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='4';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',
true);}
opt1.innerHTML='NZ$';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='5';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',
true);}
opt1.innerHTML='BHAT';
sel2.appendChild(opt1);
cellSec4.appendChild(sel2);
rowSec2.appendChild(cellSec4);

cellSec5=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_quantity);
aTextBox.size = '5';
aTextBox.name = '2d_txt_quantity__' + globalClaimLnsRowNum;
cellSec5.appendChild(aTextBox);
rowSec2.appendChild(cellSec5);

cellSec6=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_total_line);
aTextBox.size = '5';
aTextBox.name = '2d_txt_total_line__' + globalClaimLnsRowNum;
cellSec6.appendChild(aTextBox);
rowSec2.appendChild(cellSec6);

cellSec7=document.createElement('TD');
sel2=document.createElement('select');
sel2.name='2d_cmb_uplift__' + globalClaimLnsRowNum;
opt1=document.createElement('option');
opt1.value='100';
if (opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='100%';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='105';
if (opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='105%';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='110';
if (opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='110%';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='0';
if (opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='other';
sel2.appendChild(opt1);
cellSec7.appendChild(sel2);
rowSec2.appendChild(cellSec7);

cellSec8=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_incl_uplift);
aTextBox.size = '3';
aTextBox.name = '2d_txt_incl_uplift__' + globalClaimLnsRowNum;
cellSec8.appendChild(aTextBox);
rowSec2.appendChild(cellSec8);

cellSec9=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_incl_uplift_euro);
aTextBox.name = '2d_txt_incl_uplift_euro__' +
globalClaimLnsRowNum;
aTextBox.size = '5';
aTextBox.onchange=function(){calculateTab(globalClaimLnsRowNum);}
cellSec9.appendChild(aTextBox);
rowSec2.appendChild(cellSec9);

cellSec10=document.createElement('TD');
ButtonSupprimer=document.createElement('input');
ButtonSupprimer.type = 'button';
ButtonSupprimer.value = 'remove';
ButtonSupprimer.onclick=function(){formation.removeChild(tablebody2)}
cellSec10.appendChild(ButtonSupprimer);
rowSec2.appendChild(cellSec10);

tablebody2.appendChild(rowSec2);
formation.appendChild(tablebody2);
globalClaimLnsRowNum = globalClaimLnsRowNum + 1;
}

function calculateTab(n_line_no){

var oForm = document.forms[0];

alert('line number = ' + n_line_no);

oForm.elements['2d_txt_total_line__' + n_line_no].value =
nz(oForm.elements['2d_txt_unit_price__' + n_line_no].value) *
nz(oForm.elements['2d_txt_quantity__' + n_line_no].value);
}

function nz(s_val){
var s_datatype = "undefined";
if(s_val==null || s_val=='' || isNaN(s_val) ||
typeof(s_val)==s_datatype){
//return isNaN(num)?0:num;
return 0;
}
else{
//alert(s_val);
return parseFloat(s_val);
}
}

function resetForm(){
var oForm = document.forms[0];
for (i=0; i<oForm.elements.length; i++){
oForm.elements.value = "";
}
}

function setLabel(s_message,s_label_name) {
document.all(s_label_name).innerText = s_message;
}
//-->
</script>

<html>
<head>
</head>
<body>
<form method="post" action="" id="form1" name="form1">
<table>
<tr>
<td width='4%'>
<tr>
<td colspan='6'> </td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type="button" name="Button2" value="change label"
onclick=setlabel('here is some different text','label_detail');>
</td>
<td><input type="button" name="Button" value="calculate"
onclick=calculateTab('1');></td>
<td colspan='1' align='right'>&nbsp;</td>
<td>
<input name='cmd_detail_new' type='button' id='cmd_detail_new'
value='add row [click here]' onclick=detail_row();>
</td>
<td>&nbsp;</td>
<td colspan='3' align='right'>total </td>
<td>
<input name='2d_txt_detail_total' type='text'
id='2d_txt_detail_total' value='' size='10'
onChange=document.forms[0].elements['txt_haschanged'].value ='yes';>
</td>
<td width='16%'>=EUR</td>
<td width='30%'>
<input name='2d_txt_detail_total_euro' type='text'
id='2d_txt_detail_total_euro' value='' size='10'
onChange=document.forms[0].elements['txt_haschanged'].value
='yes';></td>
</td>
</tr>
<tr>
<td colspan='6'></td>
</tr>
</table>
</td>
</tr>
</table>


<table>
<tr>
<td height='20' valign='top'>
<table width='100%' border='1' cellpadding='0' cellspacing='0'
valign='top' id='formation'>
<tr>
<td width='15%'>&nbsp;<b>GOODS</b></td>
<td width='5%'>&nbsp;<b>UNIT PRICE</b></td>
<td width='10%'>&nbsp;<b>CURRENCY</b></td>
<td width='5%'>&nbsp;<b>QUANTITY</b></td>
<td width='5%'>&nbsp;<b>TOTAL LINE</b></td>
<td width='5%'>&nbsp;<b>UPLIFT (%)</b></td>
<td width='5%'>&nbsp;<b>INCL. UPLIFT</b></td>
<td width='5%'>&nbsp;<b>=EUR</b></td>
<td width='5%'>&nbsp;<b>&nbsp;</b></td>
</tr>
<tr>
<td colspan='10'>
<label id='label_detail'>NO RECORDS FOUND</label>
</td>
</tr>
<input type='text' id='2d_txt_detail_ids'
name='2d_txt_detail_ids' value=''>
<input type='text' id='txt_update_ids' name='txt_update_ids'
value=''>
</table> </td>
</tr>
</table>
</form>
</body>
</html>


thanks for any help, kie

p.s. the funtion setLabel that addresses: <label id='label_detail'>
fails for a reason unknown to me, any pointers on this too would be
greatly appreciated.
 
D

Dom Leonard

kie said:
i have a table that creates and deletes rows dynamically using
createElement, appendChild, removeChild.
when i have added the required amount of rows and input my data, i
would like to calculate the totals in each row.

when i try however, i receive the error:

"Error: 'elements[...]' is null or not an object"

i have tried looping through all elements in the form and this gives
me the name & value of each element.
when i call the element by name however, i receive the above error.

it is easier to explain by cuting and pasting the source of my html
file below:

i'd greatly appreciate any pointers, here's the code.
<snip>

hi Kie,
had a look and wrote down things as I encountered them. IE bug looks
like the biggest problem:

HTML:
Generally include script elements in the head section or (less often) in
the body section.
(moved script to head section when testing)

Element names beginning with "2d" are invalid HTML:
<cite>
ID and NAME tokens must begin with a letter ([A-Za-z]) and may be
followed by any number of letters, digits ([0-9]), hyphens ("-"),
underscores ("_"), colons (":"), and periods (".").
</cite>
(low priority in that it works in some browsers. Changed "2d" to "Two_d"
when testing)


Handler text used in HTML attribute values should be quoted
<cite>
In certain cases, authors may specify the value of an attribute without
any quotation marks. The attribute value may only contain letters (a-z
and A-Z), digits (0-9), hyphens (ASCII decimal 45), periods (ASCII
decimal 46), underscores (ASCII decimal 95), and colons (ASCII decimal
58). We recommend using quotation marks even when it is possible to
eliminate them.
</cite>
This rules out most javascript code.
(low priority in that it works in some browsers. Didn't change during test)



DOM errors:
To access elements by id, use document.getElementById.
Access using named properties of the window object is not standard.
Hence changed

theTable=document.getElementById('formation').getElementsByTagName('tbody')[0];
to
var formation = document.getElementById('formation');
theTable=formation.getElementsByTagName('tbody')[0];
in order to allow later event handlers to access the formation value
held in the closure.
[This may set up memory leaks under IE. Walking the DOM in an
unbeforeunload handler and setting event handlers to null is one
possible workaround, although changing use of formation to
getElementById('formation') may be a better idea.]

Also changed setLabel use of document.all to document.getElementById.
Note that innerText is not a standard method of altering the text
content of element nodes. This has been covered in other posts with
bodies containing "createTextNode". Even "innerHTML" is more cross
browser compliant.
function setLabel(s_message,s_label_name) {
document.getElementById(s_label_name).innerHTML = s_message;
}
tested.

(priority for WWW documents)


Browser Detection
IE conditional comments can reliably check for Internet Explorer
(from Lasse Reichstein Nielsen, 27/9/03)

<script type="text/javascript">
var isIE = false;
</script>
<!--[if IE]>
<script type="text/javascript">
isIE = true;
</script>
<!--[end if]-->

Internet Explorer Bug
This is the killer. Form field elements added to the DOM are not
reflected as named properties of the FORM object. Code fix after adding
elements and before accessing them by name:

function makeFieldUnionByName(oEls, name)
{
var result = null;
var resultArray = false;
for( var i = 0; i < oEls.length; ++i)
{
if( oEls.name!=name)
continue;
if( result && !resultArray)
{ result = [result];
resultArray = true;
}
if(resultArray)
result[result.length]=oEls;
else
result=oEls;
}
return result;
}
function fixFormBug( oForm) // Internet Explorer only
{
var oEls = oForm.elements;
var name;
for( var i = 0; i < oEls.length; ++i)
{
name = oEls.name;
if(name && !oForm[ name])
oForm[name] = makeFieldUnionByName(oEls,name);
}
}
/* ....
and then in calculateTab
...
*/
var oForm = document.forms[0];
if(isIE)
fixFormBug( oForm)
...

Case Sensitivity
call setLabel using the same combination of case.
onchange="setLabel(....)"

========
HTH
Dom
 
K

kie

wow! Dom, that really helped me a lot.

what you said made sense and i can now calculate all the rows after
updating each cell. this is what i set out to do.

to increase efficiency i tried to calculate each row individually,
using the line: "aTextBox.onchange=function(){calculateTab(globalClaimLnsRowNum);}"
to send the current row number to the calculateTab function.
globalClaimLnsRowNum is a global variable though, so the value in
calculateTab is always the maximum value of globalClaimLnsRowNum. is
there a way to calculate a single row?

copy and pasting the below code will present an example, it also shows
dom's solutions in practice:

<html>
<head>
<script language="JavaScript" type="text/JavaScript" >
<!--

var InternetExplorer = navigator.appName.indexOf("Microsoft") != -1;
var globalChanged = false;
var globalNewRowChanged = false;
var globalClaimLnsRowNum = 1;
var isIE = false;
// make check - find IE = true.
isIE = true;

function ex_amount(s_cur){
switch (s_cur)
{
case '1':
return '1.2';
break;
case '2':
return '0.6';
break;
case '3':
return '1.5';
break;
case '4':
return '2';
break;
case '5':
return '1';
break;
default:
return 0;
}
}

function detail_row(s_goods_id,s_unit_price,s_currency_id,s_quantity,s_uplift,s_total_line,s_incl_uplift,s_incl_uplift_euro)
{
var body=document.body;
var Table,theRow,aCell,aTextBox,tablebody2,rowSec2;
var theTable;
var rowSec1,currenttext;
var opt1,sel2;
var cellSec1,cellSec2,cellSec3,cellSec4,cellSec5;
var cellSec6,cellSec7,cellSec8,cellSec9,cellSec10;
var formation = document.getElementById('formation');
theTable=formation.getElementsByTagName('tbody')[0];

tablebody2 = document.createElement('TBODY');
rowSec2=document.createElement('TR');
cellSec2=document.createElement('TD');
sel2=document.createElement('select');
sel2.name='two_d_cmb_goods__' + globalClaimLnsRowNum;
sel2.onchange=function(){calculateTab(globalClaimLnsRowNum);}
opt1=document.createElement('option');
opt1.value='9';
if (opt1.value==s_goods_id){opt1.setAttribute('selected',true);}
opt1.innerHTML='Airconditioners';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='5';
if (opt1.value==s_goods_id){opt1.setAttribute('selected',true);}
opt1.innerHTML='Audio Goods';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='13';
if (opt1.value==s_goods_id){opt1.setAttribute('selected',true);}
opt1.innerHTML='Video Recorder';
sel2.appendChild(opt1);
cellSec2.appendChild(sel2);
rowSec2.appendChild(cellSec2);

cellSec3=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_unit_price);
aTextBox.size = '10';
aTextBox.name = 'two_d_txt_unit_price__' + globalClaimLnsRowNum;
aTextBox.onchange=function(){calculateTab(globalClaimLnsRowNum);}
cellSec3.appendChild(aTextBox);
rowSec2.appendChild(cellSec3);

cellSec4=document.createElement('TD');
sel2=document.createElement('select');
sel2.name='two_d_cmb_currency__' + globalClaimLnsRowNum;
sel2.onchange=function(){calculateTab(globalClaimLnsRowNum);}
opt1=document.createElement('option');
opt1.value='1';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',true);}
opt1.innerHTML='US$';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='2';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',true);}
opt1.innerHTML='」STG';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='3';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',true);}
opt1.innerHTML='A$';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='4';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',true);}
opt1.innerHTML='NZ$';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='5';
if (opt1.value==s_currency_id){opt1.setAttribute('selected',true);}
opt1.innerHTML='BHAT';
sel2.appendChild(opt1);
cellSec4.appendChild(sel2);
rowSec2.appendChild(cellSec4);

cellSec5=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_quantity);
aTextBox.size = '5';
aTextBox.name = 'two_d_txt_quantity__' + globalClaimLnsRowNum;
aTextBox.onchange=function(){calculateTab(globalClaimLnsRowNum);}
cellSec5.appendChild(aTextBox);
rowSec2.appendChild(cellSec5);

cellSec6=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_total_line);
aTextBox.size = '5';
aTextBox.name = 'two_d_txt_total_line__' + globalClaimLnsRowNum;
aTextBox.onchange=function(){calculateTab(globalClaimLnsRowNum);}
cellSec6.appendChild(aTextBox);
rowSec2.appendChild(cellSec6);

cellSec7=document.createElement('TD');
sel2=document.createElement('select');
sel2.name='two_d_cmb_uplift__' + globalClaimLnsRowNum;
sel2.onchange=function(){calculateTab(globalClaimLnsRowNum);}
opt1=document.createElement('option');
opt1.value='100';
if (opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='100%';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='105';
if (opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='105%';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='110';
if (opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='110%';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='0';
if (opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='other';
sel2.appendChild(opt1);
cellSec7.appendChild(sel2);
rowSec2.appendChild(cellSec7);

cellSec8=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_incl_uplift);
aTextBox.size = '3';
aTextBox.name = 'two_d_txt_incl_uplift__' +
globalClaimLnsRowNum;
aTextBox.onchange=function(){calculateTab(globalClaimLnsRowNum);}
cellSec8.appendChild(aTextBox);
rowSec2.appendChild(cellSec8);

cellSec9=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_incl_uplift_euro);
aTextBox.name = 'two_d_txt_incl_uplift_euro__' +
globalClaimLnsRowNum;
aTextBox.size = '5';
aTextBox.onchange=function(){calculateTab(globalClaimLnsRowNum);}
cellSec9.appendChild(aTextBox);
rowSec2.appendChild(cellSec9);

cellSec10=document.createElement('TD');
ButtonSupprimer=document.createElement('input');
ButtonSupprimer.type = 'button';
ButtonSupprimer.value = 'remove';
ButtonSupprimer.onclick=function(){formation.removeChild(tablebody2)}
cellSec10.appendChild(ButtonSupprimer);
rowSec2.appendChild(cellSec10);

tablebody2.appendChild(rowSec2);
formation.appendChild(tablebody2);
globalClaimLnsRowNum = globalClaimLnsRowNum + 1;
}

function calculateTab(n_line_no){

alert(n_line_no);

var oForm = document.forms[0];
var a_ids = new Array;
var j=0;
var s_cur = 0;
var n_uplift = 0;
var n_uplift_euro = 0;

if(isIE)
fixFormBug( oForm)

// make array of ids were dealing with
for (i=0; i<oForm.elements.length; i++){
if(oForm.elements.name.indexOf("two_d_") == 0){
if(oForm.elements.name.indexOf("two_d_cmb_goods__") == 0){
n = oForm.elements.name.replace(/two_d_cmb_goods__/g,'');
a_ids[j] = n;
j = j+1;
}
}
}

// use array of ids to calculate all values in all rows
for (i=0; i<j; i++){
oForm.elements['two_d_txt_total_line__' + a_ids].value =
(parseFloat(nz(oForm.elements['two_d_txt_unit_price__' +
a_ids].value)) * parseFloat(nz(oForm.elements['two_d_txt_quantity__'
+ a_ids].value)));
oForm.elements['two_d_txt_incl_uplift__' + a_ids].value =
(parseFloat(nz(oForm.elements['two_d_txt_total_line__' +
a_ids].value)) * (parseFloat(nz(oForm.elements['two_d_cmb_uplift__'
+ a_ids].value))/100));
oForm.elements['two_d_txt_incl_uplift_euro__' + a_ids].value =
(parseFloat(oForm.elements['two_d_txt_incl_uplift__' +
a_ids].value) * (ex_amount(oForm.elements['two_d_cmb_currency__' +
a_ids].value)));
n_uplift = n_uplift + nz(oForm.elements['two_d_txt_incl_uplift__' +
a_ids].value);
n_uplift_euro = n_uplift_euro +
nz(oForm.elements['two_d_txt_incl_uplift_euro__' + a_ids].value);
}

// if currencies are not all equal, cannot show sum of incl. uplift,
only sum of euro
for (i=0; i<j; i++){
if((s_cur != oForm.elements['two_d_cmb_currency__' +
a_ids].value) & (s_cur != 0)){
n_uplift = 0
break;
}
s_cur = oForm.elements['two_d_cmb_currency__' + a_ids].value;
}

// assign total values
oForm.elements['two_d_txt_detail_total'].value = nz(n_uplift);
oForm.elements['two_d_txt_detail_total_euro'].value =
nz(n_uplift_euro);

}

function nz(s_val){
var s_datatype = "undefined";
if(s_val==null || s_val=='' || isNaN(s_val) ||
typeof(s_val)==s_datatype){
//return isNaN(num)?0:num;
return 0;
}
else{
//alert(s_val);
return parseFloat(s_val);
}
}

function resetForm(){
var oForm = document.forms[0];
for (i=0; i<oForm.elements.length; i++){
oForm.elements.value = "";
}
}

function setLabel(s_message,s_label_name) {
document.getElementById(s_label_name).innerHTML = s_message;
}


function makeFieldUnionByName(oEls, name)
{
var result = null;
var resultArray = false;
for( var i = 0; i < oEls.length; ++i)
{
if( oEls.name!=name)
continue;
if( result && !resultArray)
{ result = [result];
resultArray = true;
}
if(resultArray)
result[result.length]=oEls;
else
result=oEls;
}
return result;
}
function fixFormBug( oForm) // Internet Explorer only
{
var oEls = oForm.elements;
var name;
for( var i = 0; i < oEls.length; ++i)
{
name = oEls.name;
if(name && !oForm[ name])
oForm[name] = makeFieldUnionByName(oEls,name);
}
}


//-->
</script>

</head>
<body>
<form method="post" action="" id="form1" name="form1">
<table>
<tr>
<td width='4%'>
<tr>
<td colspan='6'> </td>
</tr>
<tr>
<td>&nbsp;</td>
<td><input type='button' name='Button2' value='change label'
onclick=setLabel('','label_detail');>
</td>
<td><input type="button" name="Button" value="calculate"
onclick=calculateTab(1);></td>
<td colspan='1' align='right'>&nbsp;</td>
<td>
<input name='cmd_detail_new' type='button' id='cmd_detail_new'
value='add row [click here]'
onclick=detail_row();setLabel('','label_detail');>
</td>
<td>&nbsp;</td>
<td colspan='3' align='right'>total </td>
<td>
<input name='two_d_txt_detail_total' type='text'
id='two_d_txt_detail_total' value='' size='10'
onChange=document.forms[0].elements['txt_haschanged'].value ='yes';>
</td>
<td width='16%'>=EUR</td>
<td width='30%'>
<input name='two_d_txt_detail_total_euro'
type='text'id='two_d_txt_detail_total_euro' value='' size='10'
onChange=document.forms[0].elements['txt_haschanged'].value='yes';></td>
</td>
</tr>
<tr>
<td colspan='6'></td>
</tr>
</table>
</td>
</tr>
</table>


<table>
<tr>
<td height='20' valign='top'>
<table width='100%' border='1' cellpadding='0' cellspacing='0'
valign='top' id='formation'>
<tr>
<td width='15%'>&nbsp;<b>GOODS</b></td>
<td width='5%'>&nbsp;<b>UNIT PRICE</b></td>
<td width='10%'>&nbsp;<b>CURRENCY</b></td>
<td width='5%'>&nbsp;<b>QUANTITY</b></td>
<td width='5%'>&nbsp;<b>TOTAL LINE</b></td>
<td width='5%'>&nbsp;<b>UPLIFT (%)</b></td>
<td width='5%'>&nbsp;<b>INCL. UPLIFT</b></td>
<td width='5%'>&nbsp;<b>=EUR</b></td>
<td width='5%'>&nbsp;<b>&nbsp;</b></td>
</tr>
<tr>
<td colspan='10'>
<label id="label_detail">NO RECORDS FOUND</label>
</td>
</tr>
<input type='text'
id='two_d_txt_detail_ids'name='two_d_txt_detail_ids' value=''>
<input type='text' id='txt_update_ids' name='txt_update_ids'
value=''>
</table> </td>
</tr>
</table>
</form>
</body>
</html>

thanks for any help,

kie
 
D

Dom Leonard

kie wrote:
i can now calculate all the rows after
updating each cell. this is what i set out to do.

to increase efficiency i tried to calculate each row individually,
This might be useful, but be careful: if an existing row is changed, how
to make certain page totals are updated correctly?
using the line: "aTextBox.onchange=function(){calculateTab(globalClaimLnsRowNum);}"
to send the current row number to the calculateTab function.
globalClaimLnsRowNum is a global variable though, so the value in
calculateTab is always the maximum value of globalClaimLnsRowNum. is
there a way to calculate a single row?

You could determine the row number of an element which has changed by
assigning the TR in which it resides an id based on row number, and
searching for the TR and its id in the parentNode chain of an element.

After looking at the code further I noticed a few more things. For your
own interest I suggest placing doctype and character set definitions:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

at the start of the document and validating it at:
http://validator.w3.org

<html>
<head>
<script language="JavaScript" type="text/JavaScript" >
Note the language attribute is deprecated:
SGML comments can be omitted nowadays (unless you use a validation tool
that requires them)
var InternetExplorer = navigator.appName.indexOf("Microsoft") != -1;
This check is unreliable - apparently browsers forge the appName value
on a regular basis.
var globalChanged = false;
var globalNewRowChanged = false;
var globalClaimLnsRowNum = 1;
var isIE = false;
// make check - find IE = true.
isIE = true;

uh huh :)
Okay, the page is under development and the real test goes in later...

<snip>

All the event handlers in detail_row retain the activation object of
detail_row() in scope and probably leak memory under IE6. I made some
static functions to replace the nested function versions (not shown,
they could go into a single object as properties to save on name space)

function calculateAll()
{
calculateTab(globalClaimLnsRowNum);
}

function removeRow() // method of form element within row
{
for( var node = this.parentNode; node; node=node.parentNode)
if(node.tagName=="TR")
break;
node.parentNode.removeChild(node);
calculateAll();
}

and in the editor replaced all
function(){calculateTab(globalClaimLnsRowNum);}
with
calculateAll;

Now comes the gritty bit. In the HTML code posted, you do not provide a
TBODY element before accessing it in code (so the browser must supply
it), and insert rows as TBODY elements. In testing, I inserted the
missing TBODY tag and started inserting and deleting TR elements, so a
few lines need changing:


function detail_row(s_goods_id,s_unit_price,s_currency_id,s_quantity,s_uplift,s_total_line,s_incl_uplift,s_incl_uplift_euro)
{ ....
removed:
// theTable=formation.getElementsByTagName('tbody')[0];
inserted:
var tablebody2=formation.getElementsByTagName('tbody')[0];
removed:
// tablebody2 = document.createElement('TBODY');
....

aTextBox.onchange=function(){calculateTab(globalClaimLnsRowNum);}
This is one of the lines changed to
aTextBox.onchange=calculateAll;

....

Changed
ButtonSupprimer.onclick=function(){formation.removeChild(tablebody2)} to
ButtonSupprimer.onclick=removeRow;

....
After
rowSec2.appendChild(cellSec10);
inserted:
rowSec2.id = "row_" + globalClaimLnsRowNum;
and after
tablebody2.appendChild(rowSec2); removed:

....
function fixFormBug( oForm) // Internet Explorer only

[A cautionary note on this code fix. As written, it patches an IE form
object after elements have been added, but makes no repairs if field
elements have been removed. In this particular case that should be not
be a problem, but would test submission]....
The BODY used in testing (with various fixups) was:

<body>
<form method="post" action="" id="form1" name="form1">

<table>
<tbody>
<tr>
<td>&nbsp;</td>
<td>
<input type='button' name='Button2' value='change label'
onclick="setLabel('','label_detail');">
</td>
<td>
<input type="button" name="Button" value="calculate"
onclick="calculateTab(1);">
</td>
<td>&nbsp;</td>
<td>
<input name='cmd_detail_new' type='button' id='cmd_detail_new'
value='add row [click here]'
onclick="detail_row();setLabel('','label_detail');">
</td>
<td>&nbsp;</td>
<td align='right'>total </td>
<td>
<input name='two_d_txt_detail_total' type='text'
id='two_d_txt_detail_total' value='' size='10'
onchange="form.elements['txt_haschanged'].value ='yes';">
</td>
<td width='16%'>=EUR</td>
<td width='30%'>
<input name='two_d_txt_detail_total_euro' type='text'
id='two_d_txt_detail_total_euro' value='' size='10'
onchange="form.elements['txt_haschanged'].value='yes';">
</td>
</tr>
</tbody>
</table>

<!-- table in table removed, head and body elements inserted -->

<table width='100%' border='1' cellpadding='0' cellspacing='0'
valign='top' id='formation'>
<thead>
<tr>
<td width='15%'>&nbsp;<b>GOODS</b></td>
<td width='5%'>&nbsp;<b>UNIT PRICE</b></td>
<td width='10%'>&nbsp;<b>CURRENCY</b></td>
<td width='5%'>&nbsp;<b>QUANTITY</b></td>
<td width='5%'>&nbsp;<b>TOTAL LINE</b></td>
<td width='5%'>&nbsp;<b>UPLIFT (%)</b></td>
<td width='5%'>&nbsp;<b>INCL. UPLIFT</b></td>
<td width='5%'>&nbsp;<b>=EUR</b></td>
<td width='5%'>&nbsp;<b>&nbsp;</b></td>
</tr>
<tr>
<td colspan='9'>
<label id="label_detail">NO RECORDS FOUND</label>
</td>
</tr>

<!-- insert missing TR, TD tags, put spaces between attributes -->

<tr>
<td colspan='9'>
<input type='text' id='two_d_txt_detail_ids'
name='two_d_txt_detail_ids' value=''>
<input type='text' id='txt_update_ids'
name='txt_update_ids' value=''>
</td>
</tr>
</thead>

<tbody><!-- empty at this stage -->
</tbody>
</table>
</form>
</body>

========

Finally, if you want to find the row number from an event handler set on
element within a row, walk up the DOM to find the TR element (similar to
deleting a row) and extract the row number from the id value assigned in
detail_row().

HTH and good luck,
Dom
 
K

kie

thanks for your advice Dom, you're making a lot of things clearer for
me
After looking at the code further I noticed a few more things. For your
own interest I suggest placing doctype and character set definitions:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

at the start of the document and validating it at:
http://validator.w3.org

I have done this, one thing I noticed was that I couldn't control the
size of my select boxes:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<META http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1">
<html>
<head>
<title>Untitled Document</title>
</head>
<body>
<SELECT id='test' name='test' style='width:50'>
<OPTION name='select one' value='' selected>
<OPTION name='1h_cmb_policy' value='YY10001878'>YY10001878
<OPTION name='1h_cmb_policy' value='YY10001884'>YY10001884
</OPTION>
</SELECT>
</body>
</html>

if I remove the first 2 lines from the code above, the select box will
become 50 pixels in width.
If I include the first 2 lines. the select box size is the width of
the longest string contained in it.

I tried adding the line:
"sel2.style.width ='100';"

after
rowSec2.id = 'row_' + globalClaimLnsRowNum;
cellSec2=document.createElement('TD');
and I had no problems.
Are the style.width used in createElement and the style='width:50' in
HTML much different commands?

I've been studying the code you used to restrict a loop to one row; by
copying the code from removeRow() into CalculateAll() I have tried and
failed to extract the row number or "TR" id

function calculateAll()
{
for( var node = this.parentNode; node; node=node.parentNode){
//if(node.tagName=="TR"){
//alert('node name and current row id: ' + node.Name);
//alert('node name and current row id: ' +
document.forms[0].elements.node.name);
//alert('node name and current row id: ' +
document.forms[0].elements[node].name);
//alert('node name and current row id: ' + node);
alert('node name and current row id: ' +
document.getElementById(node));
// }
}
calculateTab(2,globalClaimLnsRowNum);
}

I would expect that if "node" is an object, then it should have a
name. And I expected that name to be "row_1", "row_2" etc, from the
line:

"rowSec2.id = 'row_' + globalClaimLnsRowNum;"

am I mistaking "node" for an "element"? I have changed the attributes
of "TR" elements in the past using lines such as:

"document.getElementById(tdId).style.backgroundColor = '#000000';"

With the "TBODY" element missing, the browser creates it. does that
mean before i was unnecesasarily creating a new "TBODY" element each
time the user clicks "Add Row"?
Memory dissipation is also halted by calling a function instead of
inserting the code into the elements onchange handler?

I need to find out more about nodes, apart from that it works great,
and is easier to understand now it is better laid out (below),

thanks again,

kie

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<META http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1">
<html>
<head>
<meta name="Author" content="kie">
<meta name="Copyright" content="2003">
<meta name="keywords" content="">
<meta name="description" content="">
<link rel="stylesheet" type="text/css"
href="http://192.168.100.215/g-macs/lib/stylesheet.css">
<script>
<!--
// browser detection
// "InternetExplorer = navigator.appName.indexOf("Microsoft") != -1;"
// This check is unreliable - apparently browsers forge the appName
value on a regular basis.
//
// checks should be made using the javascript that will be in the page
for certain safety
/**/
var s_browser = "";
// W3C & Netscape 6
if(document.getElementById) {
s_browser = "netscape6";
}
// browser is IE 4+:
else if (document.all) {
s_browser = "ie4";
}
// browser is Netscape 4+:
else if (document.layers) {
s_browser = "netscape4";
}
else {
s_browser = "";
}

//-->
</script>

<script type="text/javaScript" >
// JavaScript Document
// lib/js_functions.js - contents:

// setClaimID
// AllCheck_onclick
// mWillSave
// showHide
//calculateTab(3) used to calculate values in individual pages esp.
03_page.asp
<!--

var InternetExplorer = navigator.appName.indexOf("Microsoft") != -1;
var globalChanged = false;
var globalNewRowChanged = false;
var globalClaimLnsRowNum = 1;
var isIE = false;

isIE = true;

function ex_amount(s_cur){
switch (s_cur)
{
case '1':
return '1.2';
break;
case '2':
return '0.6';
break;
case '3':
return '1.5';
break;
case '4':
return '2';
break;
case '5':
return '1';
break;
default:
return 0;
}
}

function detail_row(s_goods_id,s_unit_price,s_currency_id,s_quantity,s_uplift,s_total_line,s_incl_uplift,s_incl_uplift_euro)
{
var body=document.body;
var Table,theRow,aCell,aTextBox,tablebody2,rowSec2;
var theTable;
var rowSec1,currenttext;
var opt1,sel2;
var cellSec1,cellSec2,cellSec3,cellSec4,cellSec5;
var cellSec6,cellSec7,cellSec8,cellSec9,cellSec10;
var formation = document.getElementById('formation');
var tablebody2=formation.getElementsByTagName('tbody')[0];
rowSec1=document.createElement('TR');
cellSec1=document.createElement('TD');
currenttext=document.createTextNode('Intitul');
rowSec2=document.createElement('TR');
rowSec2.id = 'row_' + globalClaimLnsRowNum;
cellSec2=document.createElement('TD');
sel2=document.createElement('select');
sel2.style.width ='100';
sel2.name='two_d_cmb_goods__' +
globalClaimLnsRowNum;
sel2.onchange=calculateAll;
opt1=document.createElement('option');
opt1.value='6';
if
(opt1.value==s_goods_id){opt1.setAttribute('selected', true);}
opt1.innerHTML='Vacuum Cleaner';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='2';
if
(opt1.value==s_goods_id){opt1.setAttribute('selected', true);}
opt1.innerHTML='Video Camera';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='1';
if
(opt1.value==s_goods_id){opt1.setAttribute('selected', true);}
opt1.innerHTML='Video Recorder';
sel2.appendChild(opt1);
cellSec2.appendChild(sel2);
rowSec2.appendChild(cellSec2);

cellSec3=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_unit_price);
aTextBox.size = '10';
aTextBox.name = 'two_d_txt_unit_price__' +
globalClaimLnsRowNum;
aTextBox.onchange=calculateAll;
cellSec3.appendChild(aTextBox);
rowSec2.appendChild(cellSec3);

cellSec4=document.createElement('TD');
sel2=document.createElement('select');
sel2.name='two_d_cmb_currency__' +
globalClaimLnsRowNum;
sel2.onchange=calculateAll
opt1=document.createElement('option');
opt1.value='1';
if
(opt1.value==s_currency_id){opt1.setAttribute('selected', true);}
opt1.innerHTML='US$';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='2';
if
(opt1.value==s_currency_id){opt1.setAttribute('selected', true);}
opt1.innerHTML='£STG';
sel2.appendChild(opt1);
sel2.appendChild(opt1);
cellSec4.appendChild(sel2);
rowSec2.appendChild(cellSec4);

cellSec5=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_quantity);
aTextBox.size = '5';
aTextBox.name = 'two_d_txt_quantity__' +
globalClaimLnsRowNum;
aTextBox.onchange=calculateAll
cellSec5.appendChild(aTextBox);
rowSec2.appendChild(cellSec5);

cellSec6=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_total_line);
aTextBox.size = '5';
aTextBox.name = 'two_d_txt_total_line__' +
globalClaimLnsRowNum;
aTextBox.onchange=calculateAll
cellSec6.appendChild(aTextBox);
rowSec2.appendChild(cellSec6);

cellSec7=document.createElement('TD');
sel2=document.createElement('select');
sel2.name='two_d_cmb_uplift__' +
globalClaimLnsRowNum;
sel2.onchange=function(){calculateTab(2,globalClaimLnsRowNum);}
opt1=document.createElement('option');
opt1.value='100';
if
(opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='100%';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='105';
if
(opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='105%';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='110';
if
(opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='110%';
sel2.appendChild(opt1);
opt1=document.createElement('option');
opt1.value='0';
if
(opt1.value==s_uplift){opt1.setAttribute('selected', true);}
opt1.innerHTML='other';
sel2.appendChild(opt1);
cellSec7.appendChild(sel2);
rowSec2.appendChild(cellSec7);

cellSec8=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_incl_uplift);
aTextBox.size = '3';
aTextBox.name = 'two_d_txt_incl_uplift__' +
globalClaimLnsRowNum;
aTextBox.onchange=calculateAll
cellSec8.appendChild(aTextBox);
rowSec2.appendChild(cellSec8);

cellSec9=document.createElement('TD');
aTextBox=document.createElement('input');
aTextBox.type = 'text';
aTextBox.value = nz(s_incl_uplift_euro);
aTextBox.name = 'two_d_txt_incl_uplift_euro__' +
globalClaimLnsRowNum;
aTextBox.size = '5';
aTextBox.onchange=calculateAll
cellSec9.appendChild(aTextBox);
rowSec2.appendChild(cellSec9);

cellSec10=document.createElement('TD');
ButtonSupprimer=document.createElement('input');
ButtonSupprimer.type = 'button';
ButtonSupprimer.value = 'remove';
ButtonSupprimer.onclick=removeRow;
cellSec10.appendChild(ButtonSupprimer);
rowSec2.appendChild(cellSec10);
tablebody2.appendChild(rowSec2);
formation.appendChild(tablebody2);
globalClaimLnsRowNum = globalClaimLnsRowNum + 1;
}

function calculateTab(n_tab_no,n_line_no){

var oForm = document.forms[0];
var n;
var t;
var a_ids = new Array;
var j=0;

switch (n_tab_no)
{
case 1: // header
break;
case 2: // detail

var s_cur = 0;
var n_uplift = 0;
var n_uplift_euro = 0;

if(s_browser = "ie4"){
fixFormBug(oForm)
}

// make array of ids were dealing with
for (i=0; i<oForm.elements.length; i++){
if(oForm.elements.name.indexOf("two_d_") == 0){
if(oForm.elements.name.indexOf("two_d_cmb_goods__") == 0){
n = oForm.elements.name.replace(/two_d_cmb_goods__/g,'');
a_ids[j] = n;
j = j+1;
}
}
}

// use array of ids to calculate all values in all rows
for (i=0; i<j; i++){
//alert(n_tab_no+' ' +n_line_no + ' ' + a_ids);
oForm.elements['two_d_txt_total_line__' + a_ids].value =
(parseFloat(nz(oForm.elements['two_d_txt_unit_price__' +
a_ids].value)) * parseFloat(nz(oForm.elements['two_d_txt_quantity__'
+ a_ids].value)));
oForm.elements['two_d_txt_incl_uplift__' + a_ids].value =
(parseFloat(nz(oForm.elements['two_d_txt_total_line__' +
a_ids].value)) * (parseFloat(nz(oForm.elements['two_d_cmb_uplift__'
+ a_ids].value))/100));
oForm.elements['two_d_txt_incl_uplift_euro__' + a_ids].value =
(parseFloat(oForm.elements['two_d_txt_incl_uplift__' +
a_ids].value) * (ex_amount(oForm.elements['two_d_cmb_currency__' +
a_ids].value)));
n_uplift = n_uplift + nz(oForm.elements['two_d_txt_incl_uplift__'
+ a_ids].value);
n_uplift_euro = n_uplift_euro +
nz(oForm.elements['two_d_txt_incl_uplift_euro__' + a_ids].value);
}

// if currencies are not all equal, cannot show sum of incl.
uplift, only sum of euro
for (i=0; i<j; i++){
if((s_cur != oForm.elements['two_d_cmb_currency__' +
a_ids].value) & (s_cur != 0)){
n_uplift = 0
break;
}
s_cur = oForm.elements['two_d_cmb_currency__' + a_ids].value;
}

// assign total values
oForm.elements['two_d_txt_detail_total'].value = nz(n_uplift);
oForm.elements['two_d_txt_detail_total_euro'].value =
nz(n_uplift_euro);
break;
case 3: // pay rec
break;
case 4: // banking
break;
case 5: // p uk
break;
default:
false;
}
}

function makeFieldUnionByName(oEls, name)
{
var result = null;
var resultArray = false;
for( var i = 0; i < oEls.length; ++i)
{
if( oEls.name!=name)
continue;
if( result && !resultArray)
{ result = [result];
resultArray = true;
}
if(resultArray)
result[result.length]=oEls;
else
result=oEls;
}
return result;
}

function fixFormBug(oForm) // Internet Explorer only
{
var oEls = oForm.elements;
var name;
for( var i = 0; i < oEls.length; ++i)
{
name = oEls.name;
if(name && !oForm[ name])
oForm[name] = makeFieldUnionByName(oEls,name);
}
}

function calculateAll()
{
for( var node = this.parentNode; node; node=node.parentNode){
//if(node.tagName=="TR"){
//alert('node name and current row id: ' + node.Name);
//alert('node name and current row id: ' +
document.forms[0].elements.node.name);
//alert('node name and current row id: ' +
document.forms[0].elements[node].name);
//alert('node name and current row id: ' + node);
alert('node name and current row id: ' +
document.getElementById(node));
// }
}
calculateTab(2,globalClaimLnsRowNum);
}

function removeRow() // method of form element within row
{
for( var node = this.parentNode; node; node=node.parentNode)
if(node.tagName=="TR")
break;
node.parentNode.removeChild(node);
calculateAll();
}


function nz(s_val){
var s_datatype = "undefined";
if(s_val==null || s_val=='' || isNaN(s_val) ||
typeof(s_val)==s_datatype){
//return isNaN(num)?0:num;
return 0;
}
else{
//alert(s_val);
return parseFloat(s_val);
}
}

function setLabel(objectId,s_label_name,is_element) {
if (is_element=='true'){
var n=document.all(objectId).length;
for(i=0;i<n;++i) {
if(document.all(objectId).options.selected) {
document.all(s_label_name).innerText =
document.all(objectId).options.text;
}
}
}else{
document.getElementById(s_label_name).innerHTML = objectId;
}
}
//-->
</script>

</HEAD>
<BODY>
<form method="post" action="" id="form_h" name="form_h">
<table>
<tr>
<td width='4%'>
<tr>
<td colspan='6'> </td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
</td>
<td>&nbsp;</td>
<td colspan='1' align='right'>&nbsp;</td>
<td>
<input name='cmd_detail_new' type='button'
id='cmd_detail_new' value='ADD DETAIL [CLICK HERE]'
onclick=detail_row();>
</td>
<td>&nbsp;</td>
<td colspan='3' align='right'>DETAIL TOTAL / LOCAL </td>
<td>
<input name='two_d_txt_detail_total' type='text'
id='two_d_txt_detail_total' value='' size='10'>
</td>
<td width='16%'>=EUR</td>
<td width='30%'>
<input name='two_d_txt_detail_total_euro' type='text'
id='two_d_txt_detail_total_euro' value='' size='10'></td>
</td>
</tr>
<tr>
<td colspan='6'></td>
</tr>
</table>
</td>
</tr>
</table>
<table>
<tr>
<td height='20' valign='top'>
<table width='100%' border='1' cellpadding='0' cellspacing='0'
valign='top' id='formation'>
<thead>
<tr>
<td width='15%'>&nbsp;<b>GOODS</b></td>
<td width='5%'>&nbsp;<b>UNIT PRICE</b></td>
<td width='10%'>&nbsp;<b>CURRENCY</b></td>
<td width='5%'>&nbsp;<b>QUANTITY</b></td>
<td width='5%'>&nbsp;<b>TOTAL LINE</b></td>
<td width='5%'>&nbsp;<b>UPLIFT (%)</b></td>
<td width='5%'>&nbsp;<b>INCL. UPLIFT</b></td>
<td width='5%'>&nbsp;<b>=EUR</b></td>
<td width='5%'>&nbsp;<b>&nbsp;</b></td>
</tr>
<thead>
<tbody>
<tr>
<td colspan='10'>
<label id='label_detail'>NO RECORDS FOUND</label>
</td>
</tr>
</tbody>
<input type='hidden' id='two_d_txt_detail_ids'
name='two_d_txt_detail_ids' value=''>
<input type='hidden' id='txt_update_ids' name='txt_update_ids'
value=''>
</table>
</td>
</tr>
</table>
</form>
</body>
</html>
 
L

Lasse Reichstein Nielsen

(e-mail address removed) (kie) writes:

[adding DOCTYPE that selects standard mode]
I have done this, one thing I noticed was that I couldn't control the
size of my select boxes:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
....
<SELECT id='test' name='test' style='width:50'>

In CSS, all (non-zero) lengths must have a unit. In this case, I assume
you mean 50 pixels, so it should be "style='width:50px'".

I quirks mode, browsers allow some errors, and missing units is one of
them. It is still illegal CSS.
if I remove the first 2 lines from the code above, the select box will
become 50 pixels in width.

Yes, because the browser humors you instead of following standards.

....
I tried adding the line:
"sel2.style.width ='100';"

That is no better. It should still be '100px'.
Are the style.width used in createElement and the style='width:50' in
HTML much different commands?

No, they are completely equivalent. The elem.style property corresponds
directly with the style attribute in HTML.

Still, the browser might be more linient in Javascript than in the
HTML/CSS parser.

(That was a lot of code to post!)
/L
 
D

Dom Leonard

kie wrote:

I have tried and
failed to extract the row number or "TR" id

function calculateAll()
{
for( var node = this.parentNode; node; node=node.parentNode){
//if(node.tagName=="TR"){
//alert('node name and current row id: ' + node.Name);
//alert('node name and current row id: ' +
document.forms[0].elements.node.name);
//alert('node name and current row id: ' +
document.forms[0].elements[node].name);
//alert('node name and current row id: ' + node);
alert('node name and current row id: ' +
document.getElementById(node));
// }
}
calculateTab(2,globalClaimLnsRowNum);
}

I would expect that if "node" is an object, then it should have a
name. And I expected that name to be "row_1", "row_2" etc, from the
line:

"rowSec2.id = 'row_' + globalClaimLnsRowNum;"

am I mistaking "node" for an "element"? I have changed the attributes
of "TR" elements in the past using lines such as:

"document.getElementById(tdId).style.backgroundColor = '#000000';"

"node" refers to the primary datatype of objects in the DOM which
support the Node interface, described in W3C DOM recommendations [1]. In
the above code, the variable named "node" simply means a DOM node
without regard to whether it is an element or something else (document,
text node, document fragment or whatever).

An element is a *node* which supports the Element interface. Elements
correspond to HTML tags. Under the Element interface the tag name is
available as node.tagName. Under the node interface of an element, the
tag name is also availabe as node.nodeName. A subtle difference is that
node.tagName is undefined on nodes which are *not* elements.

Name and id are separate attributes of element nodes, so if row number
information has been placed in the id attribute, id would be used to
retrieve it as in:

function getRowNumber(element) // for element within row id "row_xxxx"
{
for( var node = element.parentNode; node; node=node.parentNode)
{ if(node.tagName == "TR")
return + node.id.substring(4);
}
}

[The unary plus operator applied to a string is equivalent to
parseFloat(string,10)]

With the "TBODY" element missing, the browser creates it. does that
mean before i was unnecesasarily creating a new "TBODY" element each
time the user clicks "Add Row"?
That is something only you can answer - yes I assumed inserting multiple
TBODY elements was unnecessary, but didn't look up the HTML DTD either:
<cite>
<!ELEMENT TABLE - -
(CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+)>
Memory dissipation is also halted by calling a function instead of
inserting the code into the elements onchange handler?
True to the extent that multiple function objects and closures are not
created. The main reason for the suggestion, however, was to avoid
memory leaks in IE6. These are caused by *circular* references between
document nodes and javascript. The scope chain of a nested function
(assigned as an event handler) contains the activation object of the
call to its outer function, so if variables of the outer function refer
back to the DOM node, the circle is complete.

For example,

function detail_row(s_goods_ ...

var sel2; ...

sel2.onchange=function(){calculateTab(2,globalClaimLnsRowNum);}

is prime suspect material.

An alternative solution to your problem occured to me after the original
reply. Why not use a static *factory* function to create a handler,
which can pass a line number and avoid the requirement for an id value
on the TR. Untested, but along lines of

function calculateCall( tab_no, line_no)
{
return function(){ calculateTab(tab_no, line_no)}
}

and then

sel2.onchange = calculateCall( 2, globalClaimLnsRowNum);

on the understanding of that calculateTab would receive
globalClaimLnsRowNum as at the time calculateCall was called, not as
updated afterwards.

This approach will create multiple function objects and closures, but at
least they are on the smallish side :)
I need to find out more about nodes,
[1].

thanks again,
You're welcome. As Lasses noted for the style-pixel-width question, the
amount of code has become excessive for email posting [2]. Providing a
URL or posting separate cut-down code and code usage questions is the
way to go.

var s_browser = "";
// W3C & Netscape 6
if(document.getElementById) {
s_browser = "netscape6";
}
// browser is IE 4+:
else if (document.all) {
s_browser = "ie4";
}
// browser is Netscape 4+:
else if (document.layers) {
s_browser = "netscape4";
}
else {
s_browser = "";
}

An observation: page design and code are unlikely to ever work in
browsers lacking document.getElementById support. IIRC, IE4 does not
support nested functions, and NS4 does not support layers within forms
(forms within layers are ok).

</tbody>
<input type='hidden' id='two_d_txt_detail_ids'
name='two_d_txt_detail_ids' value=''>
<input type='hidden' id='txt_update_ids' name='txt_update_ids'
value=''>
</table>

INPUT elements are not amongst the permitted child elements of TABLE.
Basically they go outside all tables or within TD cells.

Happy testing,
Dom
==============
[1] W3C
<URL http://www.w3.org/TR/DOM-Level-2-Core >
<URL http://www.w3.org/TR/DOM-Level-2-HTML >
[2] Newgroup FAQ:
<URL: http://www.jibbering.com/faq/ >
 
D

Dom Leonard

Lasse Reichstein Nielsen wrote:

No, they are completely equivalent. The elem.style property corresponds
directly with the style attribute in HTML.

Still, the browser might be more linient in Javascript than in the
HTML/CSS parser.

Mozilla (at least) has an "almost" standards mode [1], triggered by the
HTML loose DTD, which IIRC permits setting dimensions as pixels by
default from within javascript as for NS4. Using the strict DTD triggers
full standards mode and ignores dimensions without unit values.

Also, thanks for the tip about IE conditional comments [2]. For
accuracy, the end comment uses the form:
<![endif]-->
which certainly wasn't my first guess :)

cheers,
Dom

=======
[1]
http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/ccomment_ovw.asp
 
D

Dr John Stockton

JRS: In article <[email protected]>, seen in
Dom Leonard <[email protected]
m.au> posted at Sun, 14 Sep 2003 04:01:55 :-
[The unary plus operator applied to a string is equivalent to
parseFloat(string,10)]

It is not equivalent. Consider the string "3k".

It is true that, in many cases where a programmer uses parseFloat, unary
+ would be better. "3l" should generally give an error, not 3.

And parseFloat only uses one argument.
 
L

Lasse Reichstein Nielsen

Dr John Stockton said:
JRS: In article <[email protected]>, seen in
Dom Leonard <[email protected]
m.au> posted at Sun, 14 Sep 2003 04:01:55 :-
[The unary plus operator applied to a string is equivalent to
parseFloat(string,10)]

It is not equivalent. Consider the string "3k".

Yes, unary plus is equivalent to the function "Number" (not used as a
constructor). The "Number" conversion function works exactly like the
automatic type conversions induced by operators, which is what unary
plus is (just like Boolean is equivalent to double-negation and the
implicit conversion performed by conditionals).

I can't really find a reason to use parseInt or parseFloat.

/L
 
D

Dom Leonard

Dom said:
function fixFormBug( oForm) // Internet Explorer only


[A cautionary note on this code fix. As written, it patches an IE form
object after elements have been added, but makes no repairs if field
elements have been removed. In this particular case that should be not
be a problem, but would test submission]

This has gone further. Testing shows what looks like two closely related
problems when inserting form field elements using DOM methods under IE:

Problem 1
==========

Form field elements given a "name" attribute alone (without an "id"),
are not made named properties of the FORM object when a field is
inserted into the document using appendChild().

The lack of the FORM property does not seem to upset form submission,
just programatic access to the fields. This problem occurs outside
tables so does not appear to be related to table usage in test code.

Providing an "id" attribute with the same value as the "name" attribute
of field elements appears to fix the problem, and is the work around I
would now suggest. It will not work, of course, for multiple field
elements, such as radio inputs, sharing the same name.

Problem 2
==========

Only the first radio inputs added to a FORM using appendChild() can be
navigated to using the keyboard. Any additional (radio) inputs can not
be set by clicking. Basically no radio group is created, and setting a
FORM property to an array of radio inputs sharing the same name fails to
improve the situation.

The only work around I could devise for this was to create radio inputs
by inserting HTML code into the innerHTML property of an outer element.

===========

Because of these findings, as well as the lack of handling for form
field deletes, I can only describe the fixFormBug() function as
intermediate test code. Kie, I would suggest monitoring this thread to
see if better solutions are proposed, but if not to remove the
fixFormBug code and supply id as well as name values for the form field
elements.

best regards,
Dom

====== cut down test code =======

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>IE6 DHTML Form Element test</title>

<script type="text/javascript">

var rowCount=0;

function addRow( form)
{
var tbody = document.getElementById("tableBody");
var tr = document.createElement("TR");
var td = document.createElement("TD");
var oText;
var oRadio;
var radioInnerHTML = "";
var oSel, oOpt;
var useInnerHTML = form.useInnerHTML.checked;
var includeId = form.includeId.checked;

oText = document.createElement("INPUT");
oText.type="text";
oText.name="text" + rowCount;
if(includeId)
oText.id = "text" + rowCount; // same as name
oText.value=rowCount;

if(useInnerHTML)
{ radioInnerHTML =
'<input type="radio" name="radioGroup" '
+ ' id="radio' + rowCount + '"'
+ ' value="radio' + rowCount + '"'
+ (rowCount ? "" : " checked")
+ ">";
}
else
{ oRadio = document.createElement("INPUT");
oRadio.name="radioGroup";
oRadio.type="radio";
oRadio.id = "radio" + rowCount;
oRadio.value = oRadio.id; // always set radio id
if(rowCount==0)
{ oRadio.defaultChecked = true; // ??
oRadio.checked = true;
}
else
oRadio.checked = false;
oRadio.disabled=false;
}

oSel = document.createElement("SELECT");
oSel.name = "sel" + rowCount;
if(includeId)
oSel.id = "sel" + rowCount;
oOpt = document.createElement("OPTION");
oOpt.innerHTML = "option1";
oOpt.value="opt1";
oSel.appendChild(oOpt);
oOpt = document.createElement("OPTION");
oOpt.innerHTML = "option2";
oOpt.value = "opt2";
oSel.appendChild(oOpt);

tbody.appendChild(tr);
tr.appendChild(td);
if(useInnerHTML)
td.innerHTML = radioInnerHTML;
else
td.appendChild(oRadio);
td.appendChild(oText);
td.appendChild(oSel);
alert("td.innerHTML: " + td.innerHTML);

++rowCount;
}
function checkForm( form)
{
var oEls = form.elements;
var el;
var msg;
for( var i = 0; i < oEls.length; ++i)
{
el = oEls;
if(el.tagName == "OPTION")
continue; // ignore for now
if(el.className == "test")
continue; // part of test chrome
if(el.id && !form[el.id])
alert("element with id " + el.id + " not in FORM");
if(el.name && !form[el.name])
{ msg = "element with name " + el.name + " not in FORM";
if(el.name && !oEls[el.name])
msg+="\nit is not a named property of the elements array
either";
if(el.id && form[el.id])
msg+="\nit IS in the FORM under it's id of " + el.id;
alert(msg);
}
if(!(el.name || el.id))
alert( el.tagName + " [type=" + el.type + "] "
+ "value: " + el.value + "without name or id encountered");
}
}

function makeFieldUnionByName(oEls, name)
{
var result = null;
var resultArray = false;
for( var i = 0; i < oEls.length; ++i)
{
if( oEls.name!=name)
continue;
if( result && !resultArray)
{ result = [result];
resultArray = true;
}
if(resultArray)
result[result.length]=oEls;
else
result=oEls;
}
return result;
}
function fixFormBug( oForm) // Internet Explorer only
{
var oEls = oForm.elements;
var name;
for( var i = 0; i < oEls.length; ++i)
{
name = oEls.name;
if(name && !oForm[ name])
oForm[name] = makeFieldUnionByName(oEls,name);
}
}
</script>
</head>
<body>
<p>Reload the page if changing check box values.
</p>
<p>Clicking "check Form" should produce no alerts. Mozilla and Opera pass.
</p>
<p>Clicking "test Form Fix" before "check Form" suppresses alerts in IE
but doesn't fix radio group behavior.
</p>
<hr>
<form name="testForm" action="test.html">

<!-- form field used as test controls have class "test" -->

<input type="checkbox" name="includeId" class="test">
include id attribute on text/select INPUT elements<br>
<input type="checkbox" name="useInnerHTML" class="test">
use innerHTML for radio inputs<br>

<button type="button" onclick="addRow(this.form)" class="test">add
row</button>
<button type="button" onclick="checkForm(this.form)" class="test">check
Form</button>
<button type="button" onclick="fixFormBug(this.form)" class="test">test
form fix</button>
<input type="submit" value="submit" class="test">
<table>
<tbody id="tableBody">

<tbody>
</table>

</form>

</body>
</html>
 
K

kie

thanks Lasse,

an add on of 'px' makes all the difference! something simple that i
haven't been practicing, but will from now on. including the use of:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

at the top of all my HTML. do you and other developers you know use
'loose' or 'strict' as a rule of thumb?

with browsers allowing users such as myself to get away with emitting
cetain code, bad habits can be made. i wonder, does 'relaxed' coding
slow the browser down? or just make the page incompatible with less
tolerant browsers?

thanks for the tips,

kie
 
L

Lasse Reichstein Nielsen

at the top of all my HTML. do you and other developers you know use
'loose' or 'strict' as a rule of thumb?

I am not sure I would consider myself a "developer" with the (low)
number of pages I have made. But I do use "strict" whenever possible.
with browsers allowing users such as myself to get away with emitting
cetain code, bad habits can be made. i wonder, does 'relaxed' coding
slow the browser down? or just make the page incompatible with less
tolerant browsers?

I doubt it is slower. Most browsers are very tolerant of user mistakes
(they have to be with the amount of crap HTML out there), so that is
probably not a problem either (i.e., I don't know of a less tolerant
browser that doesn't understand HTML 4).

I always use the optional end tags, always quote attribute values, and
try to go strict by the standards, even where browsers don't require it.
That is just how I am. :) I follow standards very strictly because I
believe it to make code that is less error prone and more maintainable
in the long run.

/L
 
D

Dr John Stockton

JRS: In article <[email protected]>, seen in
news:comp.lang.javascript said:
Dr John Stockton said:
JRS: In article <[email protected]>, seen in
Dom Leonard <[email protected]
m.au> posted at Sun, 14 Sep 2003 04:01:55 :-
[The unary plus operator applied to a string is equivalent to
parseFloat(string,10)]

It is not equivalent. Consider the string "3k".

Yes, unary plus is equivalent to the function "Number" (not used as a
constructor). The "Number" conversion function works exactly like the
automatic type conversions induced by operators, which is what unary
plus is (just like Boolean is equivalent to double-negation and the
implicit conversion performed by conditionals).

I can't really find a reason to use parseInt or parseFloat.

Use parseInt for bases other than 10 (not needed for 0x<hex>).
Use parseInt to truncate a decimal part.

Use either if the string may contain non-whitespace to be ignored after
the numeric part.

Unary +, being stricter in what it accepts, is safer.
Math.floor(+value) is only a little longer than parseInt(value).
 
L

Lasse Reichstein Nielsen

Dr John Stockton said:
JRS: In article <[email protected]>, seen in


Use parseInt for bases other than 10 (not needed for 0x<hex>).

Not neede for 0 said:
Use parseInt to truncate a decimal part.

Math.floor is build to do just that.
Use either if the string may contain non-whitespace to be ignored after
the numeric part.

If you were actually making a parser, that could be useful, but *only*
if you knew how many characters were used. However, parseInt and
parseFloat does not tell you that, and leaves no way to find out. So,
if you are parsing, you will have to find the length of the numeric
part of the string anyway ... and then you don't need parseInt/Float.

That was what I was thinking of, when I discounted parseInt/Float. I
can see that parseInt has a use in converting from different
non-standard bases.

/L
 
D

Dr John Stockton

JRS: In article <[email protected]>, seen in
news:comp.lang.javascript said:
Not neede for 0<octal> either, but that is a good point.



As a code literal, 033 & +033 are twenty-seven; but +'033' is thirty-
three (and 0x33, +0x33, & +'0x33' are all fifty-one).

For input by non-programmers, it is more likely that 033 means thirty-
three than twenty-seven; +<string> gets it right.

Number('033') is thirty-three; Number('0x33') is fifty-one.

AFAICS, therefore there is no way for a string to say that, interpreted
numerically, it is Octal; but it can claim to be Hex with 0x.




It's a pity that +078 - +077 gives fifteen, when written in code.

It's a pity that a digit string that looks, to a layman, like a decimal
number can ever be interpreted otherwise.

But it does seem that, from outside the coding, leading zeroes do not
octalize.

Or is it different in other browsers?
 
K

kie

i see,

i always use id when making input elements in plain HTML so that they
can be picked up by getElementById() among other things. Leaving it
out meant that lines such as:

"oForm.elements['text_box'].value" inside the calculation function
failed.

so when using the above line, it must always be calling on the id of
an element for the value, and not the name?

it's a great test script Dom, i need to look at it further to get my
head around the radio button problem. i had't tried testing radio
buttons.

thanks!, kie
 
D

Dom Leonard

kie said:
i always use id when making input elements in plain HTML so that they
can be picked up by getElementById() among other things. Leaving it
out meant that lines such as:

"oForm.elements['text_box'].value" inside the calculation function
failed.

so when using the above line, it must always be calling on the id of
an element for the value, and not the name?
It depends on context. In older browsers, named lookup on the elements
collection returned a field element by name - id values were not even
implemented. In current browsers it should preferentially return an
element with id matching the string supplied for lookup, or if no id is
matched, a field element with name value matching the supplied string.

Some testing suggests, however, that form submission is performed using
element *name* values - they can't be left out simply because id has
been supplied for element access. This is why the work around suggested
for IE behavior is to supply id attributes with the same value as the
name attribute on field elements added programatically.

cheers,
Dom
 
K

kie

following on from the subject of assigning events to dynamically
created elements, i would like to ask some advice on how to assertain
when there is only one row in the table.

using a button marked 'remove row' a user can remove dynamic rows in a
table until there are none left

(see http://www.kieran.f2s.com/removeChild/working.htm)

i would like to inform the user that they are attempting to remove the
last row, and disallow the attempt.

i want to create a universal function, triggered from the onclick
event of the 'remove row' button.

so far my best attempt is to pass the element name of the button to
the function and search in all elements:

function removeRow(s_element_name) // method of form element within
row
{
//alert();
var oForm = document.forms[0];
var n = 0;

//if row is last row, it cannot be deleted

for (i=0; i<oForm.elements.length; i++){
//alert(oForm.elements.name);
if(oForm.elements.name.indexOf(s_element_name) == 0){
n += 1;
//alert(oForm.elements.name);
}
}
//alert(n);
if(n = 1){
for( var node = this.parentNode; node; node=node.parentNode)
if(node.tagName=="TR")
break;
node.parentNode.removeChild(node);
calculateTab(2,0);
}else{
alert('Cannot remove last remaining row');
}
}

(see http://www.kieran.f2s.com/removeChild/attempt.htm for a failing
example of this)

if you have any ideas on a better way to do this, please add it to
this thread,

kind regards, kie
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top