HTML5 + CSS3 + Javascript: Xây dựng select field responsive

Rate this post

Việc xây dựng các mục chọn Select (Select Field) đẹp về hình thức và hoạt động tốt cả về tính năng là một bài toán rất cơ bản dành cho các lập trình viên khi bắt đầu vào lập trình Frontend. Code Tốt xin hướng dẫn cách xây dựng một tính năng hoàn thiện và hoạt động tương thích đúng chuẩn responsive.

Các bài viết của chuyên mục Full-stack Frontend sẽ yêu cầu bạn nắm bắt và tự thực hành kiến thức, nên bạn cần lưu ý thực hiện đúng và đủ các nội dung bằng gợi ý từ Code Tốt. Việc copy code không giúp bạn trở thành pro đâu!!!

Mục đích của Custom Select Field Responsive

Các bạn hẳn đã biết khi nhấp vào một mục chọn và xổ ra danh sách, trông nó sẽ như thế này:

Tuy nhiên, nhiều website bạn sẽ thấy select field trông rất đẹp như thế này:

Bài toán sẽ phức tạp hơn:

Vậy bằng cách nào bạn thực hiện được yêu cầu sau?

  • Trên màn hình mobile: Hiển thị như desktop, nhưng khi click vào select option thì hiện ra giống như select của trình duyệt.
  • Trên màn hình tablet trở lên: Hiển thị như desktop, nhưng khi click vào select option thì hiện ra custom select field.
  • Sau khi click bất kể trên mobile hay desktop, nội dung được select từ danh sách sẽ hiển thị.

Câu trả lời đó chính là bài tập xây dựng Custom Select Field mà hôm nay Code Tốt sẽ chia sẻ. Do đây là một bài viết theo hướng training, kết quả code sẽ không được công bố ở cuối bài. Nhiệm vụ của bạn là hoàn thành theo hướng dẫn trên nhé!

Cũng bởi đây là bài viết tốn khá nhiều công sức của Code Tốt, các bạn hãy bỏ chút thời gian like dạo để nhiều người khác biết đến các bài viết hay của Code Tốt hơn nhé. Đừng chơi trick để xem nội dung bài viết, không hay đâu!!!

[sociallocker]

Chuẩn bị

Để xây dựng được tính năng Custom Select Field, bạn cần:

  • HTML5 để dựng markup
  • CSS3 để dựng layout và style
  • Javascript để xác định và đồng bộ dữ liệu hiển thị tới người dùng

Bắt đầu

Xác định các đối tượng chính trong markup (HTML5 + CSS3)

Đầu tiên, ta phải nắm rõ các thành phần sẽ xuất hiện và tương tác với nhau.

Trên mobile: Ta cần 1 <select> field hoạt động như bình thường.

Trên desktop: Ta cần 1 <ul> chứa các dữ liệu hệt như trên mobile.

Quan trọng hơn, ta cần 1 <div> giả lập giúp hiển thị dữ liệu đã chọn bất kể là trên mobile hay desktop.

Tiếp theo, để responsive thì cần ẩn hiện các element này tương ứng các breakpoint bằng @media queries.

Trạng thái hoạt động (Javascript)

Sự xuất hiện và thay thế trên màn hình khi người dùng click chuột vào select này sẽ được sync bởi Javascript. Khi click vào select, ta tìm giá trị của select field, từ đó tìm ra <option> nào đang chứa value hiện tại, tiếp theo ta lấy hai giá trị value và innerHTML của option để tìm <li> tương ứng và bật class active cho item này (thì như vậy trên desktop sẽ xuất hiện trạng thái tương đương sau khi resize window). Thêm nữa, để giá trị hiển thị thì lấy innerHTML của <option> để hiển thị trong <div> chứa markup hiện tại.

Mô phỏng Markup Select Field ban đầu (HTML5)

<div class="select-field">
  <!-- Chứa "text" hiện tại" hoặc placeholder ban đầu -->
  <div class="select-field__current"></div>
  <!-- Hiển thị chỉ trên mobile -->
  <div class="select-field__mobile">
    <select></select>
  </div>
  <!-- Hiển thị chỉ trên desktop -->
  <div class="select-field__desktop">
    <ul></ul>
  </div>
</div>

(Các class được xác định theo BEM Class, các bạn tự tìm hiểu thêm ở đây).

Xác định các thành phần ẩn hiện trong từng breakpoint (SCSS)

