208 lines
10 KiB
Markdown
208 lines
10 KiB
Markdown
# Изменение: change, input, propertychange
|
||
|
||
На элементах формы происходят события клавиатуры и мыши, но есть и несколько других, особенных событий.
|
||
|
||
## Событие change
|
||
|
||
Событие [change](http://www.w3.org/TR/html5/forms.html#event-input-change) происходит по окончании изменении значения элемента формы, когда это изменение зафиксировано.
|
||
|
||
Для текстовых элементов это означает, что событие произойдёт не при каждом вводе, а при потере фокуса.
|
||
|
||
Например, пока вы набираете что-то в текстовом поле ниже -- события нет. Но как только вы уведёте фокус на другой элемент, например, нажмёте кнопку -- произойдет событие `onchange`.
|
||
|
||
```html
|
||
<!--+ autorun height=40 -->
|
||
<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`, описанным далее.
|
||
[/warn]
|
||
|
||
## Событие propertychange
|
||
|
||
Это событие происходит только в старых IE, до версии 11, при любом изменении свойства. Оно позволяет отлавливать изменение тут же и используется, преимущественно, для исправления ошибок в старых IE.
|
||
|
||
Если поставить его на `checkbox` в IE8-, то получится "правильное" событие `change`:
|
||
|
||
```html
|
||
<!--+ autorun height=40 -->
|
||
<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>
|
||
```
|
||
|
||
Это событие также срабатывает при изменении значения текстового элемента, но в IE9 у него ошибка: оно не срабатывает при удалении символов.
|
||
|
||
## Событие 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>
|
||
```
|
||
|
||
## События cut, copy, paste
|
||
|
||
Эти события используются редко. Они происходят при вырезании/вставке/копировании значения.
|
||
|
||
К сожалению, кросс-браузерного способа получить данные, которые вставляются/копируются, не существует, поэтому их основное применение -- это отмена соответствующей операции.
|
||
|
||
Например, вот так:
|
||
|
||
```html
|
||
<!--+ autorun height=40 -->
|
||
<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`.
|
||
|
||
Получается вот такая комбинация:
|
||
|
||
```html
|
||
<!--+ autorun run height=60 -->
|
||
<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`-декоратора, описанного в задаче [](/task/throttle).
|
||
|
||
**Есть и совсем другой простой, но действенный вариант: через `setInterval` регулярно проверять значение и, если оно слишком длинное, обрезать его.**
|
||
|
||
Чтобы сэкономить ресурсы браузера, мы можем начинать отслеживание по `onfocus`, а прекращать -- по `onblur`, вот так:
|
||
|
||
```html
|
||
<!--+ autorun height=60 -->
|
||
<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` не работает при удалении.
|
||
|
||
## Итого
|
||
|
||
События изменения данных:
|
||
|
||
<table class="bordered">
|
||
<tr>
|
||
<th>Событие</th>
|
||
<th>Описание</th>
|
||
<th>Особенности</th>
|
||
</tr>
|
||
<tr>
|
||
<td>`change`</td>
|
||
<td>Изменение значения любого элемента формы. Для текстовых элементов срабатывает при потере фокуса.</td>
|
||
<td>В IE8- на чекбоксах ждет потери фокуса, поэтому для мгновенной реакции ставят также `onclick`-обработчик или `onpropertychange`.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>`input`</td>
|
||
<td>Событие срабатывает только на текстовых элементах. Оно не ждет потери фокуса, в отличие от `change`.</td>
|
||
<td>В IE8- не поддерживается, в IE9 не работает при удалении символов.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>`propertychange`</td>
|
||
<td>Только для IE10-. Универсальное событие для отслеживания изменения свойств элементов. Имя изменённого свойства содержится в `event.propertyName`. Используют для мгновенной реакции на изменение значения в старых IE.
|
||
</td>
|
||
<td>В IE9 не срабатывает при удалении символов.</td>
|
||
</tr>
|
||
<tr>
|
||
<td>`cut/copy/paste`</td>
|
||
<td>Срабатывают при вставке/копировании/удалении текста. В них можно отменить действие браузера, и тогда вставке/копирования/удаления не произойдёт.</td>
|
||
<td>Вставляемое значение получить нельзя: на момент срабатывания события в элементе всё ещё *старое* значение, а новое недоступно.</td>
|
||
</tr>
|
||
</table>
|
||
|
||
Ещё особенность: в IE8- события `change`, `propertychange`, `cut` и аналогичные не всплывают. То есть, обработчики нужно назначать на сам элемент, без делегирования.
|
||
|