Clone a row but remove its values


#1

I'm trying to create a purchase order website. The portion of the form where the list of items is listed is giving me some problems. Basically, I'm trying to clone a row without its values. I've read several articles online about this issue, but I'm still having a hard time figuring out how to get this to work in my particular scenario. Any suggestions would be much appreciated. Also, any recommended reading would be awesome. :slight_smile: Also, I'm struggling with how to display a line# for each line - my global is clearly not working. :-/

<form action="index.php" method="POST">
<fieldset>
            <legend>Items:</legend>
                        <?php global $number;
//                        $number=1
                        ?>
                <div class="table" id="tbl">
                    <div class="tr">
                        <div class="td bold">Line#</div>
                        <div class="td bold">Contract:</div>
                        <div class="td bold">Description:</div>
                        <div class="td bold">Qty:</div>
                        <div class="td bold">Each:</div>
                        <div class="td bold">Price total:</div>
                    </div>
                    <div class="tr">
                        <div class="td"><?php echo $number; $number++; ?></div>
<!--                        <div class="td"><input type="text" name="" /></div>-->
                        <div class="td"><input class="td contract" type="text" name="contract[]" /></div>
                        <div class="td"><input class="td descr" type="text" name="itemDescription[]" /></div>
                        <div class="td"><input type="number" name="itemQuantity[]" keyup()
                                               id="qty" step='0.01' value='0.00' placeholder='0.00' class="td qty" /></div>
                        <div class="td"><input type="number" name="itemCostEach[]"
                                               id="price" step='0.01' value='0.00' placeholder='0.00' class="td price each" /></div>
                        <div class="td"><input type="number" name="itemTotal[]"
                                               id="total" step='any' value='0.00' placeholder='0.00' class="td price total" /></div>
                        <div class="td"><input type="button" class="button"
                                               value="Add item" onclick="addRow(this);" /></div>
                    </div>
<!--                    <div class="tr">-->
<!--                        <div class="td"></div>-->
<!--                        <div class="td"></div>-->
<!--                        <div class="td"></div>-->
<!--                        <div class="td"></div>-->
<!--                        <div class="td">Grand total:</div>-->
<!--                        <div class="td"><input type="number" name="grandTotal[]"-->
<!--                                               id="grandTotal" step='any' value='0.00' placeholder='0.00' class="td price total" /></div>-->
<!--                    </div>-->
                </div>
                <script>
                    $('input').keyup(function(){ // run anytime the value changes
                        var firstValue  = Number($('#qty').val());   // get value of field
                        var secondValue = Number($('#price').val()); // convert it to a float


//                        $('#total').html(firstValue + secondValue + thirdValue + fourthValue); // add them and output it
                        document.getElementById('total').value = firstValue * secondValue;
// add them and output it
                    });
                </script>

            </fieldset>
</form>

   <script type="text/javascript">

        function addRow(n)
        {

            var tr = n.parentNode.parentNode.cloneNode(true);
            for(var i = 0; i < tr.length; i++)
            {
                tr[i].innerHTML = "";
            }
            document.getElementById('tbl').appendChild(tr);

        }
    </script>

#2

Hi @jcarlisle2 I'm not entirely sure this will fix your issue but I think it may get you a little further. I think you intend to be looping through the container child nodes instead like so:

var tr = n.parentNode.parentNode.cloneNode(true);
for (var i = 0; i < tr.childNodes.length; i++) {
	tr.childNodes[i].innerHTML = "";
}

#3

@Andres_Vaquero howdy sir! Thank you for your prompt reply - I'm actually working on this project part time as I'm still a full-time student, but I'll try to respond/incorporate your suggestion(s) as time allows... I was able to add the childNode code as you suggested, but now I'm getting "empty rows?" being inserted. In other words, it looks as though <br /> or <p>'s are being rendered on the page...


#4

I would be tempted not to clone any existing row, but to instead use createElement and setAttribute lines to insert new "no values" rows into the DOM


#5

@Mittineague Cool! I'll give it a try.


#6

I would have wrapped the cloned element(s) in a form, called the reset() method and copied it from there into the document.


#7

