Изменение: change, input, cut, copy, paste

На элементах формы происходят события клавиатуры и мыши, но есть и несколько других, особенных событий.

Событие change

Событие change происходит по окончании изменения значения элемента формы, когда это изменение зафиксировано.

Для текстовых элементов это означает, что событие произойдёт не при каждом вводе, а при потере фокуса.

Например, пока вы набираете что-то в текстовом поле ниже – события нет. Но как только вы уведёте фокус на другой элемент, например, нажмёте кнопку – произойдет событие onchange.

<input type="text" onchange="alert(this.value)">
<input type="button" value="Кнопка">

Для остальных же элементов: select, input type=checkbox/radio оно срабатывает сразу при выборе значения.

Поздний onchange в IE8-

В IE8- checkbox/radio при изменении мышью не инициируют событие сразу, а ждут потери фокуса.

Для того, чтобы видеть изменения checkbox/radio тут же – в IE8- нужно повесить обработчик на событие click (оно произойдет и при изменении значения с клавиатуры) или воспользоваться событием propertychange, описанным далее.

Событие input

Событие input срабатывает тут же при изменении значения текстового элемента и поддерживается всеми браузерами, кроме IE8-.

В IE9 оно поддерживается частично, а именно – не возникает при удалении символов (как и onpropertychange).

Пример использования (не работает в IE8-):

<input type="text"> oninput: <span id="result"></span>
<script>
  var input = document.body.children[0];

  input.oninput = function() {
    document.getElementById('result').innerHTML = input.value;
  };
</script>

В современных браузерах oninput – самое главное событие для работы с элементом формы. Именно его, а не keydown/keypress следует использовать.

Если бы ещё не проблемы со старыми IE… Впрочем, их можно решить при помощи события propertychange.

IE10-, событие propertychange

Это событие происходит только в IE10-, при любом изменении свойства. Оно позволяет отлавливать изменение тут же. Оно нестандартное, и его основная область использования – исправление недочётов обработки событий в старых IE.

Если поставить его на checkbox в IE8-, то получится «правильное» событие change:

<input type="checkbox"> Чекбокс с "onchange", работающим везде одинаково
<script>
  var checkbox = document.body.children[0];

  if ("onpropertychange" in checkbox) {
    // старый IE
    checkbox.onpropertychange = function() {
      // проверим имя изменённого свойства
      if (event.propertyName == "checked") {
        alert( checkbox.checked );
      }
    };
  } else {
    // остальные браузеры
    checkbox.onchange = function() {
      alert( checkbox.checked );
    };
  }
</script>

Это событие также срабатывает при изменении значения текстового элемента. Поэтому его можно использовать в старых IE вместо oninput.

К сожалению, в IE9 у него недочёт: оно не срабатывает при удалении символов. Поэтому сочетания onpropertychange + oninput недостаточно, чтобы поймать любое изменение поля в старых IE. Далее мы рассмотрим пример, как это можно сделать иначе.

События cut, copy, paste

Эти события используются редко. Они происходят при вырезании/вставке/копировании значения.

К сожалению, кросс-браузерного способа получить данные, которые вставляются/копируются, не существует, поэтому их основное применение – это отмена соответствующей операции.

Например, вот так:

<input type="text" id="input"> event: <span id="result"></span>
<script>
  input.oncut = input.oncopy = input.onpaste = function(event) {
    result.innerHTML = event.type + ' ' + input.value;
    return false;
  };
</script>

Пример: поле с контролем СМС

Как видим, событий несколько и они взаимно дополняют друг друга.

Посмотрим, как их использовать, на примере.

Сделаем поле для СМС, рядом с которым должно показываться число символов, обновляющееся при каждом изменении поля.

Как такое реализовать?

Событие input идеально решит задачу во всех браузерах, кроме IE9-. Собственно, если IE9- нам не нужен, то на этом можно и остановиться.

IE9-

В IE8- событие input не поддерживается, но, как мы видели ранее, есть onpropertychange, которое может заменить его.

