Почему при вызове event.preventDefault() переключается флажок /radioobutton с ошибкой JavaScript?

Рассмотрим этот пример кода:

<span>
    <input type="checkbox">
</span>

$('span').click(function(e) {
    e.preventDefault();
    $(':checkbox')[0].checked = true;
});

Fiddle

Насколько мне известно, это должно произойти:

  • preventDefault() должен помешать проверке флажка по умолчанию браузера, даже если обработчик события указан выше в иерархии DOM. Эта часть работает правильно.
  • Настройка .checked = true должна работать, как я полагаю, она должна быть независимой от действия браузера по умолчанию для события, которое я отменил. Эта часть кажется ошибочной, как будто preventDefault() влияет на нее - удалите preventDefault() и она работает по назначению.

Какова фактическая причина, по которой флажок остается неизменным?

Я тестировал на Chrome 33 и Firefox 27, поэтому это не похоже на ошибку браузера.

Этот вопрос в основном объясняется любопытством расширить мои знания модели DOM/Event. Мне не нужны обходные пути, но я хочу знать, почему этот пример не работает.

Ответ 1

Учитывая ваш HTML:

<span>
    <input type="checkbox">
</span>

и ваш код:

$('span').click(function(e) {
    e.preventDefault();
    $(':checkbox')[0].checked = true;
});

Событие барботажа является вашей проблемой здесь.

При нажатии checkbox событие click распространяется на span. e.preventDefault() - это вызов объекта события, распространяемого из checkbox.

Следовательно, preventDefault() выполняется против самого checkbox, предотвращая его проверку.

Событие click checkbox отменяется с помощью preventDefault, оно будет отменено до завершения потока событий. (См. Нижнюю часть ответа для получения более подробной информации об этом)

Если вы применяете некоторый стиль к диапазону, вы заметите, что щелчок по checkbox ваш код работает так, как ожидалось, но нажатие на checkbox само реплицирует вашу проблему из-за вышеупомянутого.


DEMO - Оригинальные стили Fiddle+. Нажатие span в порядке, checkbox не


В соответствии с документацией MDN на preventDefault():

Вызов preventDefault в течение любого этапа потока событий отменяет событие, означающее, что любое действие по умолчанию, обычно принимаемое реализация в результате события не произойдет.

DOM Level 2 Spec также отмечает:

Если событие отменено, метод preventDefault используется для означает, что событие должно быть отменено, что означает любое действие по умолчанию обычно принимаемый реализацией в результате мероприятия, не будет происходят. Если на любом этапе потока событий метод preventDefault называется событием отменено.

DOM Level 3 Events Spec в примере 5:

Действие по умолчанию, связанное с событием click в <input type="checkbox">, переключает значение атрибута checked IDL этого элемента. Если действие события по умолчанию click отменено, значение возвращается в прежнее состояние.

Ответ 2

Обтекание части кода в setTimeout делает трюк, как вы можете видеть здесь http://jsfiddle.net/y7C77/

$('span').click(function(e) {
    e.preventDefault();
    setTimeout(function () {
        $('input').prop('checked', true);
    }, 1);
});

preventDefault() отменяет действие по умолчанию, и я думаю, что он может отменить его, даже если вы вручную сделаете то, что сделает браузер (в этом случае: изменение свойства checked).

Ответ 3

Вам нужно preventDefault как click, так и mouseup и установить флажок mouseup

попробовать:

$('span').click(function(e) {
    e.preventDefault();
}).mouseup(function(e){
    e.preventDefault();
    $(':checkbox')[0].checked = true;
});

Ответ 4

Это ожидаемое поведение, которое я чувствую с тех пор, когда вы говорите event.preventDefault(), вы поручаете браузеру не делать никаких операций над ним в этом случае. Таким образом, даже после утверждения, когда вы явно проверяете флажок, браузер не будет выполнять никаких операций с ним. Мы можем видеть разницу, когда я меняю событие следующим образом:

$('span').click(function(e) {
     e.preventDefault();
     //$(':checkbox')[0].checked = true;
     setTimeout(function(){$(':checkbox')[0].checked = true;});
});

Используя тайм-аут, мы меняем проверяемое свойство вне события, поэтому его проверили.