Tạo thành phần thông báo ngắn

Tổng quan cơ bản về cách tạo một thành phần thông báo ngắn thích ứng và dễ tiếp cận.

Trong bài đăng này, tôi muốn chia sẻ cách tạo thành phần thông báo ngắn. Dùng thử bản minh hoạ.

Bản minh hoạ

Nếu bạn thích xem video hơn, sau đây là phiên bản của bài đăng này trên YouTube:

Tổng quan

Thông báo ngắn là các thông báo ngắn không tương tác, thụ động và không đồng bộ cho người dùng. Nói chung, các API này được dùng làm mẫu phản hồi về giao diện để thông báo cho người dùng về kết quả của một hành động.

Lượt tương tác

Thông báo ngắn không giống như thông báo, cảnh báolời nhắc vì chúng không tương tác; chúng không bị loại bỏ hay tồn tại. Thông báo dùng cho thông tin quan trọng hơn, thông báo đồng bộ mà yêu cầu sự tương tác hoặc thông báo ở cấp hệ thống (thay vì thông báo ở cấp trang). Thông báo ngắn là cách thụ động hơn so với các chiến lược thông báo khác.

Markup (note: đây là tên ứng dụng)

Chiến lược phát hành đĩa đơn <output> là một lựa chọn phù hợp cho thông báo ngắn vì nó được thông báo trên màn hình độc giả. HTML đúng cung cấp cơ sở an toàn để chúng tôi nâng cao bằng JavaScript và CSS và sẽ có nhiều JavaScript.

Một thông báo ngắn

<output class="gui-toast">Item added to cart</output>

Có thể nhiều hơn dành cho tất cả mọi người bằng cách thêm role="status". Điều này mang lại dự phòng nếu trình duyệt không cung cấp ngầm ẩn cho các phần tử <output> vai trò theo thông số kỹ thuật.

<output role="status" class="gui-toast">Item added to cart</output>

Vùng chứa thông báo ngắn

Có thể hiển thị nhiều thông báo ngắn cùng một lúc. Để sắp xếp nhiều thông báo ngắn, một vùng chứa được sử dụng. Vùng chứa này cũng xử lý vị trí của thông báo ngắn trên màn hình.

<section class="gui-toast-group">
  <output role="status">Wizard Rose added to cart</output>
  <output role="status">Self Watering Pot added to cart</output>
</section>

Bố cục

Tôi chọn ghim thông báo ngắn vào inset-block-end của khung nhìn và nếu bạn thêm nhiều thông báo ngắn, chúng sẽ được xếp chồng từ cạnh màn hình đó.

Vùng chứa GUI

Vùng chứa thông báo ngắn sẽ thực hiện toàn bộ bố cục để trình bày thông báo ngắn. Bây giờ fixed cho khung nhìn và sử dụng thuộc tính logic inset để chỉ định các cạnh để ghim, cộng với một chút padding từ cùng một cạnh block-end.

.gui-toast-group {
  position: fixed;
  z-index: 1;
  inset-block-end: 0;
  inset-inline: 0;
  padding-block-end: 5vh;
}

Ảnh chụp màn hình cho thấy kích thước hộp và khoảng đệm của Công cụ cho nhà phát triển phủ trên phần tử .gui-toast-container.

Ngoài việc tự định vị trong khung nhìn, vùng chứa thông báo ngắn còn là một vùng chứa lưới có thể căn chỉnh và phân phối thông báo ngắn. Các mục được căn giữa dưới dạng nhóm với justify-content và từng được căn giữa bằng justify-items. Cho một chút gap để thông báo ngắn không chạm vào.

.gui-toast-group {
  display: grid;
  justify-items: center;
  justify-content: center;
  gap: 1vh;
}

Ảnh chụp màn hình cho thấy lớp phủ lưới CSS trên nhóm thông báo ngắn lần này
làm nổi bật khoảng trống và khoảng trống giữa các phần tử con của thông báo ngắn.

Chào mừng GUI (Giao diện người dùng đồ hoạ)

Một thông báo ngắn có một số padding, một số góc mềm hơn border-radius, và một hàm min() để hỗ trợ kích thước thiết bị di động và máy tính. Kích thước đáp ứng trong CSS sau ngăn thông báo ngắn mở rộng hơn 90% khung nhìn hoặc 25ch.

.gui-toast {
  max-inline-size: min(25ch, 90vw);
  padding-block: .5ch;
  padding-inline: 1ch;
  border-radius: 3px;
  font-size: 1rem;
}

Ảnh chụp màn hình một phần tử .gui-toast, có khoảng đệm và đường viền
bán kính hiển thị.

Kiểu

Sau khi thiết lập bố cục và vị trí, hãy thêm CSS giúp thích ứng với người dùng cài đặt và tương tác.

Dụng cụ đựng bánh mì