Что же касается IE9 – там поддерживаются и input и onpropertychange, но они оба не работают при удалении символов. Поэтому мы будем отслеживать удаление при помощи keyup на Delete и BackSpace . А вот удаление командой «вырезать» из меню – сможет отловить лишь oncut.

Получается вот такая комбинация:

<input type="text" id="sms"> символов: <span id="result"></span>
<script>
  function showCount() {
    result.innerHTML = sms.value.length;
  }

  sms.onkeyup = sms.oninput = showCount;
  sms.onpropertychange = function() {
    if (event.propertyName == "value") showCount();
  }
  sms.oncut = function() {
    setTimeout(showCount, 0); // на момент oncut значение еще старое
  };
</script>

Здесь мы добавили вызов showCount на все события, которые могут приводить к изменению значения. Да, иногда изменение будет обрабатываться несколько раз, но зато с гарантией. А лишние вызовы легко убрать, например, при помощи throttle-декоратора, описанного в задаче Тормозилка.

Есть и совсем другой простой, но действенный вариант: через setInterval регулярно проверять значение и, если оно слишком длинное, обрезать его.

Чтобы сэкономить ресурсы браузера, мы можем начинать отслеживание по onfocus, а прекращать – по onblur, вот так:

<input type="text" id="sms"> символов: <span id="result"></span>

<script>
  var timerId;

  sms.onfocus = function() {

    var lastValue = sms.value;
    timerId = setInterval(function() {
      if (sms.value != lastValue) {
        showCount();
        lastValue = sms.value;
      }
    }, 20);
  };

  sms.onblur = function() {
    clearInterval(timerId);
  };

  function showCount() {
    result.innerHTML = sms.value.length;
  }
</script>

Обратим внимание – весь этот «танец с бубном» нужен только для поддержки IE8-, в которых не поддерживается oninput и IE9, где oninput не работает при удалении.

Итого

События изменения данных:

Событие Описание Особенности
change Изменение значения любого элемента формы. Для текстовых элементов срабатывает при потере фокуса. В IE8- на чекбоксах ждет потери фокуса, поэтому для мгновенной реакции ставят также onclick-обработчик или onpropertychange.
input Событие срабатывает только на текстовых элементах. Оно не ждет потери фокуса, в отличие от change. В IE8- не поддерживается, в IE9 не работает при удалении символов.
propertychange Только для IE10-. Универсальное событие для отслеживания изменения свойств элементов. Имя изменённого свойства содержится в event.propertyName. Используют для мгновенной реакции на изменение значения в старых IE. В IE9 не срабатывает при удалении символов.
cut/copy/paste Срабатывают при вставке/копировании/удалении текста. Если в их обработчиках отменить действие браузера, то вставки/копирования/удаления не произойдёт. Вставляемое значение получить нельзя: на момент срабатывания события в элементе всё ещё старое значение, а новое недоступно.

Ещё особенность: в IE8- события change, propertychange, cut и аналогичные не всплывают. То есть, обработчики нужно назначать на сам элемент, без делегирования.

Задачи

важность: 5

Создайте интерфейс для автоматического вычисления процентов по вкладу.

Ставка фиксирована: 12% годовых. При включённом поле «капитализация» – проценты приплюсовываются к сумме вклада каждый месяц (сложный процент).

Пример:

Технические требования:

  • В поле с суммой должно быть нельзя ввести не-цифру. При этом пусть в нём работают специальные клавиши и сочетания Ctrl-X/Ctrl-V.
  • Изменения в форме отражаются в результатах сразу.

Открыть песочницу для задачи.

Алгоритм решения такой.

Только численный ввод в поле с суммой разрешаем, повесив обработчик на keypress.

Отслеживаем события изменения для перевычисления результатов:

  • На input: событие input и дополнительно propertychange/keyup для совместимости со старыми IE.
  • На checkbox: событие click вместо change для совместимости с IE8-.
  • На select: событие change.

Открыть решение в песочнице.

Карта учебника

Комментарии

перед тем как писать…
  • Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.