1

I have a plain webpage with a singe button.

Using jQuery 3.3.1, a 'click' event is attached to this button such that once this button is clicked, the next click anywhere on the page(document) makes an alert('Clicked!').

<div id="container">
    <button id="Btn">Click</button>
</div>

<script type="text/javascript">
    $(document).ready(function() {
        $("#Btn").click(function() {
            $(document).click(function() {
                alert("Clicked!");
                $(document).unbind('click');
            });
        });
    });
</script>

But using this code, the first click on the button itself makes the alert('Clicked!').

I changed the code a bit and attached the 'click' event to $(document) inside a nested $(document).ready() :

<script type="text/javascript">
    $(document).ready(function() {
        $("#Btn").click(function() {
            $(document).ready(function() {   // <--here
                $(document).click(function() {
                    alert("Clicked!");
                    $(document).unbind('click');
                });
            });
        });
    });
</script>

This works as intended but I still can't infer such behaviour: why do I need to include a nested $(document).ready() ?

Or is there any other approach to achieve the desired functionality?

benTen
  • 13
  • 5

1 Answers1

2

The reason your first snippet generates alert on the first click on button is event propagation, specifically event bubbling. When you click the button, after the event handler has executed, the same event propagates up the DOM tree; eventually it reaches the document element, and triggers the new event handler that you subscribe to.

One of the approaches to ensure that the event doesn't immediately trigger at document would be to prevent the propagation of the event by calling event.stopPropagation().

You can see that in action in the snippet below:

$(document).ready(function() {
  $("#Btn").click(function(e) {
    e.stopPropagation();
  
    $(document).click(function() {
      alert("Clicked!");
      $(document).unbind('click');
    });
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
  <button id="Btn">Click</button>
</div>
Nisarg Shah
  • 14,151
  • 6
  • 34
  • 55
  • That makes sense. And what about the second code snippet in my question? Why does writing a nested `$(document).ready()` give the desired behaviour? – benTen Oct 04 '18 at 10:30
  • @benTen Which browser are you using? I tried your second snippet in Chrome 69, and it works similar to the first one, i.e. the clicked alert shows up as soon as I click the button. – Nisarg Shah Oct 04 '18 at 10:34
  • I am also using the same version of Chrome. Yes. you are right. The behaviour of second snippet is same with `jQuery 2.1.1` that you used in your code. I am using `jQuery 3.3.1` : [link](https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js) . It has different behaviour. Please check using that. Updated the question by mentioning the jQuery version. – benTen Oct 04 '18 at 11:04
  • @benTen Good catch! Looks like that is caused by [this breaking change in jQuery 3.0](https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous). The documentation notes that with 3.0+ version, the document-ready handlers will be invoked asynchronously. So in your case, the event handler is not bound to document element immediately. I suppose this allows the event to propagate up the call stack before that event is bound. – Nisarg Shah Oct 04 '18 at 11:10