Podstawowe informacje o tym, jak utworzyć adaptacyjny i dostępny komponent powiadomienia.
W tym poście chcę podzielić się przemyśleniami na temat tworzenia komponentu powiadomienia. Wypróbuj wersję demonstracyjną.
Jeśli wolisz film, obejrzyj tę wersję posta w YouTube:
Przegląd
Toasty to nieinteraktywne, pasywne i asynchroniczne krótkie wiadomości dla użytkowników. Zwykle służą one jako wzorzec informacji zwrotnych w interfejsie, który informuje użytkownika o wynikach działania.
Interakcje
Komunikaty typu toast różnią się od powiadomień, alertów i promptów, ponieważ nie są interaktywne – nie można ich odrzucić ani zachować. Powiadomienia służą do przekazywania ważniejszych informacji, wiadomości synchronicznych, które wymagają interakcji, lub wiadomości na poziomie systemu (w przeciwieństwie do wiadomości na poziomie strony). Wyskakujące okienka są mniej aktywne niż inne strategie powiadamiania.
Znacznik
Element
<output>
jest dobrym wyborem w przypadku wyskakujących powiadomień, ponieważ jest odczytywany przez czytniki ekranu. Prawidłowy kod HTML stanowi bezpieczną podstawę, którą możemy rozszerzyć za pomocą JavaScriptu i CSS. Będzie dużo JavaScriptu.
toast,
<output class="gui-toast">Item added to cart</output>
Możesz zwiększyć role="status"
. Zapewnia to rezerwę, jeśli przeglądarka nie przypisze elementom <output>
domyślnej roli zgodnie ze specyfikacją.
<output role="status" class="gui-toast">Item added to cart</output>
Pojemnik na tosty
Jednocześnie może być wyświetlanych więcej niż 1 komunikat. Do koordynowania wielu komunikatów typu toast służy kontener. Ten kontener obsługuje też pozycję wyskakujących okienek na ekranie.
<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>
Układy
Wybrałem przypinanie powiadomień do inset-block-end
obszaru widocznego i jeśli dodam więcej powiadomień, będą one ułożone w stos od tej krawędzi ekranu.
Kontener GUI
Kontener powiadomień wykonuje całą pracę związaną z układem, aby wyświetlać powiadomienia. Jest on fixed
do obszaru widoku i używa właściwości logicznej inset
, aby określić, do których krawędzi ma być przypięty, oraz niewielkiego padding
od tej samej krawędzi block-end
.
.gui-toast-group {
position: fixed;
z-index: 1;
inset-block-end: 0;
inset-inline: 0;
padding-block-end: 5vh;
}
Oprócz pozycjonowania w widocznym obszarze kontener powiadomień jest kontenerem siatki, który może wyrównywać i rozmieszczać powiadomienia. Elementy są wyśrodkowane jako grupa za pomocą justify-content
i indywidualnie za pomocą justify-items
.
Dodaj trochę gap
, aby tosty się nie stykały.
.gui-toast-group {
display: grid;
justify-items: center;
justify-content: center;
gap: 1vh;
}
Toast GUI
Pojedynczy toast ma pewne padding
, bardziej miękkie rogi z border-radius
i funkcję min()
, która pomaga w dostosowywaniu rozmiaru na urządzeniach mobilnych i komputerach. Elastyczny rozmiar w tym kodzie CSS zapobiega rozszerzaniu się wyskakujących okienek powyżej 90% widocznego obszaru lub 25ch
.
.gui-toast {
max-inline-size: min(25ch, 90vw);
padding-block: .5ch;
padding-inline: 1ch;
border-radius: 3px;
font-size: 1rem;
}
Style
Po ustawieniu układu i położenia dodaj CSS, który pomoże dostosować się do ustawień i interakcji użytkownika.
Pojemnik na tosty
Toast nie jest interaktywny – kliknięcie ani przesunięcie nie powoduje żadnej reakcji, ale obecnie przetwarza zdarzenia wskaźnika. Zapobiegaj przechwytywaniu kliknięć przez wyskakujące okienka za pomocą tego kodu CSS.
.gui-toast-group {
pointer-events: none;
}
Toast GUI
Nadaj powiadomieniom jasny lub ciemny motyw adaptacyjny z własnymi właściwościami, HSL i zapytaniem o preferencje dotyczące mediów.
.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%;
}
}
Animacja
Nowy komunikat powinien pojawić się na ekranie z animacją.
Domyślnie ustawia się wartości translate
na 0
, ale wartość ruchu można zaktualizować do długości w zapytaniu o media preferencji ruchu . Animacja jest wyświetlana wszystkim użytkownikom, ale tylko niektórzy z nich widzą, jak komunikat przesuwa się na pewną odległość.
Oto klatki kluczowe użyte w animacji powiadomienia. CSS będzie kontrolować pojawianie się, oczekiwanie i zamykanie powiadomienia w ramach jednej animacji.
@keyframes fade-in {
from { opacity: 0 }
}
@keyframes fade-out {
to { opacity: 0 }
}
@keyframes slide-in {
from { transform: translateY(var(--_travel-distance, 10px)) }
}
Element powiadomienia następnie konfiguruje zmienne i koordynuje klatki kluczowe.
.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
Gdy style i kod HTML dostępny dla czytników ekranu są gotowe, JavaScript jest potrzebny do tworzenia, dodawania i usuwania powiadomień na podstawie działań użytkownika. Korzystanie z komponentu powiadomienia powinno być proste i wygodne dla deweloperów, np. tak:
import Toast from './toast.js'
Toast('My first toast')
Tworzenie grupy powiadomień i powiadomień
Gdy moduł powiadomienia wczytuje się z JavaScriptu, musi utworzyć kontener powiadomienia i dodać go do strony. Element został dodany przed body
, co zmniejsza prawdopodobieństwo problemów z układaniem z-index
, ponieważ kontener znajduje się nad kontenerem wszystkich elementów treści.
const init = () => {
const node = document.createElement('section')
node.classList.add('gui-toast-group')
document.firstElementChild.insertBefore(node, document.body)
return node
}
Funkcja init()
jest wywoływana wewnętrznie w module, a element jest przechowywany jako Toaster
:
const Toaster = init()
Element HTML powiadomienia jest tworzony za pomocą funkcji createToast()
. Funkcja wymaga tekstu, który ma być wyświetlany w powiadomieniu, tworzy element <output>
, dodaje do niego klasy i atrybuty, ustawia tekst i zwraca węzeł.
const createToast = text => {
const node = document.createElement('output')
node.innerText = text
node.classList.add('gui-toast')
node.setAttribute('role', 'status')
return node
}
Zarządzanie jednym lub wieloma komunikatami
JavaScript dodaje teraz do dokumentu kontener, w którym będą wyświetlane powiadomienia, i jest gotowy do dodawania utworzonych powiadomień. Funkcja addToast()
koordynuje obsługę jednego lub wielu komunikatów. Najpierw sprawdza liczbę komunikatów i czy ruch jest w porządku, a potem wykorzystuje te informacje, aby dodać komunikat lub wykonać animację, dzięki której inne komunikaty „zrobią miejsce” na nowy.
const addToast = toast => {
const { matches:motionOK } = window.matchMedia(
'(prefers-reduced-motion: no-preference)'
)
Toaster.children.length && motionOK
? flipToast(toast)
: Toaster.appendChild(toast)
}
Podczas dodawania pierwszego powiadomienia Toaster.appendChild(toast)
dodaje powiadomienie do strony, wywołując animacje CSS: animacja wejścia, oczekiwanie 3s
, animacja wyjścia.
flipToast()
jest wywoływana, gdy istnieją już powiadomienia typu toast. Wykorzystuje technikę o nazwie FLIP, którą opisał Paul Lewis. Chodzi o obliczenie różnicy w położeniu kontenera przed dodaniem nowego powiadomienia i po jego dodaniu.
Wyobraź sobie, że zaznaczasz, gdzie jest teraz toster, gdzie będzie, a potem animujesz jego ruch z jednego miejsca do drugiego.
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',
})
}
Układ jest tworzony za pomocą siatki CSS. Gdy dodasz nowy toast, siatka umieści go na początku i rozmieści w odpowiedniej odległości od pozostałych. W tym czasie animacja internetowa jest używana do animowania kontenera z poprzedniej pozycji.
Łączenie wszystkich elementów JavaScript
Gdy wywoływana jest funkcja Toast('my first toast')
, tworzony jest komunikat, który jest dodawany do strony (może nawet kontener jest animowany, aby pomieścić nowy komunikat), zwracana jest obietnica, a utworzony komunikat jest obserwowany pod kątem zakończenia animacji CSS (3 animacji klatek kluczowych) w celu rozwiązania obietnicy.
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()
})
}
Uważam, że najbardziej mylące w tym kodzie są funkcja Promise.allSettled()
i mapowanie toast.getAnimations()
. Ponieważ w przypadku wyskakującego okienka użyłem kilku animacji klatek kluczowych, aby mieć pewność, że wszystkie się zakończyły, każda z nich musi zostać wywołana z JavaScriptu, a każda z nich musi mieć finished
obserwowane obietnice zakończenia.
allSettled
działa w ten sposób, że po spełnieniu wszystkich obietnic
zostaje uznana za zakończoną. Użycie await Promise.allSettled()
oznacza, że następny wiersz kodu może bezpiecznie usunąć element i założyć, że toast zakończył swój cykl życia. Wywołanie resolve()
spełnia obietnicę Toast na wysokim poziomie, dzięki czemu programiści mogą po wyświetleniu powiadomienia wyczyścić kod lub wykonać inne czynności.
export default Toast
Na koniec funkcja Toast
jest eksportowana z modułu, aby inne skrypty mogły ją importować i używać.
Korzystanie z komponentu Toast
Aby użyć powiadomienia lub jego funkcji dla deweloperów, zaimportuj funkcję
Toast
i wywołaj ją za pomocą ciągu tekstowego wiadomości.
import Toast from './toast.js'
Toast('Wizard Rose added to cart')
Jeśli deweloper chce wykonać jakieś czynności po wyświetleniu powiadomienia, może użyć funkcji asynchronicznej i await.
import Toast from './toast.js'
async function example() {
await Toast('Wizard Rose added to cart')
console.log('toast finished')
}
Podsumowanie
Teraz, gdy wiesz, jak to zrobiłem, jak Ty byś to zrobił? 🙂
Urozmaićmy nasze podejście i poznajmy wszystkie sposoby tworzenia treści w internecie. Utwórz demo, wyślij mi na Twitterze linki, a ja dodam je do sekcji remiksów społeczności poniżej.
Remiksy społeczności
- @_developit z HTML/CSS/JS: demo i kod
- Joost van der Schee z HTML/CSS/JS: demo i kod