@Dormilich thank you for your recommendation. However, to my knowledge, it is not possible to put a form within a form...


#8

I didn't mean the reset form to be in the document. When you clone an HTML element, it exists outside your document, in empty space so-to-speak, and that's where I'd use the reset form..


#9

@Dormilich I see. I didn't even realize that https://www.w3schools.com/jsref/met_form_reset.asp existed. I'll see about incorporating it - thanks much! I just wonder... when I reset, how will I keep from losing the values of the line that is being cloned... Will have to study this. :+1:


#10

you temporarily wrap the cloned line into the form before you insert it into the document.


#11

@Mittineague Okay, so I've read through the Document.createElement article you included as well as the w3schools article on creating an element (https://www.w3schools.com/jsref/met_document_createelement.asp) but I'm still really unclear about how to apply that here. Do I need to define a row in my js that is then inserted into the document? I've played around with the "Try it Yourself" editor but am clearly not getting something. Also, I don't know if I explained my ultimate goal very well. I'd like to be able to enter values in a row, add a new row (without the values, but still retaining the "logic" of calculating total cost (each item cost * number of items). I'd like to be able to have a running list of line numbers, as well as a total for everything. I've also spent the last several days reading through w3schools info on jquery and was originally wondering if I should us jquery. Anyway, sorry for the long post. I have yet to fully understand @Dormilich's suggestion, but I will continue researching that as well. Eventually, I will also have to figure out how to submit the values to a MS SQL database, but that bridge is a bit far off as I see it. Many thanks for your patience and suggestions. :+1:


#12

I think databases beginnings can be a bit of bump in the learning curve for many.

As for the JavaScipt, a browser first starts getting the HTML (the DOM) and then starts getting external script.

It can be debated about its quality and I have preference for the habit largely because it's a habit.

I first start with putting together a pre-pre-pre-alpha file. My naming has improved. maybe ... eg.

function formRowCreator(args) {
  // process arguments if any else use defaults 
  // eldest element 
  var row_element = document.createElement("tr"); 
  // setAttributes if any 
  // add attributes to tr if exist
  // create child row cells 
   var cell_element = documentCreateElement("td"); 
  // setAttributes if any 
  // add attributes to td if exist 
  // assign textContent to cells if any 
  // rinse repeat as needed 
  // add cells to row 
  // return new object else false 
}

it might be bit or work setting up, but by using arguments, if you work with HTML tables often it is reusable and can be a time saver.


#13

@Mittineague Thank you for your reply.. I'm not sure I understand "It can be debated about its quality" referring to the JavaScript? Thank for the pre-pre-pre-alpha file template. I'll see what I can come up with. :slight_smile:


#14

@Mittineague does it look like I might be on the right path?

    <script type="text/javascript">
        function formRowCreator() {
            // process arguments if any else use defaults
//            In this case I don't think I'm wanting to process any arguments
//            var tr = rowToProcess.parentNode.parentNode.cloneNode(true);
            // eldest element
            var rowElement1 = document.createElement("tr");
            var rowElement2 = document.createElement("td");

            // createAttributes if any
            var rowElement2att1 = document.createAttribute("type");
            rowElement2.value = "text";
            var rowElement2att2 = document.createAttribute("name");
            rowElement2.value = "contract[]";

            // add attributes to tr if exist
            // create child row cells
            var cell_element = documentCreateElement("td");
            // createAttributes if any
            // add attributes to td if exist
            // assign textContent to cells if any
            // rinse repeat as needed
            // add cells to row
            // return new object else false
        }
    </script>

#15

Very close to a good start I think.

I'm lazy and shortcut by using

.... If the attribute already exists, the value is updated; otherwise a new attribute is added with the specified name and value.

But if helps to read the code by having two lines instead of two arguments together that's the way you should do it for now.

Personally, I have used the generic "number this number that" way of naming before. It was no trouble while the code was short and fresh, but as the code grew and/or I was away from it for a while, not so much. I have since got in the habit of using longer more descriptive names as best as I can. You can create names to a great enough length that there is no need to worry about having longer names if they make the code more readable.


#16

