Анатомия .click() или история одной проблемы
Доброго времени суток, товарищи.
Столкнулся с одной проблемой в JQuery и теперь хочу выяснить: все как всегда (сам дурак и прощай остатки кармы) или все же это такая особенность JQuery?
Положим, есть следующий HTML-код:
Схема работы предельно простая: чекбокс #chb управляет активностью текстового поля #txt (когда checked — !disabled и наоборот), а чекбокс #dis отключает (disabled=«disabled») чекбокс #chb и переключает его состояние.
Вот примерный код:
Казалось бы, ничего необычного?
Кликая по #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.
Вот так мне видится решение:
Таким образом, из mouseup мы сделали click с предсказуемым поведением.
Прошу прощения за стиль изложения, читать трудно, но я не умею писать по-другому. Надеюсь, когда-нибудь кому-нибудь это пригодится.
А в комментариях буду рад объяснениям столь необычного явления, критике и более простым решениям (если они вообще требуются). Спасибо за внимание.
upd: Хабраюзер Yeah подсказал функцию triggerHandler, которая может вызывать обработчики, повешенные на события, без срабатывания стандартного поведения. Тогда можно сделать проще, изменив в начальном варианте пару строк:
И никаких mouseup. Но остается вопрос в причинах такого поведения.
_________
Текст подготовлен в ХабраРедакторе
Столкнулся с одной проблемой в JQuery и теперь хочу выяснить: все как всегда (сам дурак и прощай остатки кармы) или все же это такая особенность JQuery?
Положим, есть следующий HTML-код:
<input id="dis" type="checkbox" />
<input id="chb" type="checkbox" /><input id="txt" type="text" />
* This source code was highlighted with Source Code Highlighter.Схема работы предельно простая: чекбокс #chb управляет активностью текстового поля #txt (когда checked — !disabled и наоборот), а чекбокс #dis отключает (disabled=«disabled») чекбокс #chb и переключает его состояние.
Вот примерный код:
$("#chb").click(function() {
$("#txt").attr("disabled", ! this.checked);
});
$("#dis").click(function() {
if ( $("#chb").attr("checked") )
{
$("#chb").click();
}
$("#chb").attr("disabled", this.checked);
});
* 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) {
// отключаем стандартную обработку клика, сами справимся
e.preventDefault();
});
$("#dis").click(function() {
if ( $("#chb").attr("checked") )
{
$("#chb").mouseup();
}
$("#chb").attr("disabled", this.checked);
});
$("#chb").mouseup(function() {
// имитация клика — переключение
this.checked = ! this.checked;
$("#txt").attr("disabled", ! this.checked);
});
* 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. Но остается вопрос в причинах такого поведения.
_________
Текст подготовлен в ХабраРедакторе



комментарии (11)