I did exactly that with javascript. What you have to keep track of is what element in the form you are at. I did that with the tabindex. Make the table as repetitive as possible so that you can simplify the formulas to work for any row.
I have three functions, updateLineTotal, updateSubTotal, updateGrandTotal. I fire all three for Qty and Unit Price so I can keep track of what function is doing what. I guess I could put them all into one big function...
updateLineTotal is passed the tabindex of the box to be changed as that relates to the position of that box in the form.elements array. I keep tabindex as a variable and write it dynamically via vbscript so that I have access to it when calling the javascript. So, since Qty is two elements before the line item textbox (which is readonly):
Response.Write "<tr><td><input type=text onChange=updateLineTotal("&tabi+2&"

;updateSubttl();updateGrand(); name=qty"&c2&" tabindex="&tabi&" value="&objRS("Qty"

&"></td>"
tabi=tabi+1
c2 is what row I am in and is used to store the values in the database row by row.
function updateLineTotal(target){
var newtarget,tmp,tmp2;
newtarget=parseInt(target,10);
newtarget=(3*newtarget-14)/2;
tmp=parseFloat(pickform.elements[newtarget-1].value,10);
tmp2=parseFloat(pickform.elements[newtarget-2].value,10);
if(tmp<=0 || tmp2<=0){
alert("Only positive values allowed."

;
}
else{
if(isNaN(tmp) || isNaN(tmp2)){
alert("Please enter a numeric value."

;
}
else{
pickform.elements[newtarget].value=(tmp*tmp2);
}
}
}
The formula for newtarget translates the tabindex to the element in the form. You will have to modify it to fit your form, possibly. That is why it is important to have a pattern. newtarget-1 is the Unit Price and newtarget-2 is the Qty.
function updateSubttl(){
var newsub;
newsub=0;
for(i=17;i<pickform.length;i+=3){
if(pickform.elements
.name.substring(0,6)=="linttl"
{
newsub+=parseFloat(pickform.elements.value,10);
}
}
pickform.subttl.value=numsub;
}
I start at the known first line item total on the form and work my way through them until I reach the end. If the name starts with "linttl" then I keep a running total of all the line item totals to get the new subtotal.
function updateGrand(target){
var newttl=parseFloat(pickform.subttl.value,10)
//i had shipping, etc. here which gets added to newttl
pickform.total.value=newttl;
}
To see what element each box is use this:
for(i=0;i<pickform.length;i++){
pickform.elements.value=i;
}
That will help initially to see where you are in the form.