I'm populating a chained linked dropdown box with the following code:

$(document).ready(function() {

    var data = <?php echo $arr;?>;
    var id;
    function getData(passCatID){
         var content = '';
        var content = '<option name="specify id="specify" style="background: url() right no-repeat; width: 20px">------SPECIFY-----</option>';
        var product_desc = '';
        var invt = '';
        var qty = '';
        var cost = '';
        id = passCatID;
        $.each(data[id], function(key,value) {          
            if(invt == '')invt = value['invt'];
            if(product_desc == '')product_desc = value['product_desc'];
            if(cost == '')cost = value['cost'];
             content += '<option value="' + key + '">' + value['subcat_label'] + '</option>';

        });
        $('#service_subcat').html(content);
        $('#descbox').html( product_desc );
        $('#invtbox').html(invt);
        $('#qtybox').html( qty);
        $('#costbox').html(cost);
    }
    function getDesc(passItemID){
        var itemID = passItemID;
        var invt = data[id][itemID]['invt'];
        $('#invtbox').html(invt);  

         var product_desc = data[id][itemID]['product_desc'];
        $('#descbox').html( product_desc ); 

        var qty = data[id][itemID]['qty'];
        $('#qtybox').html( qty );   

        var cost = data[id][itemID]['cost'];
        $('#costbox').html( cost );      
    }
    $('#categories').change(function(){
       id = $('#categories').val();
       getData(id);
    });
    $('#service_subcat').change(function(){
       var itemID = $('#service_subcat').val();
       getDesc(itemID);
    }); 


});

Subsquently, I have an option to add additional dropdown boxes; once the boxes are added, the mechanism to populate the chained link effect is attempted with following code:

function populateBox()
{
    var data_1 = <?php echo $arr1;?>;
    var id;
    function getData_1(passCatID){
          var content = '';
        var content = '<option name="specify id="specify" style="background: url() right no-repeat; width: 20px">------SPECIFY-----</option>';
          var product_desc_ = '';
        var invt_ = '';
        var qty_ = '';
        var cost_ = '';
        id= passCatID;
        $.each(data_1[id], function(key,value) {          
            if(invt_ == '')invt_ = value['invt'];
            if(product_desc_ == '')product_desc_ = value['product_desc'];
            if(cost_ == '')cost_ = value['cost'];
             content += '<option value="' + key + '">' + value['subcat_label_1'] + '</option>';

        });
        $('#service_subcat_1').html(content);
        $('#descbox_').html( product_desc_ );
        $('#invtbox_').html(invt_);
        $('#qtybox_').html( qty_);
        $('#costbox_').html(cost_);
    }
    function getDesc_1(passItemID){
        var itemID = passItemID;
        var invt_ = data_1[id][itemID]['invt_'];
        $('#invtbox_').html(invt_);  

         var product_desc_ = data_1[id][itemID]['product_desc_'];
        $('#descbox_').html( product_desc_ ); 

        var qty_ = data_1[id][itemID]['qty_'];
        $('#qtybox_').html( qty_ );   

        var cost_ = data_1[id][itemID]['cost_'];
        $('#costbox_').html( cost_ );      
    }
}
  $(document).ready(function(){
  populateBox();
});
      $('#categories_1').change(function(){
       id = $('#categories_1').val();
       getData_1(id);
         //populateBox();

    });
    $('#service_subcat_1').change(function(){
       var itemID = $('#service_subcat_1').val();
       getDesc_1(itemID);
         populateBox();

    });

The latter does not work! Any additional dropdown box is not populated --which is dependent on the function populateBox();

Is this a correct way of achieve the intended outcome?

Any thoughts!

Any help on this question?

First, let's check my understanding.

You have a pair of select elements with onchange event handlers to give them a cascading behaviour and to populate a set of associated HTML elements with data.

What you want is to be able to add one or more further pairs of select elements dynamically and to give them near identical behaviour. The differences in behaviour are that each pair of select elements :

  • has its own data (a javascript array)
  • has its own set of data containers.

If I am right, then I need to know the following :

  • the HTML structure that accommodates each pair of select menus and associated data containers
  • the process by which new select menus etc are added.

Airshow

Thanks for the post Airshow! Indeed, your understanding is correct.

