March 18, 2013

How to control jQuery events when you have nested HTML?

Question by Cristian David Jimenez Duarte

For example:

<ul>
<li>
   first element
   <ul>
    <li> second element
       <ul>
          <li> third element </li>
       </ul>
      </li>
   </ul>
</li>
</ul>

<script>
$('li').click( function() {
   alert('hello world!!!');
});
</script>

The output is:

THREE ALERTS WHEN I CLICK THE THIRD ELEMENT

TWO ALERTS WHEN I CLICK THE SECOND ELEMENT

ONE ALERT WHEN I CLICK THE FIRST ELEMENT

How to prevent with jQuery this?

What I’m needing is: ONE ALERT FOR EACH CLICK IN ANY ‘LI’ ELEMENT.

Answer by Starx

As you have nested li elements. The click handler applies to all li elements including the parent li‘s.

You need to stop propagating the event bubble. So that once it execute the event handler, it does not goes up the DOM tree to trigger the same event.

$('li').click( function(e) {
   alert('hello world!!!');
   e.stopPropagation();
});
March 14, 2012

Adding datepicker event handler multiple times

Question by esviko

Using jQuery I’m trying to add the datepicker event handler to an input element.

    <div id="Box_tmpl" style="border:solid 1px #777; background:#ddd; display: none;">
        <a class="remove-box" href="#">remove</a>
        <div>
            <label for="PeriodStart">Period (start):</label>
            <input id="PeriodStart" class="start-end-date" name="period_start" readonly="readonly" />
        </div>
        <div>
            <label for="PeriodEnd">Period (end):</label>
            <input id="PeriodEnd" class="start-end-date" name="period_end" readonly="readonly" />
        </div>
    </div>

    <a class="add-box" href="#" style="margin: 6px 0 10px auto;">add</a>

    <div id="Boxes">
        <div style="border:solid 1px #777; background:#ddd;">
            <a class="remove-box" href="#">remove</a>
            <div>
                <label for="PeriodStart">Period (start):</label>
                <input id="PeriodStart" class="start-end-date" name="period_start" readonly="readonly" />
            </div>
            <div>
                <label for="PeriodEnd">Period (end):</label>
                <input id="PeriodEnd" class="start-end-date" name="period_end" readonly="readonly" />
            </div>
        </div>
    </div>

    <script type="text/javascript">
        $(function(){
            $('.start-end-date').datepicker({ dateFormat: "yy-mm-dd" });

            $('.remove-box').click(function(){
                $(this).parent().remove();
            });

            $('.add-box').click(function(){
                $('#Box_tmpl').clone().removeAttr('id').show().appendTo('#Boxes');
                $('.start-end-date').datepicker({ dateFormat: "yy-mm-dd" });
                $('.remove-box').click(function(){
                    $(this).parent().remove();
                });
                return false;
            });
        });
    </script>

Adding a new box works.
Adding the remove-box event handler within the new box works.
Adding the datepicker event handler withing the new box DOES NOT work. I don’t understand why…
Creating a new element using $().clone() does the new element inherit the old element’s event handlers? If it does, may be my problem is adding the datepicker event handler multiple times to the same element… I’m running out of ideas

Answer by StilgarBF

you have to use

$().clone(true)

http://api.jquery.com/clone/

withDataAndEventsA Boolean indicating whether event handlers should be copied along with the elements. As of jQuery 1.4, element data will be copied as well.

for your question in the comment: when initializing, the datepicker adds a class “hasDatepicker” to the input. you can not reinitialize an Input with that class,
So if you want NOT to clone the events, you have to .removeClass('hasDatepicker') from your cloned input, then initialize it.

the code in your fiddle has to be changed:

$('#Box_tmpl').clone().removeAttr('id').find('input.start-end-date').removeClass('hasDatepicker').end().show().appendTo('#Boxes');
$('.start-end-date').datepicker({ dateFormat: "yy-mm-dd" });

note: .end() rewinds to the state it was until find()

Answer by Starx

In order to bind the event to the dynamically created or cloned elements use .on()

$('#Boxes').on('click', '.remove-box', function(){
   $(this).parent().remove();
});

Demo

...

Please fill the form - I will response as fast as I can!