Một phần tử nhận được tiêu điểm khi người dùng nhấp vào nó hoặc sử dụng phím Tab trên bàn phím. Cũng có thuộc tính HTML autofocus để tự động đặt tiêu điểm vào một phần tử khi trang được tải và các phương pháp khác để có được tiêu điểm.

Việc đặt tiêu điểm vào một phần tử thường có nghĩa là: “chuẩn bị để chấp nhận dữ liệu ở đây”, vì vậy đó là thời điểm chúng ta có thể chạy mã để khởi tạo chức năng cần thiết.

Khoảnh khắc mất tiêu điểm (“blur”) có thể còn quan trọng hơn. Đó là khi người dùng nhấp chuột ở nơi khác hoặc nhấn phím Tab để chuyển đến trường biểu mẫu tiếp theo, hoặc có các phương pháp khác.

Mất tiêu điểm thường có nghĩa là: “dữ liệu đã được nhập”, vì vậy chúng ta có thể chạy mã để kiểm tra hoặc thậm chí lưu nó lên máy chủ, v.v.

Có những đặc thù quan trọng khi làm việc với các sự kiện tiêu điểm. Chúng tôi sẽ cố gắng giải thích chúng kỹ càng hơn.

1. Các sự kiện focus/blur

Sự kiện focus được gọi khi phần tử nhận được tiêu điểm, và blur – khi phần tử mất tiêu điểm.
Hãy sử dụng chúng để xác thực trường nhập liệu.

Trong ví dụ dưới đây:

  • Bộ xử lý blur kiểm tra nếu trường nhập liệu có địa chỉ email và nếu không có – hiển thị lỗi.
  • Bộ xử lý focus ẩn thông báo lỗi (trên blur, nó sẽ được kiểm tra lại):
<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>

HTML hiện đại cho phép chúng ta thực hiện nhiều xác thực bằng cách sử dụng các thuộc tính đầu vào: required, pattern, v.v. Và đôi khi chúng là những gì chúng ta cần. JavaScript có thể được sử dụng khi chúng ta muốn linh hoạt hơn. Chúng ta cũng có thể tự động gửi giá trị đã thay đổi lên máy chủ nếu nó đúng.

2. Các phương thức focus/blur

Các phương thức elem.focus()elem.blur() đặt/ bỏ tiêu điểm trên phần tử.
Ví dụ, hãy làm cho người dùng không thể rời khỏi trường nhập liệu nếu giá trị không hợp lệ:

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

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

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

Nó hoạt động trên tất cả các trình duyệt ngoại trừ Firefox (bug).

Nếu chúng ta nhập gì đó vào ô input và sau đó nhấn Tab hoặc nhấp ra ngoài ô <input>, thì sự kiện onblur sẽ đưa tiêu điểm quay lại.

Lưu ý rằng chúng ta không thể “ngăn việc mất tiêu điểm” bằng cách gọi event.preventDefault() trong onblur, vì onblur hoạt động sau khi phần tử đã mất tiêu điểm.

Trên thực tế, cần cân nhắc kỹ trước khi triển khai điều này, vì chúng ta nên hiển thị lỗi cho người dùng mà không ngăn cản họ tiếp tục điền vào biểu mẫu. Họ có thể muốn điền vào các trường khác trước.

Mất tiêu điểm có thể xảy ra vì nhiều lý do.

Một trong số đó là khi người dùng nhấp chuột ở nơi khác. Nhưng cũng có thể do chính JavaScript gây ra, ví dụ:

  • Một alert di chuyển tiêu điểm đến chính nó, vì vậy nó gây ra mất tiêu điểm ở phần tử (blur event), và khi alert bị tắt, tiêu điểm trở lại (focus event).
  • Nếu một phần tử bị loại bỏ khỏi DOM, thì nó cũng gây ra mất tiêu điểm. Nếu nó được chèn lại sau đó, thì tiêu điểm không trở lại.

Những tính năng này đôi khi khiến các trình xử lý focus/blur hoạt động không như mong muốn — kích hoạt khi không cần thiết.
Công thức tốt nhất là phải cẩn thận khi sử dụng các sự kiện này. Nếu chúng ta muốn theo dõi sự mất tiêu điểm do người dùng khởi tạo, thì nên tránh gây ra nó bằng chính JavaScript của mình.

3. Cho phép đặt tiêu điểm trên bất kỳ phần tử nào: tabindex

Mặc định, nhiều phần tử không hỗ trợ lấy tiêu điểm (focus).

Danh sách này có sự khác biệt giữa các trình duyệt, nhưng có một điểm chung là các phần tử mà người dùng có thể tương tác như <button>, <input>, <select>, <a> luôn hỗ trợ focus/blur.

Ngược lại, các phần tử dùng để định dạng nội dung như <div>, <span>, <table> – mặc định không thể lấy tiêu điểm. Phương thức elem.focus() không hoạt động trên chúng, và các sự kiện focus/blur không bao giờ được kích hoạt.

