Javascript – Disabling a button in vanilla JavaScript and in jQuery

dom-eventsevent handlingjavascriptjqueryjquery-events

Vanilla JavaScript

In vanilla JavaScript, one can easily enable and disable a button using the following statement:

button.disabled = state;

This works both when humans try to click a button and when buttons are clicked programmatically:

var button = document.getElementById('myButton');

button.addEventListener('click', function() {
    alert('world');
});

button.disabled = true;
button.click(); // No output
button.disabled = false;
button.click(); // Output : "Hello" and "world
button.disabled = true;
button.click(); // No output
<input type="button" id="myButton" value="button" onClick="alert('Hello')"/>

This also works when using the MouseEvent interface:

var button = document.getElementById('myButton');

var click = new MouseEvent("click", {
    "view": window
});

button.addEventListener('click', function() {
    alert('world');
});

button.disabled = true;
button.dispatchEvent(click); // No output
button.disabled = false;
button.dispatchEvent(click); // Output : "Hello" and "world
button.disabled = true;
button.dispatchEvent(click); // No output
<input type="button" id="myButton" value="button" onClick="alert('Hello')"/>

jQuery

I can't seem to be able to do the same with jQuery, though :

var button = $("#myButton");

button.on("click", function() {
    alert("world");
});

button.prop("disabled", true);
button.click(); // Output : "world" and "Hello"
button.prop("disabled", false);
button.click(); // Output : "world" and "Hello"
button.prop("disabled", true);
button.click(); // Output : "world" and "Hello"
<script src="https://code.jquery.com/jquery-1.12.2.min.js"></script>
<input type="button" id="myButton" value="button" onClick="alert('Hello')"/>

Both button.prop("disabled", true); and button.attr("disabled", true); simply change the disabled property of the button element, but neither disables the actual click event. This means that events are triggered whenever button.click(); is called, even if the button is disabled!

Additionally, "world" and "Hello" are output in the wrong order.

The simplest code I could come up with to emulate the behavior of the vanilla JavaScript versions, is this :

var button = $("#myButton");

button.on("click", function() {
    alert("world");
});

button.disable = (function() {
    var onclick = null;
    var click = [];
    return function(state) {
        if(state) {
            this.prop('disabled', true);
            if(this.prop('onclick') !== null) {
                onclick = this.prop('onclick');
                this.prop('onclick', null);
            }
            var listeners = $._data(this.get()[0], "events");
            listeners = typeof listeners === 'undefined' ? [] : listeners['click'];
            if(listeners && listeners.length > 0) {
                for(var i = 0; i < listeners.length; i++) {
                    click.push(listeners[i].handler);
                }
                this.off('click');
            }
        } else {
            this.removeProp('disabled');
            if(onclick !== null) {
                this.prop('onclick', onclick);
                onclick = null;
            }
            if(click.length > 0) {
                this.off('click');
                for(var i = 0; i < click.length; i++) {
                    this.on("click", click[i]);
                }
                click = [];
            }
        }
    }
})();

button.disable(true);
button.click(); // No output
button.disable(false);
button.click(); // Output : "Hello" and "world
button.disable(true);
button.click(); // No output
<script src="https://code.jquery.com/jquery-1.12.2.min.js"></script>
<input type="button" id="myButton" value="button" onClick="alert('Hello')"/>

That is, of course, ridiculously convoluted and "hacky" code to achieve something as simple as disabling a button.


My questions

  • Why is it that jQuery – unlike vanilla JS – doesn't disable the events when disabling a button?
  • Is this to be considered a bug or a feature in jQuery?
  • Is there something I'm overlooking?
  • Is there a simpler way to get the expected behavior in jQuery?

Best Answer

To achieve expected result, you can utilize .isTrigger within jQuery triggered click handler to determine if event is triggered by javascript, and not user action.

Define attribute event listener as a named function, where this can be passed to check disabled property at if condition if alert() is called, or not called.

Use .attr("disabled", "disabled") to set disabled at element, .removeAttr("disabled") to remove attribute; .attr("onclick", null) to remove event attribute onclick handler; .attr("onclick", "handleClick(true)") to reset event attribute.

<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

<input type="button" id="myButton" value="button" onclick="handleClick(this)" />
<script>
  function handleClick(el) {
    if (el.disabled !== "disabled")
      alert("Hello")
  }
  var button = $("#myButton");

  button.on("click", function(e) {
    console.log(e);
    if (e.isTrigger !== 3 && !e.target.disabled)
      alert("world");
  });

  button.attr("disabled", "disabled");
  button.attr("onclick", null);
  button.click(); // no output
  
  setTimeout(function() {
    button.removeAttr("disabled");
    button.attr("onclick", "handleClick(button[0])");
    button.click(); // Output : "world" and "Hello"
    // click button during 9000 between `setTimeout` calls
    // to call both jQuery event and event attribute
  }, 1000);

  setTimeout(function() {
    button.attr("disabled", "disabled");
    button.attr("onclick", null);
    button.click(); // no output
  }, 10000);
  
</script>