Кратко
СкопированоСобытие focus вызывается, когда элемент получает фокус. У этого события нет фазы всплытия.
Пример
Скопированоfocus удобно использовать, например, для отображения истории поиска при клике на поле:
Как пишется
СкопированоСовременный способ с addEventListener:
element.addEventListener('focus', (event) => { console.log('Элемент получил фокус')})
element.addEventListener('focus', (event) => {
console.log('Элемент получил фокус')
})
Также можно обработать событие через встроенный обработчик событий, но этот способ считается устаревшим.
element.onfocus = (event) => { console.log('Элемент получил фокус')}
element.onfocus = (event) => {
console.log('Элемент получил фокус')
}
Как понять
СкопированоСобытие focus инициируется в момент, когда интерактивный элемент получает фокус (например, по клику или переключению по Tab).
Событие срабатывает на тех html-элементах, которые имеют атрибут tabindex, либо атрибут contenteditable.
Некоторые html-элементы по умолчанию имеют tabindex:
<button>;<input>;<textarea>;<select>;<frame>;<iframe>;<object>;<a>или<area>с атрибутомhref;<summary>в связке с<details>.
Как добавить событие на любой элемент
СкопированоЧтобы focus работал на произвольном элементе (например, на <div>), добавьте ему атрибут tabindex или атрибут contenteditable.
tabindex указывает браузеру, что на элементе можно сфокусироваться (а, значит, и потерять фокус).
contenteditable указывает браузеру, что элемент может редактироваться пользователем.
<div tabindex="0"> Здесь можно сфокусироваться</div><div contenteditable="true"> Здесь так же можно сфокусироваться</div>
<div tabindex="0">
Здесь можно сфокусироваться
</div>
<div contenteditable="true">
Здесь так же можно сфокусироваться
</div>
Пример
СкопированоВ примере ниже одинаковый обработчик события окрашивает границы блока при получении фокуса. Кликните на любой блок:
<div class="wrapper"> <div>Блок не может получить фокус:</div> <div class="block" id="block1"></div></div><div class="wrapper"> <div>Блок может получить фокус (с tabindex):</div> <div class="block" id="block2" tabindex="0"></div></div><div class="wrapper"> <div>Блок может получить фокус (с contenteditable):</div> <div class="block" id="block3" contenteditable="true"></div></div>
<div class="wrapper">
<div>Блок не может получить фокус:</div>
<div class="block" id="block1"></div>
</div>
<div class="wrapper">
<div>Блок может получить фокус (с tabindex):</div>
<div class="block" id="block2" tabindex="0"></div>
</div>
<div class="wrapper">
<div>Блок может получить фокус (с contenteditable):</div>
<div class="block" id="block3" contenteditable="true"></div>
</div>
const block2 = document.getElementById('block2')const block3 = document.getElementById('block3')const block1 = document.getElementById('block1')function focusHandler(event) { event.target.classList.add('green') event.target.innerHTML = '👋 Я получил фокус'}block1.addEventListener('focus', focusHandler)block2.addEventListener('focus', focusHandler)block3.addEventListener('focus', focusHandler)
const block2 = document.getElementById('block2')
const block3 = document.getElementById('block3')
const block1 = document.getElementById('block1')
function focusHandler(event) {
event.target.classList.add('green')
event.target.innerHTML = '👋 Я получил фокус'
}
block1.addEventListener('focus', focusHandler)
block2.addEventListener('focus', focusHandler)
block3.addEventListener('focus', focusHandler)
Делегирование события
СкопированоИз-за того, что у события focus нет фазы всплытия, его нельзя просто так делегировать родительскому элементу. Есть два варианта решения этой проблемы.
Использование focusin
СкопированоМожно использовать вместо события focus — событие focusin. У него есть фаза всплытия и его можно делегировать родительскому элементу.
Обработка события в фазе всплытия
СкопированоТак же проблему делегирования получения фокуса можно решить передачей в метод add параметра { capture.
Обычно слушатель обрабатывает события на этапе всплытия, но, если установлен параметр { capture, будет обрабатывать событие раньше, на этапе захвата. Нужно иметь в виду, что в этом случае событие будет обработано родительским элементом раньше, чем целевым (дочерним), т.к. фаза захвата происходит до того, как событие достигает целевого элемента.
parentElement.addEventListener( 'focus', (event) => { console.log('Элемент получил фокус') }, { capture: true })// Эквивалентная запись:parentElement.addEventListener( 'focus', (event) => { console.log('Элемент получил фокус') }, true // тоже самое, что прописать { capture: true })
parentElement.addEventListener(
'focus',
(event) => {
console.log('Элемент получил фокус')
},
{ capture: true }
)
// Эквивалентная запись:
parentElement.addEventListener(
'focus',
(event) => {
console.log('Элемент получил фокус')
},
true // тоже самое, что прописать { capture: true }
)
Пример
СкопированоВ примере ниже одинаковый обработчик события, записывающий лог, вешается на две формы. В первой форме параметр { capture не установлен, во второй установлен.
<form class="form" id="form1"> <div class="form-row"> <label for="name1">Введите имя:</label> <input id="name1" type="text"> </div> <div class="form-row"> <label for="city1">Введите город:</label> <input id="city1" type="text"> </div></form>...<div class="logs"> <h4>📝 Последние 5 событий:</h4> <ol reversed id="logs"></ol></div>
<form class="form" id="form1">
<div class="form-row">
<label for="name1">Введите имя:</label>
<input id="name1" type="text">
</div>
<div class="form-row">
<label for="city1">Введите город:</label>
<input id="city1" type="text">
</div>
</form>
...
<div class="logs">
<h4>📝 Последние 5 событий:</h4>
<ol reversed id="logs"></ol>
</div>
const form1 = document.getElementById('form1')const form2 = document.getElementById('form2')const logs = document.getElementById('logs')const logEntries = [];function focusHandler(event) { const input = event.target; if (input.id) { const timestamp = new Date().toLocaleTimeString() const message = `${timestamp}: фокус на ${input.id}` logEntries.unshift(message) if (logEntries.length > 5) { logEntries.pop() } logs.innerHTML = logEntries.map((entry, index) => `<li>${entry}</li>` ).join('') }}form1.addEventListener('focus', focusHandler)form2.addEventListener('focus', focusHandler, { capture: true })
const form1 = document.getElementById('form1')
const form2 = document.getElementById('form2')
const logs = document.getElementById('logs')
const logEntries = [];
function focusHandler(event) {
const input = event.target;
if (input.id) {
const timestamp = new Date().toLocaleTimeString()
const message = `${timestamp}: фокус на ${input.id}`
logEntries.unshift(message)
if (logEntries.length > 5) {
logEntries.pop()
}
logs.innerHTML = logEntries.map((entry, index) =>
`<li>${entry}</li>`
).join('')
}
}
form1.addEventListener('focus', focusHandler)
form2.addEventListener('focus', focusHandler, { capture: true })