Клавиша / esc

focus

Событие получения фокуса элементом

Время чтения: меньше 5 мин

Кратко

Скопировано

Событие 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="0":

  • <button>;
  • <input>;
  • <textarea>;
  • <select>;
  • <frame>;
  • <iframe>;
  • <object>;
  • <a> или <area> с атрибутом href;
  • <summary> в связке с <details>.

Как добавить событие на любой элемент

Скопировано

Чтобы focus работал на произвольном элементе (например, на <div>), добавьте ему атрибут tabindex="0" или атрибут contenteditable="true".

tabindex="0" указывает браузеру, что на элементе можно сфокусироваться (а, значит, и потерять фокус).

contenteditable="true" указывает браузеру, что элемент может редактироваться пользователем.

        
          
          <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. У него есть фаза всплытия и его можно делегировать родительскому элементу.

Обработка события в фазе всплытия

Скопировано

Так же проблему делегирования получения фокуса можно решить передачей в метод addEventListener параметра { capture: true }.

Обычно слушатель обрабатывает события на этапе всплытия, но, если установлен параметр { capture: true }, будет обрабатывать событие раньше, на этапе захвата. Нужно иметь в виду, что в этом случае событие будет обработано родительским элементом раньше, чем целевым (дочерним), т.к. фаза захвата происходит до того, как событие достигает целевого элемента.

        
          
          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: true } не установлен, во второй установлен.

        
          
          <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 })


        
        
          
        
      
Открыть демо в новой вкладке