The html structure for each pair of select menu is a combination of jquery and data from mysql. The data from db is in two tables: category table which populates the first select menu and** subcategory** which populates the second menu and three other input boxes.

A functional link below:

Click Here

To get the data from db and create an array of the data, I'm doing the following:

$r = mysql_query("SELECT sc.cat_id, sc.category_label, ss.cat_id, ss.item_id, ss.subcat_label, ss.invt, ss.product_desc, ss.invt, ss.qty, ss.cost FROM service_cat AS sc INNER JOIN service_subcat AS ss ON sc.cat_id = ss.cat_id ORDER BY sc.category_label, ss.subcat_label");
      if(mysql_num_rows($r)){
       // $dd1 = "\n<select id=\"categories\" style=\"color:#003399; text-align:justify; font-size:1.0em; border-left: none; border-right: none; border-bottom: none\">\n\t<option value=\"0\">--Select One--</option>";
        $dd1 = "<select id=\"categories\" style=\"color:#003399; text-align:justify; font-size:1.0em; border-left: none; border-right: none; border-bottom: none\"><option value=\"0\">--Select One--</option>";
        $catname = '';  
        while($d = mysql_fetch_array($r)){
            if($catname != $d['category_label'])$dd1.= "<option value=\"{$d['cat_id']}\">{$d['category_label']}</option>"; 
           // if($catname != $d['category_label'])$dd1.= "\n\t<option value=\"{$d['cat_id']}\">{$d['category_label']}</option>"; 
            $array[$d['cat_id']][$d['item_id']] = array('subcat_label'=>$d['subcat_label'], 'invt' =>$d['invt'], 'product_desc' =>$d['product_desc'], 'qty'=>$d['qty'], 'cost'=>$d['cost']);
            $catname = $d['category_label'];
        }
        $dd1 .= "</select>";
      //          $dd1 .= "\n</select>\n";

    }

 $arr = json_encode($array);

and with jquery, I'm doing the following:

$(document).ready(function() {

    var data = <?php echo $arr;?>;
    var id;
    function getData(passCatID){
         var content = '';
        var content = '<option name="subcat_label" id="subcat_label" style="background: url() right no-repeat; width: 20px">------SPECIFY-----</option>';
        var product_desc = '';
        var invt = '';
        var qty = '';
        var cost = '';
        id = passCatID;
        $.each(data[id], function(key,value) {          
            if(invt == '')invt = value['invt'];
            if(product_desc == '')product_desc = value['product_desc'];
            if(cost == '')cost = value['cost'];
             content += '<option value="' + key + '">' + value['subcat_label'] + '</option>';

        });
        $('#service_subcat').html(content);
        $('#descbox').html( product_desc );
        $('#invtbox').html(invt);
        $('#qtybox').html( qty);
        $('#costbox').html(cost);
    }
    function getDesc(passItemID){
        var itemID = passItemID;
        var invt = data[id][itemID]['invt'];
        $('#invtbox').html(invt);  

         var product_desc = data[id][itemID]['product_desc'];
        $('#descbox').html( product_desc ); 

        var qty = data[id][itemID]['qty'];
        $('#qtybox').html( qty );   

        var cost = data[id][itemID]['cost'];
        $('#costbox').html( cost );      
    }
    $('#categories').change(function(){
       id = $('#categories').val();
       getData(id);
    });
    $('#service_subcat').change(function(){
       var itemID = $('#service_subcat').val();
       getDesc(itemID);
    }); 


});

The preceding process populates in an html table row:

tr class="item-row">
            <td valign='top' align=center style='border-left: 0px solid #cccccc;' width='100%' class="item-name"><div class="delete-wpr">
            <?php echo $dd1;?>   <!-- main service list-->        
            <a class="delete" href="javascript:;" title="Remove row" class='NonPrintable'>X</a></div></td><!-- end of item selection-->
            <td valign='top' align=center ><select id="service_subcat" style='color:#003399; text-align:center; font-size:1.0em; border-left: none; border-right: none; border-bottom: none'><option>Select Category</option></select></td> <!-- secondary service list--> 
            <td valign='top' align=center><div id="invtbox">---</div></td>
            <td><textarea id='descbox' name='mytextarea' rows='8' cols='40' class='expand80-1000' style='color:#003399; text-align:left; font-size:1.0em; border-left: none; border-right: none; border-bottom: none'>Make A Service or Product Selection</textarea></td>
            <td valign='top' align=center><input  type='text' name='qtys'  class="qty" size='3' maxlength='3' value='0' class='combo3' rel='code_id' title='' style='color:#003399; text-align:center; font-size:1.1em; border-left: none; border-right: none; border-bottom: none'></td>

            <td valign=top><textarea id='costbox' name='costbox' class='costbox'rows='1' cols='5' style='color:#003399; text-align:center; font-size:1.0em; border-left: none; border-right: none; border-bottom: none' onKeyPress="return(currencyFormat(this,',','.',event))">$0.00</textarea></td>
            <!--td valign='top'  width='17%'><input  type='text' id='costbox' name='cost'  class="cost" size='6' maxlength='6' class='combo3' rel='code_id' title='' style='color:#003399; text-align:left; font-size:1.1em; border-left: none; border-right: none; border-bottom: none'></td-->
            <input type="hidden" name="dimension2_id" value="0">
            <td valign='top' align=center style='border-left: 0px solid #cccccc;' width='14%' align='left'> <span class="price" style='color:#003399; text-align:left; font-size:1.0em; border-left: none; border-right: none; border-bottom: none, border-top: 2px gray'>$0.00</span></td></td>
            <!--td valign='top' style='border-left: 0px solid #cccccc;' width='10%' align='left'> <input id='amount' name='discount' size='8' style='color: #71C671' value='0.00' onChange='updatesum()'></td-->

    </tr>

Now, this process works fine with my initial selection menu. To add new select menu, I have a combination of js and htm; first, the js is as follows:

var counter = 0;
//Start a counter. Yes, at 0
function addfield() {

    if (counter < 10)
    {
    counter++;
//I find it easier to start the incrementing of the counter here.
    var newFields = document.getElementById('addfield').cloneNode(true);
    newFields.id = '';
    newFields.style.visibility = 'visible';
    var newField = newFields.childNodes;
    for (var i=0;i<newField.length;i++) {
        var theName = newField[i].name
        if (theName)
                newField[i].name = theName + counter;
//This will change the 'name' field by adding an auto incrementing number at the end. This is important.
        }
        var insertHere = document.getElementById('addfield');
//Inside the getElementById brackets is the name of the div class you will use.
        insertHere.parentNode.insertBefore(newFields,insertHere);
}
}

function addfield() adds a table cell with the following content:

<tr id=addfield  class="item-row" style="visibility:hidden">
            <td valign="top" align=center style="border-left: 0px solid #cccccc;" width="100%" class="item-name">

            <div class="delete-wpr"><?php echo $dd2;?>
            <a class="delete" href="javascript:;" title="Remove row">D</a>
            </div></td>
            <td valign="top" align=center >
            <select id="service_subcat_1" style="color:#003399; text-align:justify; font-size:1.0em; border-left: none; border-right: none; border-bottom: none">
            <option>Select Category</option>
            </select></td>
            <td valign="top" align=center>
            <div id="invtbox_" name="invtbox_">---</div></td>
            <td><textarea id="descbox_" name="descbox_" rows="8" cols="40" class="expand80-1000" style="color:#003399; text-align:justify; font-size:1.0em; border-left: none; border-right: none; border-bottom: none">Make A Service or Product Selection</textarea></td>
            <td valign="top" align=center><input  type="text" id="qtybox_" name="qtybox_"  class="qty" size="3" maxlength="3" value="0" class="combo3" rel="code_id" title="" style="color:#003399; text-align:center; font-size:1.1em; border-left: none; border-right: none; border-bottom: none"></td>
            <td valign=top><textarea id="costbox_" name="costbox_" class="costbox"rows="1" cols="5" style="color:#003399; text-align:justify; font-size:1.0em; border-left: none; border-right: none; border-bottom: none"></textarea></td>
            <input type="hidden" name="dimension2_id" value="0">
            <td valign="top" align=center style="border-left: 0px solid #cccccc;" width="14%" align="left"> <span class="price" id="pricebox_" name="pricebox_"  style="color:#003399; text-align:left; font-size:1.0em; border-left: none; border-right: none; border-bottom: none, border-top: 2px gray">$0</span></td>

            </td>

            </tr>