@Mittineague Well! I'm making progress!! (or so it seems). Here is my javascript code currently (HTML has remained largely unchanged):

    <script type="text/javascript">
            var lineNumber = 1;
        function formRowCreator() {
            // process arguments if any else use defaults
//            In this case I'm wanting to process the tr argument
//            var tr = rowToProcess.parentNode.parentNode.cloneNode(true);
//alert("in formRowCreator");
            // eldest element
            var lineBreak = document.createElement("br");
            var rowElement1 = document.createElement("tr");
//            var rowElement1 = document.createElement("button");

            // add attributes to tr if exist
//

            // create child row cells
            var cellZero = document.createElement("td");
            var inputZero = document.createTextNode(lineNumber);
            cellZero.appendChild(inputZero);


            var cellOne = document.createElement("td");
            var inputOne = document.createElement("input");
            inputOne.setAttribute("type", "text");
            inputOne.setAttribute("name", "contract[]");

            var cellTwo = document.createElement("td");
            var inputTwo = document.createElement("input");
            inputTwo.setAttribute("type", "text");
            inputTwo.setAttribute("name", "itemDescription[]");
//
//
            var cellThree = document.createElement("td");
            var inputThree = document.createElement("input");
            inputThree.setAttribute("type", "number");
            inputThree.setAttribute("name", "itemCostEach[]");
            inputThree.setAttribute("id", "qty");
            inputThree.setAttribute("step", "0.01");
            inputThree.setAttribute("value", "0.00");
            inputThree.setAttribute("placeholder", "0.00");
//
//
            var cellFour = document.createElement("td");
            var inputFour = document.createElement("input");
            inputFour.setAttribute("type", "number");
            inputFour.setAttribute("name", "itemCostEach[]");
            inputFour.setAttribute("id", "price");
            inputFour.setAttribute("step", "0.01");
            inputFour.setAttribute("value", "0.00");
            inputFour.setAttribute("placeholder", "0.00");
//
            var cellFive = document.createElement("td");
            var inputFive = document.createElement("input");
            inputFour.setAttribute("type", "number");
            inputFour.setAttribute("name", "itemTotal[]");
            inputFour.setAttribute("id", "total");
            inputFour.setAttribute("step", "any");
            inputFour.setAttribute("value", "0.00");
            inputFour.setAttribute("placeholder", "0.00");
//
//            var rowElement2att2 = document.createAttribute("name");
//            var cell_element = documentCreateElement("td");
            // createAttributes if any
            // add attributes to td if exist
            // assign textContent to cells if any
            // rinse repeat as needed
            // add cells to row
            rowElement1.innerHTML+= cellZero.outerHTML+
                cellOne.outerHTML+inputOne.outerHTML+
                cellTwo.outerHTML+inputTwo.outerHTML+
                cellThree.outerHTML+inputThree.outerHTML+
                cellFour.outerHTML+inputFour.outerHTML+
                cellFive.outerHTML+inputFive.outerHTML;
            document.getElementById("tableRow").appendChild(rowElement1);

            lineNumber++;
            // return new object else false
        }
    </script>

However, I'm rather mystified as to how I can go about adding the calculation functionality to inputFive (it's supposed to be the result of multiplying the value in inputThree by the value in inputFour). Do I need to add a "keyup" function within my formRowCreator function that will do the calcuation?

Here is the original script I was using to do the calculation when I simply trying to clone the row.

 <script>
                    $('input').keyup(function(){ // run anytime the value changes
                        var firstValue  = Number($('#qty').val());   // get value of field
                        var secondValue = Number($('#price').val()); // convert it to a float


                        document.getElementById('total').value = firstValue * secondValue;
// add them and output it
                    });
                </script>

Edit: Just realized I may need to invest some time learning Angular JS to accomplish this: https://stackoverflow.com/questions/31470273/perform-calculations-on-dynamically-added-rows

Edit: I'm going through this article a bit more carefully... Looks like I don't need AngularJS after all.


#17

It doesn't look like you are using AngularJS. Anything you can do with AngularJS you can do with JavaScript. I wouldn't recommend adding a framework to the mix unless you are going to devote the entire the project to using that project. Having said that AngularJS is obsolete. AngularJS (Angular 1) has been replaced by Angular 5. Learning AngularJS would not be very valuable unless you are working on a older code base that uses that framework.


