21 Jan 2013

Bind Events to Dynamically-Added Elements with jQuery's .on()

This post follows one from another - Adding Form Controls Dynamically with jQuery. AT the end of that post, I mentioned that dynamically added form elements can often not have pre-defined events assigned to them as these elements are added after page load. Here's an example of adding a few textboxes to a form along with an associated button for incrementing the textbox value. This is just a trivial example, just to show the issues that can arise.
<form id="frm_numbers" method="post" action="anotherpage.php">
    <label for="tb_addinputs">No. Inputs to Add: </label> 
    <input id="tb_addinputs" type="number" min="1" max="6" value="1" /> 
    <button id="btn_addinputs">Add Inputs</button><br />
    <button id="btn_sync" style="display:none;">Sync All To First</button><br />
    <input type="submit" value="Upload" name="submit" id="submit" />
</form>
Here's some of the accompanying javascript, which all works fine:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
$(function(){
    //Extend native from http://bit.ly/11MheU1
    String.prototype.repeat = function(num) {
        return new Array(isNaN(num)? 1 : ++num).join(this);
    }
        
    //Add specific number of controls and show sync button
    $('#btn_addinputs').click(function(e){
        e.preventDefault();
        addNewTextInputs();
        $('#btn_sync').show();
    });
    
    //general function to add text inputs    
    function addNewTextInputs(){
        var str = '<label>MyNumber</label> ';
        str += '<input class="tb_numbers" type="number" value="0" />';
        str += '<button class="btn_addten">+10</button><br />';
        var numToAdd = parseInt($('#tb_addinputs').val());
        $('#btn_sync').before(str.repeat(numToAdd));
    }

    //sync button click to make all text inputs equal to the first one
    $('#btn_sync').click(function(e){
        e.preventDefault();    
        if($('.tb_numbers').length > 1)$('.tb_numbers').val($('.tb_numbers').eq(0).val());
    });
});
So, if we add 5 inputs, we get this:
OK, so far so good. If we manually change the inputs and press 'sync', again everything works fine.
Now the problem arises when we try to attach a .click event to the 'Add Ten' buttons, so that they can add 10 to the value in the associated number input. If the button was present on page load, then we could do this:
$('.btn_addten').click(function(e){
    e.preventDefault();
    num = parseInt($(this).prev().val()) + 10;
    $(this).prev().val(num);        
});
Unfortunately, this won't work, so we have to resort to using the .on() method, which is no hardship:
$("#frm_numbers").on("click", "btn_addten", function(e){
    e.preventDefault();
    num = parseInt($(this).prev().val()) + 10;
    $(this).prev().val(num);
});
Magically this does the business - click the 'Add Ten' button and the associated number input increments by 10.
OK, you can argue that this isn't the most inspiring of scripts, but at least it shows how you can bind an event to a dynamically-added element.

No comments:

Post a Comment