The <tr> content within the addfield() is structured in the same way as the initial select menu selection. (test the link Click Here

Sorry, for my lenghty post, but I hope I'm addressing your request.

Thanks again!

Mossa

Mossa,

The keys here are to get the HTML right and to exploit certain features of jQuery :

  • Classes: jQuery makes it very simple to work with classes such that it's often not necessary to generate unique ids in cloned copies of a template. This is the case here and all elements that are repeated in each row now have classes rather than ids.

  • Form element arrays: HTML attributes of the format name="myElementName[]" cause form elements so named to be submitted as an array of values. Here, you can use this technique for all the fields that are repeated in each item-row - descbox, qtybox and costbox. appearing in PHP as $_POST['myElementName'];

  • Tbody elements and Delegation: Tbody elements allow table rows to be grouped into active "item_rows" and the inactive & hidden "row_template". The "item_rows" tbody gives us an element to which event handling can be delegated while the "row_template" tbody allows the template row itself to be cloned and appended to "item_rows" without needing to remove its id or to explicitly remove its invisibility.

javascript:

$(function(){
    var data = <?php echo $arr; ?>;
    var $row_template = $('#row_template tr'),
        $item_rows_container = $("#item_rows");

    $("#add_row").on('click', function(){
        if ($item_rows_container.find("tr").length < 10){
            $row_template.clone().appendTo($item_rows_container);
        }
        return false;
    });

    $item_rows_container.on('click', '.delete_row', function(){
        if(confirm("Delete row?")){
            $(this).closest("tr").remove();
        }
    }).on('change', '.categories', function(){//this anonymous function was addfield()
        var $this = $(this);
        var d = data[$this.val()] || {};
        var $row = $this.closest("tr");
        var $subcat_menu = $('.service_subcat', $row).empty().append('<option name="specify">------SPECIFY-----</option>');
        $.each(d, function(key, value) {
            $subcat_menu.append('<option value="' + key + '">' + value['subcat_label'] + '</option>');
        });
        $subcat_menu.trigger('change');
    }).on('change', '.service_subcat', function(){
        var $this = $(this);
        var $row = $this.closest("tr");
        var category = $row.find(".categories").val();
        var d = (category && data[category] && data[category][$this.val()]) ? data[category][$this.val()] : {};
        $('.invtbox', $row).html( d['invt'] || '' );
        $('.descbox', $row).html( d['product_desc'] || '' );
        $('.qtybox', $row).html( d['qty'] || '' );
        $('.costbox', $row).html( d['cost'] || '' );
    });
});

partially tested

HTML (simplified) :

<table>
<thead>
    <!-- table header here -->
</thead>

<tbody id="item_rows">
    <!-- serve page with initial item_row here -->
</tbody>

<tbody id="row_template" style="display:none">
<tr class="item-row">
<td class="item-name" valign="top" align="center" width="100%">
<div class="delete-wpr">
    <select class="categories">
        <option value="0">--Select One--</option>
        <option value="2">BRAKE SYSTEMS</option>
        <option value="1">CAR CARE SERVICES</option>
        <option value="4">COMFORT CONTROL</option>
        <option value="3">COOLING SYSTEM</option>
        <option value="9">OTHER</option>
    </select>
    <a class="delete_row" href="#" title="Remove row">D</a>
</div>
</td>
<td valign="top" align="center"><select class="service_subcat"></select></td>
<td valign="top" align="center"><div class="invtbox">---</div></td>
<td><textarea class="descbox expand80-1000" name="descbox[]" rows="8" cols="40">Make A Service or Product Selection</textarea></td>
<td valign="top" align="center"><input class="qtybox qty combo3" name="qtybox[]" type="text" size="3" maxlength="3" value="0" rel="code_id" title=""></td>
<td valign="top"><textarea class="costbox" name="costbox[]" rows="1" cols="5"></textarea></td>
<td valign="top" align="center" width="14%" align="left"><span class="pricebox price">$0</span></td>
</tr>
</tbody>

<tbody id="footer">
<tr>
<td><a href="#" id="add_row" title="Add a row">Add an Item</a></td>
<tr>
</tbody>
</table>

inline styles removed for clarity

This may not be 100% correct but should give you a good idea of the way to go.

Airshow

Airshow;

Thank you for the code. Your explanation seems pretty straight forward --as to the strategy employed; however, I seem to be missing something. On test, I'm unable to get the function for the secondary list boxes to work. Here is what i have.

javascript for both initial list box:

<script type="text/javascript">
// ajax for multiple dropdown boxes

$(document).ready(function() {

    var data = <?php echo $arr;?>;
    var id;
    function getData(passCatID){
         var content = '';
        var content = '<option name="subcat_label" id="subcat_label" style="background: url() right no-repeat; width: 20px">------SPECIFY-----</option>';
        var product_desc = '';
        var invt = '';
        var qty = '';
        var cost = '';
        id = passCatID;
        $.each(data[id], function(key,value) {          
            if(invt == '')invt = value['invt'];
            if(product_desc == '')product_desc = value['product_desc'];
            if(cost == '')cost = value['cost'];
             content += '<option value="' + key + '">' + value['subcat_label'] + '</option>';

        });
        $('#service_subcat').html(content);
        $('#descbox').html( product_desc );
        $('#invtbox').html(invt);
        $('#qtybox').html( qty);
        $('#costbox').html(cost);
    }
    function getDesc(passItemID){
        var itemID = passItemID;
        var invt = data[id][itemID]['invt'];
        $('#invtbox').html(invt);  

         var product_desc = data[id][itemID]['product_desc'];
        $('#descbox').html( product_desc ); 

        var qty = data[id][itemID]['qty'];
        $('#qtybox').html( qty );   

        var cost = data[id][itemID]['cost'];
        $('#costbox').html( cost );      
    }
    $('#categories').change(function(){
       id = $('#categories').val();
       getData(id);
    });
    $('#service_subcat').change(function(){
       var itemID = $('#service_subcat').val();
       getDesc(itemID);
    }); 


$(function(){
    var data = <?php echo $arr; ?>;
    var $row_template = $('#row_template tr'),
        $item_rows_container = $("#item_rows");
    $("#add_row").on('click', function(){
        if ($item_rows_container.find("tr").length < 10){
            $row_template.clone().appendTo($item_rows_container);
        }
        return false;
    });
    $item_rows_container.on('click', '.delete_row', function(){
        if(confirm("Confirm Deleting Row?")){
            $(this).closest("tr").remove();
        }
    }).on('change', '.categories', function(){//this anonymous function was addfield()
        var $this = $(this);
        var d = data[$this.val()] || {};
        var $row = $this.closest("tr");
        var $subcat_menu = $('.service_subcat', $row).empty().append('<option name="specify">------SPECIFY-----</option>');
        $.each(d, function(key, value) {
            $subcat_menu.append('<option value="' + key + '">' + value['subcat_label'] + '</option>');
        });
        $subcat_menu.trigger('change');

    }).on('change', '.service_subcat', function(){
        var $this = $(this);
        var $row = $this.closest("tr");
        var category = $row.find(".categories").val();
        var d = (category && data[category] && data[category][$this.val()]) ? data[category][$this.val()] : {};
        $('.invtbox', $row).html( d['invt'] || '' );
        $('.descbox', $row).html( d['product_desc'] || '' );
        $('.qtybox', $row).html( d['qty'] || '' );
        $('.costbox', $row).html( d['cost'] || '' );
    });
});

});
</script>

then, the html for the secondary list boxes

<tbody id="row_template" style="display:none">
<tr class="item-row">
<td class="item-name" valign="top" align=center style="border-left: 0px solid #cccccc;" width="100%">
<div class="delete-wpr"><?php echo $dd1;?>
<a class="delete_row" href="#" title="Remove row">D</a>
</div>
</td>
<td valign="top" align="center"><select class="service_subcat" style="color:#003399; text-align:justify; font-size:1.0em; border-left: none; border-right: none; border-bottom: none"></select></td>
<td valign="top" align="center"><div class="invtbox">---</div></td>
<td><textarea class="descbox expand80-1000" name="descbox[]" rows="8" cols="40" style="color:#003399; text-align:justify; font-size:1.0em; border-left: none; border-right: none; border-bottom: none">Make A Service or Product Selection</textarea></td>
<td valign="top" align="center"><input class="qtybox qty combo3" name="qtybox[]" type="text" size="3" maxlength="3" value="0" rel="code_id" title=""></td>
<td valign="top"><textarea class="costbox" name="costbox[]" rows="1" cols="5"></textarea></td>
<td valign="top" align="center" width="14%" align="left"><span class="pricebox price">$0</span></td>
</tr>
</tbody>     
            <tr><!-----td----></tr>

<tbody id="footer2">
<tr>
<td><a href="#" id="add_row" title="Add a row" class="classname">Add an Item</a></td>
<tr>
</tbody>

It does not seem to work. Maybe my understanding is not correct of the strategy employed.

Here is the link to the form Click Here

Can you deduce what I'm doing wrong?

Mossa

Mossa,

My code above was written to :

  • provide a 'click' action for the "Add an item" button
  • provide a 'select' action for the primary and secondary select menus
  • provide a 'click' action for the row delete buttons
  • work on the original item row and those added dynamically.

As such the code should replace all previous attempts at getting these actions working.

The HTML also needs a total review. After indenting consistently for readability, check that all tags are balanced and properly nested and no ids are duplicated.

It's about half to one a day's work just to tidy up before you can start debugging.

Here's a demo with the HTML and javascript trimmed right down. I've actually simplified things slighly more in the demo in that it doesn't rely on the initial item row being served with the page. Instead, the statement $("#add_row").on('click', function() {...}).trigger('click'); creates the initial row by simulating a click on the "Add an item" button.

Airshow

My apology for the misunderstanding. I'll rework it!

Again, thank you, and I'll update shortly!

Mossa--

Mossa, please don't apologise. I should have explained.

Thank you Adrian!

I now have it working as expected; however, with a small glitch in my math function. I believe the

<tbody id="row_template" style="display:none" >

is clashing with

//update_prices and accrue subtotal
    $('.price').each(function(i) {
        var row = $(this).parents('.item-row');
        var price = row.find('.costbox').val().fromCurrency() * Number(row.find('.qtybox').val());
        $(this).html(isNaN(price) ? errorMsg : price.toCurrency());//Display value or 'error' if calculation failed
        subtotal += price.toCurrency().fromCurrency();//"double-shuffle" to ensure the calculation is consistent with the displayed data.
    });

in particular "var row = $(this).parents('.item-row');". The function allows instantaneous calculation as the costbox and qtybox are updated. At the moment, the calculation is only performed when the tax checkbox is selected -- a bit abnormal! Click Here for the form

In debugging, If I change the

<tbody id="row_template" style="display:none" >

to simply

  <tbody>

The calculation works fine!

Any thoughts on this!

Mossa

Hi Mossa,

<tbody id="row_template" style="display:none" > is important for isolating, identifying and hiding the item_row template.

I expect you just need to ensure that the template is ignored when prices are calculated. This can be done by specifying $item_rows_container as the context for $('.price').

//update_prices and accrue subtotal
$('.price', $item_rows_container).each(function(i) {
    var row = $(this).closest('tr');
    var price = row.find('.costbox').val().fromCurrency() * Number(row.find('.qtybox').val());
    $(this).html(isNaN(price) ? errorMsg : price.toCurrency());//Display value or 'error' if calculation failed
    subtotal += price.toCurrency().fromCurrency();//"double-shuffle" to ensure the calculation is consistent with the displayed data.
}); 

Notes :

  • For $item_rows_container to be available, this code must be in the same $(function(){...}) closure as the var $item_rows_container = ... statement.
  • I also simplified the var row = ... statement.

Airshow

Thanks Airshow!

This too, produces the same outcome. The calculations are only performed after the tax check box is checked! It also does not display the subtotal and total(including the tax rate).

<!-- replace this with your modified one
    $('.price').each(function(i) {
        var row = $(this).parents('.item-row');
        var price = row.find('.costbox').val().fromCurrency() * Number(row.find('.qtybox').val());
        $(this).html(isNaN(price) ? errorMsg : price.toCurrency());//Display value or 'error' if calculation failed
        subtotal += price.toCurrency().fromCurrency();//"double-shuffle" to ensure the calculation is consistent with the displayed data.
    });
-->


$('.pricebox', $item_rows_container).each(function(i) {
    var row = $(this).closest('tr');
    var pricebox = row.find('.costbox').val().fromCurrency() * Number(row.find('.qtybox').val());
    $(this).html(isNaN(pricebox) ? errorMsg : pricebox.toCurrency());//Display value or 'error' if calculation failed
    subtotal += pricebox.toCurrency().fromCurrency();
}); 

if you click on the link to the form, you'll see that first row on the form (containing the labor selection ) works fine. when the qty and cost are updated, the calculation is performed as intended. This row is outside of the #row_template.

Mossa--

Mossa, I have some ideas but am distracted today with something else. Will reply ASAP.

I appreciate your time, and by all means attend to your priorities first.

Best,
Mossa

Airshow, this is simply an update on the form calculation.

With more debugging, I'm able to get the initial row to calculate instantaneously. But when an additional item is added, it reverts back to only calculating after the tax checkbox is checked. I'm still debugging...

Mossa,

Some more event delegation is required :

$item_rows_container.on('blur keyup', '.costbox, .qtybox', update);

which can be chained as per the other delegated events, $item_rows_container.on(...).on(...).on(...);

You should be able to delete your bind() function in favour of the above though it's a little worrying when you say that the initial row calculates instantaneously. The initial row should now be added dynamically on page load and should thus be no different from other dynamically added rows.

update() should also be called in the chained delete_row handler :

.on('click', '.delete_row', function() {
    if (confirm("Delete row?")) {
        $(this).closest("tr").remove();
        update();
    }
}).....

The page still needs a lot of tidying. If you like I can send you a (largely) tidied version to show you what you might aspire to.

Airshow

Thanks Adrian! your suggested fixes have worked! Calculations are performing as expected. I agree on the tidying. I still got ways to go. Please do send the file for aspiration --that, perhaps would be helpful.

As always, thank you very much!
Mossa

After form has been submitted, I need to capture the text value of the selectbox option rather than its numeric value. Unfortunately, I cannot make the <option></option> value equal to the text value as my entire cascading effect is dependent on the numeric value of the first select box. so I came up with the idea of adding a hidden input, and set its value to the text of the dropdown onchange. That way I can recover the value on the server side script like this:

For my casacading pair of select boxes I have:
Primary select box

<select name=\"categories[]\" class=\"categories\" onchange=\"document.getElementById('cattext').value = this.options[this.selectedIndex].text;\" style=\"color:#003399; text-align:justify; font-size:1.0em; border-left: none; border-right: none; border-bottom: none\"><option value=\"0\">--Select One--</option>

secondary select box

 <select name="service_subcat[]" onchange="document.getElementById('sub_cattext').value = this.options[this.selectedIndex].text;" ></select>

with my hidden input box like so:

<input type='hidden' name='categoriesText' id='cattext' value=''>
     <input type='hidden' name='sub_categoriesText' id='sub_cattext' value=''>

This works great for one select box and its chained linked equivalent. Now suppose, my form offers the option of adding additional select boxes --dynamically, how would I add the hidden input boxes to correspond with each dynamically added select boxes?

Any thoughts!

Mossa,

You can approach this one of two ways.

  1. Send the numeric values and look up the text server-side from PHP arrays.

  2. Use hidden text fields, as you suggest. You will need two hidden fields per item_row, therefore add the following to the row template. :

    <input type='hidden' name='categoriesText[]' class='cattext' value=''>
    <input type='hidden' name='sub_categoriesText[]' class='sub_cattext' value=''>

Note, classes not ids.

Position within the template is pretty academic as these fields will be hidden.

To set the values, you need to add to the two 'change' handlers :

.on('change', '.categories', function() {
    ...
    $row.find(".cattext").val($this.find("option:selected").text()); //add this line
    ...
});   

and

.on('change', '.service_subcat', function() {
    ...
    $row.find(".sub_cattext").val($this.find("option:selected").text()); //add this line
    ...
});   

Test by temorarily setting type='hidden' to type='text'.

Airshow

Right on the mark! Airshow, you are indeed a master at this! On this alone, I spent a day searching for a solution.

Thank you!

Best,
Mossa

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.