Các thông báo ngắn không mang tính tương tác, việc nhấn hay vuốt lên chúng không có tác dụng gì, nhưng chúng hiện sử dụng các sự kiện con trỏ. Ngăn không cho thông báo ngắn bị trộm với CSS sau.

.gui-toast-group {
  pointer-events: none;
}

Chào mừng GUI (Giao diện người dùng đồ hoạ)

Hiển thị cho thông báo ngắn về giao diện thích ứng sáng hoặc tối bằng các thuộc tính tuỳ chỉnh, HSL và truy vấn phương tiện tuỳ chọn.

.gui-toast {
  --_bg-lightness: 90%;

  color: black;
  background: hsl(0 0% var(--_bg-lightness) / 90%);
}

@media (prefers-color-scheme: dark) {
  .gui-toast {
    color: white;
    --_bg-lightness: 20%;
  }
}

Hoạt ảnh

Một thông báo ngắn mới sẽ hiển thị bằng ảnh động khi xuất hiện trên màn hình. Để điều chỉnh chuyển động giảm đi, hãy đặt giá trị translate thành 0 bằng cách mặc định, nhưng cập nhật giá trị chuyển động thành độ dài trong nội dung nghe nhìn ưu tiên chuyển động truy vấn . Mọi người đều thấy một số ảnh động, nhưng chỉ một số người dùng thấy thông báo ngắn di chuyển một khoảng cách.

Sau đây là các khung hình chính dùng cho ảnh động thông báo ngắn. CSS sẽ kiểm soát lối vào, thời gian chờ và thoát của thông báo ngắn, tất cả đều trong một ảnh động.

@keyframes fade-in {
  from { opacity: 0 }
}

@keyframes fade-out {
  to { opacity: 0 }
}

@keyframes slide-in {
  from { transform: translateY(var(--_travel-distance, 10px)) }
}

Sau đó, phần tử thông báo ngắn sẽ thiết lập các biến và sắp xếp các khung hình chính.

.gui-toast {
  --_duration: 3s;
  --_travel-distance: 0;

  will-change: transform;
  animation: 
    fade-in .3s ease,
    slide-in .3s ease,
    fade-out .3s ease var(--_duration);
}

@media (prefers-reduced-motion: no-preference) {
  .gui-toast {
    --_travel-distance: 5vh;
  }
}

JavaScript

Khi các kiểu và trình đọc màn hình đã sẵn sàng cho HTML, cần có JavaScript để phối hợp tạo, thêm và huỷ thông báo ngắn dựa trên người dùng sự kiện. Trải nghiệm của nhà phát triển đối với thành phần thông báo ngắn nên ở mức tối thiểu và dễ bắt đầu, chẳng hạn như:

import Toast from './toast.js'

Toast('My first toast')

Tạo nhóm thông báo ngắn và thông báo ngắn

Khi mô-đun thông báo ngắn tải từ JavaScript, mô-đun thông báo ngắn phải tạo một vùng chứa thông báo ngắn rồi thêm đoạn thông tin đó vào trang. Tôi đã chọn thêm phần tử trước body, thao tác này sẽ Ít có khả năng vấn đề xếp chồng z-index xảy ra vì vùng chứa nằm phía trên vùng chứa cho tất cả các thành phần nội dung.

const init = () => {
  const node = document.createElement('section')
  node.classList.add('gui-toast-group')

  document.firstElementChild.insertBefore(node, document.body)
  return node
}

Ảnh chụp màn hình nhóm thông báo ngắn giữa các thẻ head và nội dung.

Hàm init() được gọi nội bộ đến mô-đun, lưu trữ phần tử dưới tên Toaster:

const Toaster = init()

Việc tạo phần tử HTML cho thông báo nhanh được thực hiện bằng hàm createToast(). Chiến lược phát hành đĩa đơn hàm yêu cầu một số văn bản để hiển thị thông báo ngắn, tạo phần tử <output>, trang trí nó với một số lớp và thuộc tính, thiết lập văn bản và trả về nút.

const createToast = text => {
  const node = document.createElement('output')
  
  node.innerText = text
  node.classList.add('gui-toast')
  node.setAttribute('role', 'status')

  return node
}

Quản lý một hoặc nhiều thông báo ngắn

JavaScript hiện thêm một vùng chứa vào tài liệu để chứa thông báo ngắn và là sẵn sàng thêm các thông báo ngắn đã tạo. Hàm addToast() phối hợp xử lý một hàm hoặc nhiều thông báo ngắn. Trước tiên, hãy kiểm tra số lượng thông báo ngắn và xem liệu chuyển động có ổn không, sau đó sử dụng thông tin này để nối thêm thông báo ngắn hoặc làm thêm ảnh động để các thông báo ngắn khác xuất hiện để "tạo chỗ trống" để xem thông báo ngắn mới.

