user5670402
user5670402

Reputation:

Jquery one() function fires twice on click

My Jquery one() function dies after second click instead of first click. Here is my HTML

<div class="box">
  <div class="call" data-tai="5">CLICK</div>
</div>

and heres my Jquery

$('body div').one('click', '.call', function() {
  var mother = $(this).parent();
      if(mother.css('position') === 'static') 
           mother.css('position', 'relative');

  var tai = $(this).data('tai');

  $.ajax({
      method: 'GET',
      url: '/bootstrap/call.php',
      data: 'tai='+tai,
      dataType: 'html',
      success: function(ret) {
          mother.append(ret);
      },
  });

  return false;
});

Interesting thing is, if i don't use return false;, it dies after first click. However bubbling occurs and it appends 2 html tags instead of 1, inside box element. Thanks for help

Upvotes: 0

Views: 1416

Answers (3)

Ram
Ram

Reputation: 144689

That's because event handlers bound by using .one will be fired once for each element in the jQuery collection. Since the return false stops the propagation of the event, if you click on the .call element, click handler of the parent element is not executed but the parent element still has an active click handler. You should use a more specific selector for selecting the target element. If the click handler should be bound to the div.call elements:

$('.box div.call').one(...);

Now, if .box elements have 9 div.call descendants then you have 9 click handlers! After clicking on each element jQuery unbinds the handler for that specific element.

It's not once for all elements, it's once for each element.

If the handler should be called once for all the matching elements you can use the delegation version of the .one method:

$(document).one('click', '.box div.call', function() {
    // ...
});

And if you want to delegate the event and have the handler working once for dynamically generated elements you can use the .on method and :not selector:

$(document).on('click', '.box .call:not(.clicked)', function() {
  $(this).addClass('clicked');
  // ...
});

Now the handler is called once for each .call element. Since :not excludes the elements that have .clicked class the selector doesn't match the already-clicked elements.

Upvotes: 1

Zach
Zach

Reputation: 3207

Events bubble in JavaScript. Your code

$('body div').one('click', '.call', function() {
}

wires up on both

<div class="box"> <!-- This -->
  <div class="call" data-tai="5">CLICK</div> <!-- And this -->
</div>

You need a more specific selector. If this div is a parent element in the body like this:

<body>
<div class="box">
  <div class="call" data-tai="5">CLICK</div>
</div>
<div class="box">
  <div class="call" data-tai="5">CLICK</div>
</div>
</body> 

then you can use a selector like this:

$('body > div').one('click', '.call', function() {
}

The question is - where do you expect your click event to be placed? Perhaps the div with the box class?

$('div.box').one('click', '.call', function() {
}

This assumes that the .call divs are being added dynamically to the .box div.

P.S. - if you want to stop the bubbling, I suggest you pass in the event object to your click event and call stopPropagation()

$('div.box').one('click', '.call', function(evt) {
  evt.stopPropagation(); // no bubbling
}

Upvotes: 0

phreakv6
phreakv6

Reputation: 2165

$('body div')

would select both the divs and attach click handlers to both of them. When you click on the nested div then, both clicks will be fired. Use a specific selector to avoid this.

$('.call')

could perhaps achieve this.

Upvotes: 2

Related Questions