Trong bài hướng dẫn hôm nay, chúng ta sẽ tìm hiểu cách xây dựng thành phần ẩn/hiện với CSS và một chút JavaScript. Mặc định, jQuery cung cấp phương thức slideToggle cho phép chúng ta tạo ra các accordion với chuyển động trượt. Thách thức của chúng ta là bắt chước chức năng này với JavaScript thuần túy.

See the Pen How to Build an Accordion Component With CSS and a Touch of JavaScript by Envato Tuts+ (@tutsplus) on CodePen.

Lưu ý: tính năng này được cung cấp bởi các phần tử gốc <details> và <summary>, tuy vậy những phần tử này vẫn chưa được hỗ trợ trong các trình duyệt của Microsoft.

Để bắt đầu, chúng ta định nghĩa một phần tử với lớp container chứa hai phần tử con:

  • button sẽ ẩn và hiển thị nội dung (bao gồm biểu tượng SVG nội tuyến)
  • nội dung thật sự

Nó sẽ tương tự như dưới đây:

<div class="container">
  <!-- other content here -->
 
  <button class="toggle-btn">
    <span class="more show">
      Show More info
      <svg width="24" height="24" viewBox="0 0 24 24">
        <path d="M0 7.33l2.829-2.83 9.175 9.339 9.167-9.339 2.829 2.83-11.996 12.17z"/>
      </svg>
    </span>
    <span class="less hide">
      Show Less info
      <svg width="24" height="24" viewBox="0 0 24 24">
        <path d="M0 16.67l2.829 2.83 9.175-9.339 9.167 9.339 2.829-2.83-11.996-12.17z"/>
      </svg>
    </span>
  </button>
 
  <div class="info">
    <!-- content here -->
  </div>
 
  <!-- other content here -->
</div>

CSS thì khá đơn giản. Chúng ta cần định nghĩa hai lớp trợ giúp (ví dụ: hide và show).

Để ẩn và hiện phần tử mục tiêu, chúng ta sẽ sử dụng thuộc tính height, nhưng chúng ta sẽ không chỉ định giá trị của nó trong CSS. Thay vào đó, chúng ta sẽ linh hoạt thiết lập giá trị độ cao thông qua JavaScript.

Một điều cần lưu ý là chúng ta không sử dụng thuộc tính display để chuyển đổi chế độ hiển thị của nội dung. Thuộc tính đó không nằm trong số các thuộc tính CSS có thể hoạt hình.

Dưới đây là CSS tương ứng:

.hide {
  display: none;
}
 
.show {
  display: flex;
}
 
.info {
  overflow: hidden;
  transition: height .5s;
}

Bây giờ là lúc để thảo luận về code JavaScript của chúng ta sẽ hoạt động như thế nào.

Đầu tiên, ngay sau khi DOM đã sẵn sàng, chúng ta lấy chiều cao của phần tử .info và sau đó ngay lập tức thiết lập giá trị của nó thành 0.

const info = document.querySelector(".info");
 
let infoHeight = info.offsetHeight;
info.style.height = 0;

Hãy nhớ rằng tùy thuộc vào nội dung của bạn (ví dụ, nếu nội dung đó bao gồm cả hình ảnh), bạn có thể phải kẹp đoạn mã bên trên bên trong sự kiện load.

Tiếp theo, khi button .toggle-btn được nhấp, chúng ta thực hiện các công việc sau:

  • Chuyển đổi chế độ hiển thị của các phần tử .less và .more.
  • Tính toán lại chiều cao của phần tử .info. Nếu nó là 0 (giá trị thiết lúc đầu), điều đó có nghĩa là nội dung đang ẩn, vì thế chúng ta hiện nó ra bằng cách thiết lập chiều cao thành chiều cao ban đầu (được lưu trong biến infoHeight). Mặt khác, nếu nội dung đang được hiển thị, chúng ta ẩn nó bằng cách thiết lập chiều cao của nó là 0.
  • Không bắt buộc, chúng ta đảm bảo rằng .toggle-btn sẽ chỉ được nhấp một lần cho đến khi hiệu ứng động kết thúc (nó kéo dài 500 mili giây).

Dưới đây là code cài đặt tất cả hành vi đó:

const toggleBtn = document.querySelector(".toggle-btn");
const info = document.querySelector(".info");
const less = document.querySelector(".less");
const more = document.querySelector(".more");
 
// initial height
let infoHeight = info.offsetHeight;
 
toggleBtn.addEventListener("click", function() {
  this.disabled = true;
   
  more.classList.toggle("show");
  more.classList.toggle("hide");
  less.classList.toggle("show");
  less.classList.toggle("hide");
   
  const infoNewHeight = info.offsetHeight;
  if(infoNewHeight == 0) {
    info.style.height = `${infoHeight}px`;
  } else {
    info.style.height = 0;
  }
   
  setTimeout(() => {
    this.disabled = false;
  }, 500);  
});

Bước tiếp theo là đảm bảo rằng thành phần sẽ hoạt động chính xác khi cửa sổ trình duyệt được thay đổi kích thước.

Đây là code JS cần thiết cho điều đó:

// variable definitions here
 
window.addEventListener("resize", () => {
  info.style.removeProperty("height");
  infoHeight = info.offsetHeight;
  if(more.classList.contains("hide")) {
    info.style.height = `${infoHeight}px`;
  } else {
    info.style.height = 0;
  }
});

Bản demo của chúng ta sẽ hoạt động tốt trong tất cả các trình duyệt và thiết bị mới. Ngoài ra, như thường lệ, chúng ta sử dụng Babel để biên dịch mã ES6 xuống ES5.

Trong bài hướng dẫn ngắn này, chúng ta đã xây dựng một thành phần ẩn/hiện theo kiểu accordion với CSS và JavaScript. Nhờ vào thuộc tính height có thể hoạt hình, chúng ta đã thêm hiệu ứng trượt-vào/trượt-ra vào thành phần của chúng ta.

Lưu ý, chúng ta vẫn chưa cân nhắc khả năng truy cập, vì vậy nếu bạn muốn nâng cao chức năng của nó thì đó chắc chắn có thể là bước tiếp theo.