web-dev-qa-db-ja.com

Vanilla JavaScriptとjQueryでボタンを無効にする

バニラJavaScript

バニラJavaScriptでは、次のステートメントを使用してボタンを簡単に有効または無効にできます。

_button.disabled = state;
_

これは、人間がボタンをクリックしようとしたときと、プログラムでボタンをクリックしたときの両方で機能します。

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

これは、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

しかし、私はjQueryで同じことをすることができないようです:

_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);button.attr("disabled", true);はどちらも、ボタン要素のdisabledプロパティを変更するだけですが、実際のクリックイベントを無効にすることはありません。つまり、ボタンが無効になっていても、button.click();が呼び出されるたびにイベントがトリガーされます。

また、「world」と「Hello」が誤った順序で出力されます。

Vanilla JavaScriptバージョンの動作をエミュレートするために思いつくことができる最も単純なコードは、次のとおりです。

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

これはもちろん、ボタンを無効にするだけの簡単なことを実現するために、途方もなく複雑で「ハッキー」なコードです。


私の質問

  • なぜバニラJSとは異なり、jQueryはボタ​​ンを無効にしてもイベントを無効にしないのですか?
  • これは、jQueryのバグまたは機能と見なされますか?
  • 見落としているものはありますか?
  • JQueryで期待される動作を取得する簡単な方法はありますか?
16
John Slegers

期待される結果を達成するには、jQueryトリガーclickハンドラー内で.isTriggerを使用して、ユーザーアクションではなくjavascriptによってイベントがトリガーされるかどうかを判断できます。

属性イベントリスナーを名前付き関数として定義します。ここで、alert()が呼び出されたか呼び出されなかった場合、thisプロパティをチェックしてdisabled条件でifプロパティを確認できます。

.attr("disabled", "disabled")を使用してdisabledを要素に設定し、.removeAttr("disabled")を使用して属性を削除します。 .attr("onclick", null)は、イベント属性onclickハンドラーを削除します。 .attr("onclick", "handleClick(true)")イベント属性をリセットします。

<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

次の行で jquery-1.12.4.js を確認すると、

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" ) ) {

委任タイプに応じて、イベントの異なる処理が表示されます。

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

もちろん、次のようにイベントを添付した場合:

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

動作が異なり、奇妙に見えます。

1
gaetanoM

無効化された属性に関係なく発生するクリック関数を3回(button.click())直接呼び出しています。

Disabledプロパティはクリックイベントにのみ応答します。

更新された例を参照してください。

    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

.prop()を使用するのが正しい方法です。問題はあなたがそれを「テスト」している方法にあると思います。ハンドラーがonclickまたは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"/>

他の明白な解決策は、バニラJavaScriptを使用することです。 jQueryを使用しているからといって、jQueryを使用してすべてを実行する必要があるというわけではありません。 jQueryがなくても問題ないことがいくつかあります。

編集:スニペットを編集して、jqueryの.click()が実際にアラートをトリガーしないようにする方法を示しました。

0
mcgraphix