Code bằng SCSS, các bạn nào chưa quen có thể paste vào codepen rồi chọn compile code để hiện ra CSS nhé.

$mobile-breakpoint: 480px;
.fade-out {
  opacity: 0;
  visibility: hidden;
}
.fade-in {
  opacity: 1;
  visibility: visible
}

.select-field__mobile {
  display: block;
  @media (min-width: $mobile-breakpoint) {
   display: none;
  }
}

.select-field__desktop {
  @extend .fade-out; // Ẩn ngay cả khi trên desktop và chỉ hiện ra nếu có class 'active'
  display: none;
  @media (min-width: $mobile-breakpoint) {
    display: block;
  }
}

Lý do của việc dùng opacity: 0 là để tạo hiệu ứng mượt hơn khi hiện dropdown menu ra trên desktop.

Triển khai style cho các đối tượng (SCSS)

Element current luôn hiển thị

Trên mobile, để <select> hoạt động trong khi .select-field__current vẫn hiển thị, ta cần set pointer-events: none cho select-field__current.

.select-field__current {
  pointer-events: none; // Để click xuyên qua div này vào được .select-field__mobile
  cursor: pointer; // Để người dùng biết là có thể click
  @media (min-width: $mobile-breakpoint) {
    pointer-events: visible;
  }
}

Mobile Select

Đối với field Mobile, ta cần đảm bảo nó nằm chồng lên markup current.

.select-field__mobile {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 45px;
  select {
    width: 100%;
    height: 45px;
  }
}

Dropdown trên Desktop

Với dropdown trên Desktop, ta cần giả lập cách nó hiển thị (thông thường sẽ nằm dưới dropdown như một menu):