Điều này có thể thay đổi bằng cách sử dụng thuộc tính HTML tabindex.

Bất kỳ phần tử nào đều có thể trở nên có thể lấy tiêu điểm nếu có tabindex. Giá trị của thuộc tính là thứ tự của phần tử khi dùng phím Tab (hoặc tương tự) để di chuyển qua lại giữa chúng.

Nghĩa là: nếu chúng ta có hai phần tử, phần tử đầu tiên có tabindex="1", và phần tử thứ hai có tabindex="2", thì khi nhấn Tab trong phần tử đầu tiên, tiêu điểm sẽ chuyển đến phần tử thứ hai.

Thứ tự chuyển là: các phần tử có tabindex từ 1 trở lên sẽ đi trước (theo thứ tự tabindex), sau đó là các phần tử không có tabindex (như một <input> thông thường).

Các phần tử không có tabindex sẽ được chuyển đổi theo thứ tự xuất hiện trong mã nguồn (thứ tự mặc định).

Có hai giá trị đặc biệt:

  • tabindex="0" đưa phần tử vào nhóm các phần tử không có tabindex. Nghĩa là khi chuyển đổi giữa các phần tử, các phần tử với tabindex=0 sẽ xếp sau các phần tử có tabindex ≥ 1.
  • Thường được sử dụng để làm cho một phần tử có thể lấy tiêu điểm, nhưng vẫn giữ thứ tự chuyển đổi mặc định. Điều này giúp đưa phần tử vào nhóm cùng với các phần tử như <input> trong form.
  • tabindex="-1" chỉ cho phép phần tử được lấy tiêu điểm qua lập trình. Phím Tab sẽ bỏ qua các phần tử này, nhưng phương thức elem.focus() vẫn hoạt động.

Ví dụ, đây là một danh sách. Hãy nhấp vào mục đầu tiên và nhấn Tab:


Click the first item and press Tab. Keep track of the order. Please note that many subsequent Tabs can move the focus out of the iframe in the example.
<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>

Thứ tự sẽ như sau: 1 - 2 - 0. Thông thường, <li> không hỗ trợ lấy tiêu điểm, nhưng với tabindex, nó có thể được kích hoạt hoàn toàn để lấy tiêu điểm, kèm theo các sự kiện và khả năng styling với :focus.

4. Phân quyền: focusin/focusout

Các sự kiện focusblur không nổi bọt.
Ví dụ, chúng ta không thể đặt onfocus trên <form>để làm nổi bật nó, như sau:


<form *!*onfocus="this.className='focused'"*/!*>
  <input type="text" name="name" value="Name">
  <input type="text" name="surname" value="Surname">
</form>

<style> .focused { outline: 1px solid red; } </style>

Ví dụ trên không hoạt động vì khi người dùng tập trung vào một <input>, sự kiện focus chỉ kích hoạt trên chính input đó và không lan truyền lên. Do đó, form.onfocus sẽ không bao giờ được kích hoạt.

Có hai cách giải quyết.

Thứ nhất, có một tính năng lịch sử thú vị: focus/blur không lan truyền lên, nhưng lại lan truyền xuống trong giai đoạn bắt sự kiện.

Điều này sẽ hoạt động:


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

Thứ hai, có các sự kiện focusinfocusout — hoàn toàn tương tự như focus/blur, nhưng chúng nổi bọt.
Lưu ý rằng chúng phải được gán bằng elem.addEventListener, không phải on.

Đây là một biến thể hoạt động khác:


<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>
*!*
  form.addEventListener("focusin", () => form.classList.add('focused'));
  form.addEventListener("focusout", () => form.classList.remove('focused'));
*/!*
</script>

5. Tóm tắt

Các sự kiện focusblur kích hoạt khi một phần tử nhận hoặc mất tiêu điểm.
Các điểm đặc biệt của chúng:

  • Chúng không nổi bọt. Có thể sử dụng trạng thái capturing hoặc focusin/focusout.
  • Hầu hết các phần tử không hỗ trợ tiêu điểm theo mặc định. Sử dụng tabindex để làm bất kỳ phần tử nào có thể nhận tiêu điểm.

Phần tử hiện tại đang có tiêu điểm có sẵn dưới dạng document.activeElement.

Tại Cafedev, chúng tôi đã cùng bạn khám phá cách sử dụng các sự kiện `focus` và `blur` để tối ưu hóa trải nghiệm người dùng trong JavaScript. Những kỹ thuật này không chỉ giúp bạn quản lý trạng thái của các phần tử trên giao diện mà còn nâng cao tính tương tác và hiệu suất của ứng dụng web. Đừng quên quay lại Cafedev để tiếp tục hành trình học JavaScript từ cơ bản đến nâng cao với các chủ đề thú vị khác!

Tham khảo thêm: MIỄN PHÍ 100% | Series tự học Javascrypt chi tiết, dễ hiểu từ cơ bản tới nâng cao + Full Bài Tập thực hành nâng cao trình dev

Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!