Елемент отримує фокус коли користувач або клікає на нього, або натискає клавішу Tab на клавіатурі. Також є HTML-атрибут autofocus
, який автоматично встановлює фокус на елементі при завантажені сторінки. Є й інші способи отримання фокусу які ми розглянемо далі.
Фокусування на елементі загалом означає: “підготуватися до прийняття даних на елементі”, тому це підходящий момент щоб запустити код, який ініціалізує необхідну функціональність.
Момент втрати фокусу(“blur”) може бути ще важливішим. Це відбувається, коли користувач клікає деінде або натискає Tab щоб перейти до наступного поля форми. Є й інші причини втрати фокусу які ми розглянемо далі.
Втрата фокусу загалом означає: “дані вже введені”, тому можна виконати перевірку або навіть зберегти їх на сервер і таке інше.
У роботі з подіями фокусування є важливі особливості. Ми спробуємо розібрати їх далі.
Події focus/blur
Подія focus
викликається в момент фокусування, а подія blur
– коли елемент втрачає фокус.
Використаймо їх для валідації поля форми.
В наведеному нижче прикладі:
- Обробник події
blur
перевіряє чи заповнено поле email, а якщо ні – показує помилку. - Обробник події
focus
ховає повідомлення про помилку (наblur
перевірку буде виконано ще раз):
<style>
.invalid { border-color: red; }
#error { color: red }
</style>
Ваша електронна адреса: <input type="email" id="input">
<div id="error"></div>
<script>
input.onblur = function() {
if (!input.value.includes('@')) { // це не електронна адреса
input.classList.add('invalid');
error.innerHTML = 'Будь ласка, введіть правильну електронну адресу.'
}
};
input.onfocus = function() {
if (this.classList.contains('invalid')) {
// видалити індикатор помилки, тому що користувач хоче ввести дані заново
this.classList.remove('invalid');
error.innerHTML = "";
}
};
</script>
Сучасний HTML дозволяє нам виконувати багато валідацій(перевірок даних) за допомогою власних атрибутів елементів: required
, pattern
та інших. Іноді це саме те, що нам потрібно. Проте коли ми хочему більше гнучкості, можна скористатися JavaScript. Також ми можемо автоматично відправити оновлене значення поля на сервер, якщо воно правильне.
Методи focus/blur
Методи elem.focus()
та elem.blur()
встановлюють/прибирають фокус на елементі.
Наприклад, заборонимо відвідувачеві залишити поле, якщо введене значення не відповідає заданим умовам:
<style>
.error {
background: red;
}
</style>
Введіть вашу електронну адресу: <input type="email" id="input">
<input type="text" style="width:300px" placeholder="введіть невірну електронну адресу та встановіть фокус сюди">
<script>
input.onblur = function() {
if (!this.value.includes('@')) { // це не електронна адреса
// показати помилку
this.classList.add("error");
// ...та повернути фокус
input.focus();
} else {
this.classList.remove("error");
}
};
</script>
Це спрацює у всіх браузерах окрім Firefox (bug).
Якщо ми введемо щось в поле форми, а потім спробуємо натиснути Tab або клікнути в іншому місці, тоді onblur
поверне фокус полю.
Зверніть увагу, що ми не можемо “відвернути втрату фокусу” викликавши event.preventDefault()
в обробнику onblur
тому що onblur
cпрацьовує після того як елемент втратив фокус.
На практиці, однак, слід добре подумати, перш ніж впроваджувати щось подібне, тому що ми зазвичай повинні показувати помилки користувачу, але не повинні перешкоджати його просуванню у заповненні нашої форми. Вони можуть спочатку заповнити інші поля.
Елемент може втратити фокус з різних причин.
Одна з них – відвідувач клікнув в іншому місці. Але й сам JavaScript може спричинити це, наприклад:
alert
зміщує фокус на себе, тому елемент втрачає фокус (подіяblur
), а колиalert
закривається – фокус повертається елементу (подіяfocus
).- Якщо елемент видалили з DOM, це також призводить до втрати фокусу. Якщо пізніше його вставити знову, то фокус не повернеться.
Через ці особливості, поведінка обробників focus/blur
може бути непередбачуваною – вони викликатимуться коли нам це не потрібно.
Використовуйте ці події з обережністю. Якщо ми хочемо відслідковувати втрату фокусу ініційовану користувачем, уникайте можливостей спричинити її власноруч.
Дозволити фокусування на будь-якому елементі: tabindex
Багато елементів не підтримують фокусування за замовчуванням.
Їх список відрізняється у різних браузерах, але одне завжди вірно: підтримка focus/blur
гарантована для елементів з якими користувач може взаємодіяти: <button>
, <input>
, <select>
, <a>
та інших.
З іншого боку, елементи призначені для форматуваня, такі як <div>
, <span>
, <table>
– за замовчуванням не фокусуються. Метод elem.focus()
не працює з ними, а події focus/blur
ніколи не генеруються.
Цю поведінку можна змінити за допомогою HTML-атрибуту tabindex
.
Якщо елемент має атрибут tabindex
, то на ньому можна фокусуватися. Значенням атрибуту є порядковий номер елементу, коли для переходу між елементами використовується клавіша Tab (або аналогічна).
Тобто: якщо в нас є два елементи, у першого tabindex="1"
, а у другого tabindex="2"
, тоді перебуваючи на першому елементі і натиснувши клавішу Tab фокус зміститься на другий елемент.
Порядок наступний: елементи з заданим tabindex
від 1
і вище йдуть першими(в порядку значень tabindex
), а далі елементи без tabindex
(наприклад, звичайний <input>
).
Елементи з однаковим значенням tabindex
отримують фокус у типовому порядку, так як вони розміщені в документі.
Є два спеціальні значення:
-
tabindex="0"
включає елемент у звичайний порядок елементів безtabindex
. Тобто, коли ми перемикаємося між елементами, ті, що маютьtabindex=0
йдуть після тих, що зtabindex ≥ 1
.Зазвичай цей прийом використовують, щоб на елементі можна було сфокусуватися не змінюючи стандартний порядок перемикання. Щоб елемент став частиною форми на рівні з
<input>
. -
tabindex="-1"
дозволяє лише програмне фокусування на елементі. Клавіша Tab ігнорує такі елементи, проте методelem.focus()
спрацьовує.
Наприклад, є список. Клікніть на першому елементі та натисніть Tab:
Клікніть на першому елементі та натисніть Tab. Продовжуйте слідкувати за порядком. Зауважте, що натискаючи Tab багато разів підряд можна вийти за межі iframe з прикладом.
<ul>
<li tabindex="1">Один</li>
<li tabindex="0">Нуль</li>
<li tabindex="2">Два</li>
<li tabindex="-1">Мінус один</li>
</ul>
<style>
li { cursor: pointer; }
:focus { outline: 1px dashed green; }
</style>
Порядок буде таким: 1 - 2 - 0
. Зазвичай, <li>
не підтримує фокусування, але tabindex
вмикає його, разом із подіями та стилізацією псевдокласу :focus
.
elem.tabIndex
також працюєМи можемо додати tabindex
з JavaScript за допомогою властивості elem.tabIndex
. Це має такий самий ефект.
Делегування: focusin/focusout
Події focus
та blur
не спливають.
Наприклад, ми не можемо встановити onfocus
на <form>
щоб виділити її:
<!-- додати класс при фокусуванні на формі -->
<form onfocus="this.className='focused'">
<input type="text" name="name" value="Ім’я">
<input type="text" name="surname" value="Прізвище">
</form>
<style> .focused { outline: 1px solid red; } </style>
Наведений приклад не працює, тому що коли користувач фокусується на <input>
, подія focus
відбувається тільки на самому полі input
. Вона не підіймається вище. Тому form.onfocus
ніколи не виконується.
Існує два рішення.
Перше, є цікава історична особливість: focus/blur
не спливає, проте розповсюджується вниз на фазі захоплення.
Ось це спрацює:
<form id="form">
<input type="text" name="name" value="Name">
<input type="text" name="surname" value="Surname">
</form>
<style> .focused { outline: 1px solid red; } </style>
<script>
// встановіть обробник на фазі захоплення (останній аргумент true)
form.addEventListener("focus", () => form.classList.add('focused'), true);
form.addEventListener("blur", () => form.classList.remove('focused'), true);
</script>
Друге, є події focusin
та focusout
– такі ж самі як focus/blur
, тільки вони спливають.
Зауважте, що вони мають використовуватися з elem.addEventListener
, а не з on<event>
.
Отже, ще один робочий варіант:
<form id="form">
<input type="text" name="name" value="Ім’я">
<input type="text" name="surname" value="Прізвище">
</form>
<style> .focused { outline: 1px solid red; } </style>
<script>
form.addEventListener("focusin", () => form.classList.add('focused'));
form.addEventListener("focusout", () => form.classList.remove('focused'));
</script>
Підсумки
Події focus
та blur
викликають фокусування або втрату фокусу на елементі.
Їхніми особливостями є:
- Вони не спливають. Натомість можна використовувати фазу захоплення або
focusin/focusout
. - Більшість елементів не підтримує фокусування за замовчуванням. Використовуйте
tabindex
щоб на елементі можна було встановити фокус.
Поточний елемент в фокусі можна отримати з document.activeElement
.
Коментарі
<code>
, для кількох рядків – обгорніть їх тегом<pre>
, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)