Focusing : focus/blur

An element receives a focus when the user either clicks on it or uses the Tab key on the keyboard. There’s also an autofocus HTML attribute that puts the focus into an element by default when a page loads and other means of getting a focus.

Focusing generally means: “prepare to accept the data here”, so that’s the moment when we can run the code to initialize or load something.

The moment of losing the focus (“blur”) can be even more important. That’s when a user clicks somewhere else or presses Tab to go to the next form field, or there are other means as well.

Losing the focus generally means: “the data has been entered”, so we can run the code to check it or even to save it to the server and so on.

Events : focus/blur

The focus event is called on focusing, and blur – when the element loses the focus.

Let’s use them for validation of an input field. In the example below:

<style>
  .invalid { border-color: red; }
  #error { color: red }
</style>

Your email please: <input type="email" id="input">

<div id="error"></div>

<script>
input.onblur = function() {
  if (!input.value.includes('@')) { // not email
    input.classList.add('invalid');
    error.innerHTML = 'Please enter a correct email.'
  }
};

input.onfocus = function() {
  if (this.classList.contains('invalid')) {
    // remove the "error" indication, because the user wants to re-enter something
    this.classList.remove('invalid');
    error.innerHTML = "";
  }
};
</script>
Your email please:

Methods : focus/blur

Methods elem.focus() and elem.blur() set/unset the focus on the element. For instance, let’s make the visitor unable to leave the input if the value is invalid:

<style>
  .errorBackground {
    background: red;
  }
</style>

Your email please: <input id="demo1First" type="email" id="input">
<input id="demo1Second" type="text" style="width:220px" placeholder="make email invalid and try to focus here">

<script>
  demo1First.onblur = function() {
    if (!this.value.includes('@')) { // not email
      // show the error
      this.classList.add("errorBackground");
      // ...and put the focus back
      demo1First.focus();
    } else {
      this.classList.remove("errorBackground");
    }
  };
</script>
Your email please:

It works in all browsers except Firefox (bug). If we enter something into the input and then try to use Tab or click away from the <input>, then onblur returns the focus back.

Please note that we can’t “prevent losing focus” by calling event.preventDefault() in onblur, because onblur works after the element lost the focus.

Coution

When using focus and blur events. Few things need be to remembered. A focus loss can happen for many other reasons like -

These features sometimes cause focus/blur handlers to misbehave – to trigger when they are not needed.

The best recipe is to be careful when using these events. If we want to track user-initiated focus-loss, then we should evade causing it by ourselves.

Allow focusing on any element: tabindex

By default many elements do not support focusing. For example, in most browser elements like input, checkbox, radio button, textarea, select etc supports focus. But most element like div, span, article, url, ol, table, tr, td, th etc doesn't support focus. The method elem.focus() doesn’t work on them, and focus/blur events are never triggered. But you can enable focus for these element by specifying tabindex HTML attribute. There are two special values -

Any element supports focusing if it has tabindex.

Here is a demo -

Click the first item and press Tab. Keep track of the order.
<ul>
  <li tabindex="1">One</li>
  <li tabindex="0">Zero</li>
  <li tabindex="2">Two</li>
  <li tabindex="-1">Minus one</li>
</ul>

<style>
  li { cursor: pointer; }
  :focus { outline: 1px dashed green; }
</style>
Click the first item and press Tab. Keep track of the order.

Normally, <li> does not support focusing, but tabindex full enables it, along with events and styling with :focus.

elem.tabIndex works too

We can add tabindex from JavaScript by using the elem.tabIndex property. That has the same effect.

Delegation: focusin/focusout

Events focus and blur do not bubble.

For instance, we can’t put onfocus on the <form> to highlight it, like this:

Click the first item and press Tab. Keep track of the order.
<ul class="demo1">
  <li tabindex="1">One</li>
  <li tabindex="0">Zero</li>
  <li tabindex="2">Two</li>
  <li tabindex="-1">Minus one</li>
</ul>

<style>
  .demo1 li { cursor: pointer; }
  .demo1 li:focus { outline: 1px dashed green; }
</style>

The example above doesn’t work, because when user focuses on an <input>, the focus event triggers on that input only. It doesn’t bubble up. So form.onfocus never triggers.

There are two solution -

First, there’s a funny historical feature: focus/blur do not bubble up, but propagate down on the capturing phase.

<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>
  // put the handler on capturing phase (last argument true)
  form.addEventListener("focus", () => form.classList.add('focused'), true);
  form.addEventListener("blur", () => form.classList.remove('focused'), true);
</script>

Second, there are focusin and focusout events – exactly the same as focus/blur, but they bubble.

Note that they must be assigned using elem.addEventListener, not on<event>.

<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>
  // put the handler on capturing phase (last argument true)
  form.addEventListener("focusin", () => form.classList.add('focused'));
  form.addEventListener("focusout", () => form.classList.remove('focused'));
</script>

document.activeElement

The property activeElement on document object contains the element which has the current focus. As soon as the focus is lost, the activeElement is reset.

Here is a demo

<form id="demo4" name="demo4">
	<input type="text" name="inputOne" value="One" onfocus="console.log(document.activeElement.value)">
	<input type="text" name="inputTwo" value="Two" onfocus="console.log(document.activeElement.value)">
</form>

But this property is rarely used in application, because you can always get the element from the event object of focus event.