Как стать автором
Обновить

Анатомия .click() или история одной проблемы

Время на прочтение4 мин
Количество просмотров29K
Доброго времени суток, товарищи.

Столкнулся с одной проблемой в JQuery и теперь хочу выяснить: все как всегда (сам дурак и прощай остатки кармы) или все же это такая особенность JQuery?

Положим, есть следующий HTML-код:
<input id="dis" type="checkbox" /><br><input id="chb" type="checkbox" /><input id="txt" type="text" /><br><br>* This source code was highlighted with Source Code Highlighter.


Схема работы предельно простая: чекбокс #chb управляет активностью текстового поля #txt (когда checked — !disabled и наоборот), а чекбокс #dis отключает (disabled=«disabled») чекбокс #chb и переключает его состояние.

Вот примерный код:
$("#chb").click(function() {<br>    $("#txt").attr("disabled", ! this.checked);<br>});<br><br>$("#dis").click(function() {<br>    if ( $("#chb").attr("checked") )<br>    {<br>        $("#chb").click();<br>    }<br><br>    $("#chb").attr("disabled", this.checked); <br>});<br><br>* This source code was highlighted with Source Code Highlighter.


Казалось бы, ничего необычного?

Кликая по #chb, текстовое поле будет включаться/выключаться как и надо. А вот кликая по #dis, мы получим лишь отключение #chb. А как же #txt? Ведь выполнение $("#chb").click() должно гарантировать и его отключение тоже. (Если нет — поправьте меня, пожалуйста, и дайте ссылку на объясняющую статью. Спасибо.)

Как показал тест, существует некое несоответствие в последовательности действий.

При клике на чекбоксе мышью:
1) переключить checked;
2) выполнить повешенные на клик обработчики.

При программном вызове click():
1) выполнить повешенные на клик обработчики;
2) переключить checked.

(вы можете проверить, повесив на click alert)

То есть, одна и та же (казалось бы) операция вызывает разные последовательности действий.

Теперь понятно, почему не отключается #txt: в момент, когда выполняется обработчик (проверяющий, а не checked ли управляющий чекбокс?), чекбокс все еще checked.

Так что же делать?



Раз мы знаем, что происходит, значит можем решить проблему.

Все было бы просто замечательно, если бы где-то хранилось не только текущее состоянии элемента (checked), но и изменение состояния (+1 — checked изменился с false на true, -1 — c true на false). К сожалению, насколько мне известно, JQuery за этим не следит.

Немного матчасти для понимания дальнейшего. При клике на элемент последовательно генерируются три события:
1) mousedown
2) mouseup
3) click
За переключение состояния чекбокса отвечает именно click. А раз он ведет себя не так, как нам хочется, мы отключим его совсем и будем использовать mouseup.

Вот так мне видится решение:
$("#chb").click(function(e) {<br>    // отключаем стандартную обработку клика, сами справимся<br>    e.preventDefault();<br>});<br><br>$("#dis").click(function() {<br>    if ( $("#chb").attr("checked") )<br>    {<br>        $("#chb").mouseup();<br>    }<br><br>    $("#chb").attr("disabled", this.checked);<br>});<br><br>$("#chb").mouseup(function() { <br>    // имитация клика — переключение<br>    this.checked = ! this.checked; <br><br>    $("#txt").attr("disabled", ! this.checked); <br>});<br><br>* This source code was highlighted with Source Code Highlighter.


Таким образом, из mouseup мы сделали click с предсказуемым поведением.

Прошу прощения за стиль изложения, читать трудно, но я не умею писать по-другому. Надеюсь, когда-нибудь кому-нибудь это пригодится.

А в комментариях буду рад объяснениям столь необычного явления, критике и более простым решениям (если они вообще требуются). Спасибо за внимание.

upd: Хабраюзер Yeah подсказал функцию triggerHandler, которая может вызывать обработчики, повешенные на события, без срабатывания стандартного поведения. Тогда можно сделать проще, изменив в начальном варианте пару строк:

$("#dis").click(function() {
  if ( $("#chb").attr("checked") )
  {
    // сделаем поведение однообразным: сначала снимем галочку, а затем вызовем обработчики
    $("#chb").attr("checked", false);
    $("#chb").triggerHandler('click');
  }

  $("#chb").attr("disabled", this.checked);
});

* This source code was highlighted with Source Code Highlighter.

И никаких mouseup. Но остается вопрос в причинах такого поведения.

Теги:
Хабы:
Всего голосов 16: ↑10 и ↓6+4
Комментарии11

Публикации