const addToast = toast => {
  const { matches:motionOK } = window.matchMedia(
    '(prefers-reduced-motion: no-preference)'
  )

  Toaster.children.length && motionOK
    ? flipToast(toast)
    : Toaster.appendChild(toast)
}

Khi thêm thông báo ngắn đầu tiên, Toaster.appendChild(toast) sẽ thêm một thông báo ngắn vào trang kích hoạt ảnh động CSS: tạo hiệu ứng động, chờ 3s, tạo hiệu ứng động. flipToast() được gọi khi đã có thông báo ngắn, sử dụng một kỹ thuật có tên là FLIP bởi Paul Lewis. Ý tưởng là tính toán mức chênh lệch ở các vị trí của vùng chứa, trước và sau khi thêm thông báo ngắn mới. Hãy nghĩ về điều này giống như việc đánh dấu vị trí hiện tại của Máy nướng bánh mì, vị trí của nó, sau đó tạo ảnh động từ vị trí ban đầu đến vị trí hiện tại.

const flipToast = toast => {
  // FIRST
  const first = Toaster.offsetHeight

  // add new child to change container size
  Toaster.appendChild(toast)

  // LAST
  const last = Toaster.offsetHeight

  // INVERT
  const invert = last - first

  // PLAY
  const animation = Toaster.animate([
    { transform: `translateY(${invert}px)` },
    { transform: 'translateY(0)' }
  ], {
    duration: 150,
    easing: 'ease-out',
  })
}

Lưới CSS thực hiện việc nâng bố cục. Khi thêm một thông báo ngắn mới, chế độ lưới sẽ đặt thông báo đó ở đầu và tạo dấu cách với các quảng cáo khác. Trong khi đó, web ảnh động là dùng để tạo ảnh động cho vùng chứa từ vị trí cũ.

Kết hợp tất cả JavaScript lại

Khi Toast('my first toast') được gọi, một thông báo ngắn sẽ được tạo và thêm vào trang (thậm chí có thể vùng chứa được tạo ảnh động để phù hợp với thông báo ngắn mới), một lời hứa sẽ được trả về và thông báo ngắn đã tạo là đã xem trong Hoàn thành hoạt ảnh CSS (ba hoạt ảnh khung hình chính) để có độ phân giải hứa hẹn.

const Toast = text => {
  let toast = createToast(text)
  addToast(toast)

  return new Promise(async (resolve, reject) => {
    await Promise.allSettled(
      toast.getAnimations().map(animation => 
        animation.finished
      )
    )
    Toaster.removeChild(toast)
    resolve() 
  })
}

Tôi cảm thấy phần khó hiểu của mã này là trong hàm Promise.allSettled()toast.getAnimations() liên kết. Vì tôi đã dùng nhiều ảnh động khung hình chính cho thông báo ngắn, để tự tin biết rằng tất cả tin nhắn đã hoàn tất, mỗi thông báo phải được yêu cầu từ JavaScript và mỗi finished hứa hẹn hoàn thành. allSettled điều đó có giúp ích cho chúng tôi không, tự giải quyết vấn đề một khi đã thực hiện tất cả lời hứa đã được thực hiện. Sử dụng await Promise.allSettled() có nghĩa là dòng tiếp theo của mã có thể tự tin xoá phần tử và giả định rằng thông báo ngắn đã hoàn tất vòng đời. Cuối cùng, việc gọi resolve() cũng thực hiện được hứa hẹn cấp cao của Toast. nhà phát triển có thể dọn dẹp hoặc làm công việc khác sau khi thông báo ngắn xuất hiện.

export default Toast

Cuối cùng, hàm Toast được xuất từ mô-đun còn các tập lệnh khác sẽ được xuất nhập và sử dụng.

Sử dụng thành phần Thông báo nhanh

Bạn có thể sử dụng thông báo ngắn hoặc trải nghiệm của nhà phát triển đối với thông báo ngắn này bằng cách nhập Toast và gọi hàm đó bằng một chuỗi thông báo.

import Toast from './toast.js'

Toast('Wizard Rose added to cart')

Nếu nhà phát triển muốn dọn dẹp công việc hoặc bất cứ việc gì sau khi thông báo ngắn xuất hiện hiển thị, chúng có thể sử dụng chế độ không đồng bộ chờ.

import Toast from './toast.js'

async function example() {
  await Toast('Wizard Rose added to cart')
  console.log('toast finished')
}

Kết luận

Giờ bạn đã biết cách tôi thực hiện điều đó, bạn sẽ làm cách nào‽ 🙂

Hãy đa dạng hoá phương pháp tiếp cận và tìm hiểu tất cả các cách xây dựng ứng dụng trên web. Tạo một bản minh hoạ, tweet cho tôi các đường liên kết và tôi sẽ thêm vào vào phần bản phối lại của cộng đồng dưới đây!

Bản phối lại của cộng đồng