#18

@ZooKeeper Thank you for your prompt input. I just started researching AngularJS (read the wikipedia article: https://en.wikipedia.org/wiki/AngularJS and started the w3schools tutorial) however I realized that if I chose to incorporate AngularJS I would have to start the website over, pretty much from scratch. :frowning2:

I'm still at a loss of how to overcome the challenge I shared with Mittineague in my last post (i.e. how to perform calculations...).

Also, would it make sense for me to start a new thread with the current challenge I'm dealing with?

Edit: Okay, you're absolutely right. It looks like I should be able accomplish the task with JavaScript per the same article I referenced earlier (https://stackoverflow.com/questions/31470273/perform-calculations-on-dynamically-added-rows )


#19

All those innerHTML lines bother me.

What I often do is "append" or less often "insert"


I always work inner to outer. eg.
tr append td [n ..]
table append tr [n ..]
container append table


#20

@Mittineague Thank you so much for all your suggestions and explanations about how to create my own JS function to utilize the createElements and setAttributes. Unfortunately I chickened out and went with a javascript snippet I found online... However the deleteRow function of the code I found is not working and I'm not sure why. At first I though it had to do with "var chkbox = row.cells[0].childNodes[0];" (thinking that might be a checkbox element that is missing from the html), but now I'm not sure what it could be. :thinking:

Here is the code... Any insight as to why the delete row is not working would be much appreciated.

<table id="dataTable" class="form" border="1">
                    <tbody>
                    <tr id='row_0'>
                        <p>
                        <td>
<!--                            <label>Quantity</label>-->
                            <input type="text" required="required" name="qty" oninput="calculate('row_0')">
                        </td>
                        <td>
<!--                            <label for="price">Price</label>-->
                            <input type="text" required="required" class="small" name="price" oninput="calculate('row_0')">
                        </td>
                        <td>
<!--                            <label for="total">Total</label>-->
                            <input type="text" required="required" class="small" name="total">
                        </td>
                        </p>
                    </tr>
                    </tbody>
                </table>

    <script type="text/javascript">
        function addRow(tableID) {
//            alert("in addRow");
            var table = document.getElementById(tableID);
            var rowCount = table.rows.length;
            if (rowCount < 4) { // limit the user from creating fields more than your limits
                var row = table.insertRow(rowCount);

                var colCount = table.rows[0].cells.length;
                row.id = 'row_' + rowCount;
                for (var i = 0; i < colCount; i++) {
                    var newcell = row.insertCell(i);
                    newcell.outerHTML = table.rows[0].cells[i].outerHTML;
                }
                var listitems = row.getElementsByTagName("input");
                for (i = 0; i < listitems.length; i++) {
                    listitems[i].setAttribute("oninput", "calculate('" + row.id + "')");
                }
            } else {
                alert("Maximum Passenger per ticket is 4.");

            }
        }

        function deleteRow(tableID) {
            var table = document.getElementById(tableID);
            var rowCount = table.rows.length;
            for (var i = 0; i < rowCount; i++) {
                var row = table.rows[i];
                var chkbox = row.cells[0].childNodes[0];
                if (null !== chkbox && true === chkbox.checked) {
                    if (rowCount <= 1) { // limit the user from removing all the fields
                        alert("Cannot Remove all the Passenger.");
                        break;
                    }
                    table.deleteRow(i);
                    rowCount--;
                    i--;
                }
            }
        }

        function calculate(elementID) {
            var mainRow = document.getElementById(elementID);
            var myBox1 = mainRow.querySelectorAll('[name=qty]')[0].value;
            var myBox2 = mainRow.querySelectorAll('[name=price]')[0].value;
            var total = mainRow.querySelectorAll('[name=total]')[0];
            var myResult1 = myBox1 * myBox2;
            total.value = myResult1;

        }
    </script>

Also, here is the link to the original article https://stackoverflow.com/questions/31470273/perform-calculations-on-dynamically-added-rows and here is the link to the jsfiddle https://jsfiddle.net/xvahg1dk/1/ where the (partially-working) solution is demonstrated.