en.javascript.info/2-ui/4-forms-controls/3-events-change/article.md
Ilya Kantor 1f61c2ab1d ok
2017-03-15 00:43:43 +03:00

11 KiB
Raw Blame History

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

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

Событие change

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

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

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

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

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

```warn header="Поздний onchange в IE8-" В IE8- checkbox/radio при изменении мышью не инициируют событие сразу, а ждут потери фокуса.

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


## Событие input

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

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

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

```html autorun height=40
<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 на key:Delete и key: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-декоратора, описанного в задаче info:task/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 и аналогичные не всплывают. То есть, обработчики нужно назначать на сам элемент, без делегирования.