web-dev-qa-db-fra.com

Désactivation d'un bouton dans JavaScript vanille et dans jQuery

Vanilla JavaScript

Dans Vanilla JavaScript, vous pouvez facilement activer et désactiver un bouton à l’aide de l’instruction suivante:

button.disabled = state;

Cela fonctionne à la fois lorsque les humains essaient de cliquer sur un bouton et lorsque l'utilisateur clique sur les boutons par programmation:

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')"/>

Cela fonctionne également lors de l'utilisation de l'interface MouseEvent:

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

Je n'arrive pas à faire la même chose avec jQuery, cependant:

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')"/>

button.prop("disabled", true); et button.attr("disabled", true); modifient simplement la propriété disabled de l'élément button, mais ne désactivent pas l'événement de clic réel. Cela signifie que les événements sont déclenchés à chaque appel de button.click();, même si le bouton est désactivé!

De plus, "world" et "Hello" sont sortis dans le mauvais ordre.

Le code le plus simple que j'ai pu créer pour émuler le comportement des versions JavaScript de Vanilla est le suivant:

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')"/>

C’est, bien sûr, un code ridiculement compliqué et "hacky" pour réaliser quelque chose d'aussi simple que de désactiver un bouton.


Mes questions

  • Pourquoi jQuery, contrairement à Vanilla JS, ne désactive-t-il pas les événements lors de la désactivation d'un bouton?
  • Est-ce que cela doit être considéré comme un bogue ou une fonctionnalité de jQuery?
  • Y a-t-il quelque chose que je néglige?
  • Existe-t-il un moyen plus simple d'obtenir le comportement attendu dans jQuery?
16
John Slegers

Pour obtenir le résultat attendu, vous pouvez utiliser .isTrigger dans le gestionnaire click déclenché par jQuery afin de déterminer si l'événement est déclenché par javascript et non par une action de l'utilisateur. 

Définissez le programme d'écoute des événements d'attributs en tant que fonction nommée, où this peut être passé pour vérifier la propriété disabled à la condition if si alert() est appelé ou non.

Utilisez .attr("disabled", "disabled") pour définir disabled au niveau de l'élément, .removeAttr("disabled") pour supprimer un attribut; .attr("onclick", null) pour supprimer l'attribut d'événement onclick handler; .attr("onclick", "handleClick(true)") pour réinitialiser l'attribut d'événement.

<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>

2
guest271314

Si vous regardez jquery-1.12.4.js sur ces lignes:

handlers: function( event, handlers ) {
    var i, matches, sel, handleObj,
        handlerQueue = [],
        delegateCount = handlers.delegateCount,
        cur = event.target;

    // Support (at least): Chrome, IE9
    // Find delegate handlers
    // Black-hole SVG <use> instance trees (#13180)
    //
    // Support: Firefox<=42+
    // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343)
    if ( delegateCount && cur.nodeType &&
        ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) {

        /* jshint eqeqeq: false */
        for ( ; cur != this; cur = cur.parentNode || this ) {
            /* jshint eqeqeq: true */

            // Don't check non-elements (#13208)
            // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
            if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) {

Vous verrez un traitement différent des événements en fonction du type de délégation:

$(document).on("click", '#btn', function() {
  console.log("world");
});


$(function () {
  $('#btnToggle').on('click', function(e) {
    $('#btn').prop('disabled', !$('#btn').prop('disabled'));
  });
  
  
  $('#btnTestClick').on('click', function(e) {
    $('#btn').click();
  });
});
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>

<button id="btn">Click  Me</button>
<button id="btnToggle">Enable/Disable button</button>
<button id="btnTestClick">Test Click</button>

Bien sûr, si vous attachez l'événement comme dans:

$('#btn').on("click", function() {
    alert("world");
});

Le comportement est différent et semble étrange.

1
gaetanoM

Vous appelez la fonction de clic directement 3 fois (button.click ()) qui se déclenche quel que soit l'attribut désactivé. 

La propriété disabled ne répond qu'aux événements de clic. 

Voir l'exemple mis à jour: 

    var button = $("#myButton");
var button2 = $("#myButton2");

button.prop("disabled", false);

button.on("click", function() {
    alert("world");
   button2.prop("disabled", false);
});

button2.prop("disabled", true); 

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

0
Aaron_Peazzoni

Utiliser .prop () est la bonne façon de le faire. Je pense que le problème réside dans la façon dont vous le "testez". Voir cet exemple où les boutons sont désactivés/activés correctement à l'aide du bouton bascule, que le gestionnaire soit attaché via onclick ou avec jquery.

window.testFunc = function(event) {
  if (!$('#myButton2').prop('disabled')) {
     alert("hello");
     console.log("hello");
  }
}

$(document).ready(function() {
  var button = $("#myButton2");

  button.on("click", function(event) {
    if (!$(this).prop('disabled')) {
        alert("world");    
        console.log("world");
    }
  });

  $('#toggleButton').click(function() {
	  $('#myButton1').prop('disabled', !$('#myButton1').prop('disabled'));
      $('#myButton2').prop('disabled', !$('#myButton2').prop('disabled'));
  });
  
  $('#tester').click(function() {
  	$('#myButton1').click();
    $('#myButton2').click();
    
  });

})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" id="myButton1" value="Vanilla button (hello)" onclick="window.testFunc(event)"/>
<input type="button" id="myButton2" value="jquery button (world)"/>
<input type="button" id="toggleButton" value="toggle disabled"/>
<input type="button" id="tester" value="test the buttons"/>

L’autre solution évidente consiste simplement à utiliser javascript dans Vanilla. Le fait que vous utilisiez jQuery ne signifie pas que tout "doit" être utilisé. Il y a certaines choses que vous pouvez faire sans jQuery.

EDIT: J'ai édité l'extrait de code montrant comment vous pouvez empêcher le .click () de jquery de déclencher les alertes.

0
mcgraphix