.select-field__desktop {
  position: absolute;
  top: 45px;
  left: 0;
  width: 100%;
  max-height: 0; // Dùng transition max-height sẽ không bị giật
  transition: all .3s ease;
  // Trạng thái khi click vào field current và hiện ra trên desktop
  .dropdown-active & {
    max-height: 9999px;
  }
  ul {
    width: 100%;
    background: #000;
  }
  ul li {
    padding: 10px;
    color: rgba(#fff, 0.8);
    transition: all 0.3s ease; // Animation mượt hơn
    &:hover {
      color: #fff;
    }
    &.active {
      color: #fff;
    }
  }
}

Triển khai hoạt động đồng bộ dữ liệu với Javascript

Sau khi dựng markup, ta cần xem cách thức dữ liệu được đồng bộ như thế nào. Mô phỏng trạng thái có thể hiểu như sau:

Ta sẽ tách ra các function hoạt động khác nhau trên mobile và desktop. Function làm việc trên mobile sẽ làm việc với select.value . Nhóm function desktop lại chia ra làm 2 tình huống: làm việc với li trong dropdown và làm việc với __desktop khi click vào __current

Toàn bộ phần code sẽ sử dụng ES5 Javascript không cần thư viện nào cả nhé. Hãy bắt đầu!

Xác định các object ta sẽ sử dụng để tương tác

Đầu tiên, bạn nên gắn các JS-class, ám chỉ việc sử dụng các class chỉ để gọi trong Javascript như markup thay đổi sau:

var el = document.querySelector('.select-field');
var select = el.querySelector('select');
var items = Array.prototype.slice.call(el.querySelectorAll('li'));
var current = el.querySelector('.js-current');

Nếu bạn nào chưa rõ tại sao dùng Array.prototype.slice.call thì thực chất đây là 1 protype giúp object bên trong có thể được dùng với forEach. Nếu không dùng thì bạn mở Safari lên sẽ thấy báo lỗi đấy nhé!

Tiếp theo,bạn xác định các class Active sẽ add vào dropdown và item trong dropdown (tiện thay đổi về sau nếu cần đỡ phải lò dò đi tìm):

var dropdownActiveClass = 'dropdown-active';
var dropdownItemActiveClass = 'item-active';

Mô phỏng trạng thái TRUYỀN DỮ LIỆU trên mobile

Bước tiếp theo, ta tìm logic để xây dựng việc truyền dữ liệu từ select mobile lên current field hiển thị và active item <li> tương ứng:

function selectOnMobile() {
  select.addEventListener('click', function() {
    // Ta cần tìm "value" và "text" là đủ
    var currentValue = this.value;
    var matchingItem = document.querySelector('[data-value="' + currentValue + '"]');
    var currentText = matchingItem.innerHTML;
    // Giờ là lúc thay đổi markup của current field
    current.innerHTML = currentText;
    // Để tránh bug, ta cần tìm các item khác đang active và remove class active đi đã
    items.forEach(function( item ) {
      item.classList.remove(dropdownItemActiveClass);
    });
    // Sau đó mới add active state vào item trên desktop
    matchingItem.classList.add(dropdownItemActiveClass);
  });
}

Bạn thấy có khó không? Thực ra không quá khó phải không???

Mô phỏng trạng thái active của Dropdown trên khung màn hình Desktop

Giờ thì ta cần làm việc để khi click vào __current thì bật lên dropdown  mà chúng ta đã dựng markup. Nên set 1 class dropdownActiveClass cho nó tắt/bật ngang hàng với .select-field là đơn giản nhất.

function toggleDropdown() {
  current.addEventListener('click', function() {
    el.classList.toggle(dropdownActiveClass);
  });
}

Còn chuyện xử lý style hiển thị hãy để CSS lo. Xem lại mục style cho Dropdown nhé.

Đồng bộ dữ liệu sau khi click vào item desktop

Chuyện vẫn chưa kết thúc đâu. Giờ ta cần xác định khi click vào <li> thì chuyện gì sẽ xảy ra? Cách tốt nhất là ta nên kết hợp sử dụng data-attribute trong tình huống này. Markup sẽ có dạng như sau:

<ul>
  <li data-value="thoi-trang">Thời trang</li>
  <li data-value="nha-cua">Nhà cửa</li>
</ul>

Lý do là các giá trị value sẽ dễ tìm và không chứa các kí tự lạ thì khi set giá trị select.value của mobile sẽ rất dễ.

Giờ ta bắt đầu viết function để hoạt động khi click vào 1 item <li> nhé:

function clickOnDesktop() {
  items.forEach( item ) {
    item.addEventListener('click', function() {
      // Đi tìm các giá trị cần thiết là text và value
      var currentText = item.innerHTML;
      var currentValue = item.getAttribute('data-value');
      // Giờ ta cần làm vài việc.
      // Nếu item vừa click là item active thì bỏ qua không cần làm gì cả
      if( !this.hasClass(dropdownItemActiveClass) ) {
        // Remove class active nếu có của các item khác. 
        items.forEach(
          function( item ) {
            item.classList.remove(dropdownItemActiveClass);
          }
        );
        this.classList.add(dropdownItemActiveClass)
        // Add text vào field current
        current.innerHTML =currentText;
        // Set giá trị cho <select>, thì khi resize window mở ra trên mobile sẽ thấy đang select đúng item rồi.
        select.value = currentValue;
      }
    });
  }
}

Code trên hoàn toàn là đang viết theo logic mà chưa có kiểm thử nên bạn lưu ý là làm lần lượt các bước theo như Code Tốt hướng dẫn nhé. Nếu có gì đó sai, bạn có thể comment trong bài viết này.

Bài tập dành cho bạn

Nếu các function trên đã hoạt động, bạn có thể viết 1 function để khi load web thì sẽ tự động lựa chọn giá trị đầu tiên hiển thị cho người dùng không?

Các bạn nào comment link codepen các bạn đã làm thành công sẽ nhận được ngay giao diện WordPress trả phí Total (trị giá $60.00) được chia sẻ miễn phí để bạn làm website cực dễ dàng nhé!

[/sociallocker]

Viết một bình luận


Chuyên gia về Web
Bạn muốn làm việc với dịch vụ website do chúng tôi triển khai?
Gọi tư vấn 0982.90.4343
Chuyên gia về Web
Bài viết liên quan

02/01/2024

Fix lỗi npm không thể cài các package devDependencies
Khi cài đặt dự án,  có lúc bạn sẽ cài mãi cũng không đủ các package npm, đặc biệt là...
Gọi file PHP trong WordPress

19/09/2023

Cài đặt và sử dụng WP-CLI trên môi trường Linux
Trên một số môi trường Hosting có thể cung cấp SSH hoặc Terminal access, song không có sẵn WP-CLI để...
Import database MySQL lớn trên môi trường Docker

06/08/2023

Import database MySQL lớn trên môi trường Docker
Mình sử dụng EasyPanel để quản lý các Docker và build môi trường app. Nay gặp tình huống phải import...

28/01/2023

Hướng dẫn cài đặt php extension mongodb trên Mac OS M1
Khi bạn muốn hỗ trợ MongoDB trên dòng Mac M1, phần cài đặt sẽ cần một chút lưu ý. Hướng...