Podstawowe informacje o tworzeniu mini i megamodalnych mini i megamodalnych reklam adaptacyjnych kolorystycznych za pomocą elementu <dialog>
.
W tym poście chcę opowiedzieć o tym, jak stworzyć
elastyczne, łatwo dostępne mini i megamodalne panele z elementem <dialog>
.
Wypróbuj wersję demonstracyjną i zobacz
!
Jeśli wolisz film, oto wersja tego posta w YouTube:
Omówienie
<dialog>
doskonale nadaje się do przekazywania informacji kontekstowych lub dotyczących działań w treści strony. Zastanów się, kiedy
użytkownicy mogą skorzystać na tym samym działaniu, a nie na wielu stronach
działania: na przykład dlatego, że formularz jest mały lub
jedynie działanie wymagane przez
potwierdź lub anuluj.
Element <dialog>
jest ostatnio stabilny w różnych przeglądarkach:
Zauważyłem, że w elemencie brakuje kilku elementów, więc w tym GUI Wyzwanie Dodaję doświadczenia programisty oczekiwanych przez Ciebie elementów: dodatkowych zdarzeń, lekkiego zamknięcia, niestandardowych animacji i dużych typów.
Markup
Podstawowe elementy elementu <dialog>
są skromne. Element będzie
automatycznie ukryty i ma wbudowane style, które nakładają się na treść.
<dialog>
…
</dialog>
Możemy poprawić tę wartość bazową.
Tradycyjnie elementy dialogowe mają wiele wspólnego z elementem modalnym i często ich nazwy
są wymienne. Tutaj mogę wykorzystać element dialogowy
zarówno niewielkie wyskakujące okienka (mini), jak i duże okna dialogowe. Nazwałem(-am) nazwę
w wersji mega i mini, a oba okna nieznacznie się przystosowały do różnych zastosowań.
Dodałem atrybut modal-mode
, aby umożliwić określenie typu:
<dialog id="MegaDialog" modal-mode="mega"></dialog>
<dialog id="MiniDialog" modal-mode="mini"></dialog>
Nie zawsze, ale zwykle do zbierania określonych danych
informacje o interakcji. Formularze w elementach dialogowych są tworzone z myślą o użytkownikach
razem.
Dobrze jest użyć elementu formularza, który otacza treść okna,
JavaScript ma dostęp do danych wpisanych przez użytkownika. Ponadto przyciski wewnątrz
formularz korzystający z method="dialog"
może zamknąć okno bez JavaScriptu i przekazać
i skalowalnych danych.
<dialog id="MegaDialog" modal-mode="mega">
<form method="dialog">
…
<button value="cancel">Cancel</button>
<button value="confirm">Confirm</button>
</form>
</dialog>
Mega okno
Duże okno ma 3 elementy wewnątrz formularza:
<header>
<article>
,
oraz
<footer>
.
Pełnią one funkcję kontenerów semantycznych, a także docelowych stylów dla elementów
prezentację danego okna. Nagłówek zawiera tytuł modalu i zawiera podsumowanie
Przycisk Ten artykuł dotyczy wprowadzania danych i informacji w formularzu. Stopka zawiera
<menu>
z
przyciski poleceń.
<dialog id="MegaDialog" modal-mode="mega">
<form method="dialog">
<header>
<h3>Dialog title</h3>
<button onclick="this.closest('dialog').close('close')"></button>
</header>
<article>...</article>
<footer>
<menu>
<button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
<button type="submit" value="confirm">Confirm</button>
</menu>
</footer>
</form>
</dialog>
Pierwszy przycisk menu zawiera
autofocus
i wbudowany moduł obsługi zdarzeń onclick
. Atrybut autofocus
otrzyma
po otwarciu okna dialogowego.
użyj przycisku anulowania, a nie przycisku potwierdzenia. Dzięki temu potwierdzenie
celowe, a nie przypadkowe.
Miniokno
Małe okno jest bardzo podobne do megaokna, ale brakuje w nim
<header>
element. Dzięki temu będą mniejsze i lepiej będą umieszczone w tekście.
<dialog id="MiniDialog" modal-mode="mini">
<form method="dialog">
<article>
<p>Are you sure you want to remove this user?</p>
</article>
<footer>
<menu>
<button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
<button type="submit" value="confirm">Confirm</button>
</menu>
</footer>
</form>
</dialog>
Okno to solidny fundament dla pełnego widocznego obszaru, aby zbierać dane i informacje o interakcjach użytkowników. Te podstawowe narzędzia mogą sprawić, do ciekawych i skutecznych interakcji w witrynie lub aplikacji.
Ułatwienia dostępu
Okno ma wbudowane bardzo dobre ułatwienia dostępu. Zamiast dodawać te elementy jak zwykle, ale wiele jest już dostępnych.
Przywracam fokus
Podobnie jak w ćwiczeniu Tworzenie bocznej nawigacji , ważne jest, Odpowiednie otwarcie i zamykanie czegoś skupia się na tym, co ważne, otwarte i zamknięte przyciskami. Po otwarciu panelu bocznego zaznaczenie zostanie umieszczone na przycisku zamykania. Gdy przycisk zamknięcia, zaznaczenie zostanie przywrócone na przycisk, który go otworzył.
W przypadku elementu okna wbudowane jest domyślne działanie:
Jeśli chcesz animować okno dialogowe i oddalać się od niego, ta funkcja przepada. Przywrócę go w sekcji JavaScript funkcji.
Traktowanie fokusu
Okno zarządza
inert
za Ciebie. Przed inert
do sprawdzania ostrości używano JavaScriptu
opuszczając element, w którym przechwytuje on i wsuwa go z powrotem.
Po inert
dowolne części dokumentu mogą zostać „zablokowane” nie są aż tak bardzo, że
nie są już skoncentrowane na celach ani nie są interaktywne po wskazaniu myszą. Zamiast łapać
zaznaczenie jest wyświetlane w jedynej interaktywnej części dokumentu.
Otwieranie i automatyczne ustawianie ostrości elementu
Domyślnie element okna aktywuje pierwszy element, który można zaznaczyć.
w znacznikach dialogu. Jeśli nie jest to najlepszy element, który użytkownik powinien wybrać,
użyj atrybutu autofocus
. Jak napisałem wcześniej,
najlepszą metodą jest to,
żeby umieścić go na przycisku anulowania, a nie na przycisku potwierdzenia. Dzięki temu
jest celowe i nie przypadkowe.
Zamykanie klawisza Escape
Ważne jest, by ułatwić zamknięcie tego potencjalnie zakłócającego elementu. Na szczęście element okna zajmie się klawiszem Escape, z pracy administracyjnego.
Style
Istnieje prosty sposób na określenie stylu elementu dialogowego i sztywnej ścieżki. Łatwe
trzeba wprowadzić zmiany, nie zmieniając właściwości wyświetlania okna,
z jego ograniczeniami. Przygotowuję animacje niestandardowe dla:
otwieranie i zamykanie okna, przejmując między innymi właściwość display
.
Stylizacja z otwartymi rekwizytami
Bez wstydu, aby przyspieszyć dostosowywanie kolorów i ogólną spójność projektu, co udało mi się znaleźć w bibliotece zmiennych CSS Open Props. W oprócz bezpłatnych zmiennych, importuję również znormalizować plik i niektóre z nich. przyciski – oba te formaty są przyciskami Open Props. udostępnia jako opcjonalne importy. Ten import pomaga mi skupić się na dostosowywaniu okna i demonstracja, nie wymagają przy tym wielu stylów dobrze.
Określanie stylu elementu <dialog>
Posiadanie usługi displayowej
Domyślny sposób pokazywania i ukrywania elementu okna przełącza widok.
od block
do none
. Niestety, ten film nie może być animowany.
i wylotu, tylko do wewnątrz. Chciałbym włączyć animację na początku i na końcu,
ustaw własny
właściwość display:
dialog {
display: grid;
}
Zmieniając i tym samym będąc właścicielem wartości usługi displayowej, jak widać w tabeli powyżej fragmentu kodu CSS, znaczna liczba stylów wymaga zarządzania, aby i dbać o wygodę użytkowników. Po pierwsze, domyślny stan okna to zamknięto. Możesz przedstawić ten stan wizualnie i uniemożliwić wyświetlanie otrzymywanie interakcji z następującymi stylami:
dialog:not([open]) {
pointer-events: none;
opacity: 0;
}
Teraz okno jest niewidoczne i nie można z nim korzystać, gdy nie jest otwarte. Później
Dodam JavaScript do zarządzania atrybutem inert
w oknie, dzięki czemu
użytkownicy korzystający z klawiatury i czytników ekranu nie mogą otworzyć ukrytego okna.
Nadanie dialogowi adaptacyjnego motywu kolorystycznego
Mimo że color-scheme
włącza dokument jako udostępniany przez przeglądarkę
z adaptacyjnym motywem kolorystycznym do preferencji systemu jasnego i ciemnego, chciałem dostosować
niż ten element okna. Otwarte rekwizyty zapewniają kilka powierzchni
kolorów, które automatycznie dostosowują się do
ustawienia systemu jasnego i ciemnego, podobnie jak w przypadku color-scheme
. Te
świetnie nadają się do tworzenia warstw.
Uwielbiam używać kolorów,
są zgodne z wyglądem powierzchni warstw. Kolor tła to
var(--surface-1)
; aby umieścić ją na tej warstwie, użyj narzędzia var(--surface-2)
:
dialog {
…
background: var(--surface-2);
color: var(--text-1);
}
@media (prefers-color-scheme: dark) {
dialog {
border-block-start: var(--border-size-1) solid var(--surface-3);
}
}
Później dodamy więcej kolorów adaptacyjnych do elementów podrzędnych, takich jak nagłówek. i stopkę. Traktuję je jako dodatkowe elementy dialogów, ale niezwykle ważne zaprojektowanie atrakcyjnego i dobrze zaprojektowanego dialogu.
Elastyczny rozmiar okien
Domyślnie okno dialogowe przekazuje rozmiar do zawartości, co jest
świetnie. Moim celem jest ograniczenie
max-inline-size
do czytelnych rozmiarów (--size-content-3
= 60ch
) lub do 90% szerokości widocznego obszaru. Ten
Zapewnia, że okno dialogowe nie będzie miało
na ekranie komputera, który trudno jest odczytać. Następnie dodaję
max-block-size
aby okno nie przekraczało wysokości strony. Oznacza to również, że będziemy
Określ, gdzie znajduje się obszar, który można przewijać (jeśli jest wysoki)
.
dialog {
…
max-inline-size: min(90vw, var(--size-content-3));
max-block-size: min(80vh, 100%);
max-block-size: min(80dvb, 100%);
overflow: hidden;
}
Czy widzisz, że aplikacja max-block-size
jest wyświetlana 2 razy? W pierwszym użyto 80vh
,
jednostki widocznego obszaru. Naprawdę chcę, aby dialog
był względny.
dla użytkowników międzynarodowych, więc używam logicznego, nowszego i tylko częściowo
obsługuje jednostkę dvb
w drugiej deklaracji, gdy stanie się bardziej stabilna.
Pozycjonowanie megaokna
Aby ułatwić określenie pozycji elementu okna, warto podzielić jego dwa aspekty: tło pełnoekranowe i kontener okna. Tło musi: wszystkie elementy, dodając efekt cieniowania, który pomaga wzmocnić efekt cieniowania. a treść jest niedostępna. Kontener dialogowy może wyśrodkowuje się na tym tle i nabiera dowolne kształty, jakich wymaga jego zawartość.
Te style naprawiają element okna, rozciągając go do każdego
i użyje elementu margin: auto
do wyśrodkowania treści:
dialog {
…
margin: auto;
padding: 0;
position: fixed;
inset: 0;
z-index: var(--layer-important);
}
Style okien Mega na komórki
W małych widocznych obszarach trochę inaczej określam styl tego megamodalnego całej strony. Ja
ustaw dolny margines na 0
, co umieszcza zawartość okna na dole
w widocznym obszarze. Dzięki kilku korektom stylu mogę zmienić okno
arkusz działań, bliżej kciuków użytkownika:
@media (max-width: 768px) {
dialog[modal-mode="mega"] {
margin-block-end: 0;
border-end-end-radius: 0;
border-end-start-radius: 0;
}
}
Pozycjonowanie miniokna
Gdy za pomocą większego widocznego obszaru, np. na komputerze, ustawiam miniokna dialogowe element, który je wywołał. Potrzebuję do tego JavaScriptu. Znajdziesz tu wykorzystywana przeze mnie technika tutaj, ale moim zdaniem wykraczają one poza zakres tego artykułu. Bez JavaScriptu wyświetla się na środku ekranu, tak jak duże okno dialogowe.
Wyróżnij się
Na koniec dodajmy trochę blasku do okna, aby wyglądało jak miękka, daleka powierzchnia nad stroną. Zmniejsza to miękkość obrazu za pomocą zaokrąglania rogów okna. Głębia jest tworzona dzięki starannie dopracowanym cieniu rekwizytów w otwartej przestrzeni rekwizyty:
dialog {
…
border-radius: var(--radius-3);
box-shadow: var(--shadow-6);
}
Dostosowywanie pseudoelementu tła
Postanowiłam pracować bardzo delikatnie z tłem, dodając tylko efekt rozmycia
backdrop-filter
to wielkie okno:
dialog[modal-mode="mega"]::backdrop {
backdrop-filter: blur(25px);
}
Ustawiam też przejście na backdrop-filter
, mam nadzieję, że przeglądarki
umożliwia zmianę elementu tła w przyszłości:
dialog::backdrop {
transition: backdrop-filter .5s ease;
}
Dodatki do stylu
Określam tę sekcję jako „dodatki”. ponieważ ma to więcej wspólnego z elementem okna niż w przypadku okna dialogowego.
Ograniczenia dotyczące przewijania
Po wyświetleniu okna użytkownika użytkownik wciąż może przewinąć stronę za nim, które są niepotrzebne:
Zwykle
overscroll-behavior
to standardowe rozwiązanie, ale zgodnie z
,
Nie ma to wpływu na okno dialogowe, ponieważ nie jest to port do przewijania, tzn.
czy przewijanie. Mogę użyć JavaScriptu, by monitorować
nowe zdarzenia z tego przewodnika, np. „zamknięte”; i „otwarty” oraz przełącz
overflow: hidden
na dokumencie, lub poczekaj, aż :has()
będzie stabilna w
wszystkie przeglądarki:
html:has(dialog[open][modal-mode="mega"]) {
overflow: hidden;
}
Teraz po otwarciu megaokna dokument HTML zawiera overflow: hidden
.
Układ <form>
Będąc bardzo istotnym elementem przy zbieraniu interakcji
na podstawie informacji od użytkownika, używam ich tutaj do rozmieszczenia nagłówka, stopki
elementów artykułu. Przy tym układzie zamierzam przedstawić artykuł podrzędny jako
obszar do przewijania. Robię to dzięki
grid-template-rows
Elementowi artykułu przypisano atrybut 1fr
, a formularz ma taką samą wartość maksymalną
wysokość jako element okna. Ustawienie tej wysokości i rozmiaru wiersza firmy
umożliwia ograniczenie elementu artykułu i przewijanie go, gdy wykracza poza kontener:
dialog > form {
display: grid;
grid-template-rows: auto 1fr auto;
align-items: start;
max-block-size: 80vh;
max-block-size: 80dvb;
}
Określanie stylu okna dialogowego <header>
Rola tego elementu to nadanie tytułu zawartości okna i oferty łatwo dostępny przycisk zamykania. Ma również określony kolor powierzchni, za treść artykułu. Te wymagania prowadzą do stworzenia Flexbox kontener, elementy wyrównane w pionie i odstępy od krawędzi, dopełnienie i luki, aby zwolnić trochę miejsca dla tytułu i przycisków zamykania:
dialog > form > header {
display: flex;
gap: var(--size-3);
justify-content: space-between;
align-items: flex-start;
background: var(--surface-2);
padding-block: var(--size-3);
padding-inline: var(--size-5);
}
@media (prefers-color-scheme: dark) {
dialog > form > header {
background: var(--surface-1);
}
}
Określanie stylu przycisku zamykania nagłówka
Ponieważ w wersji demonstracyjnej są używane przyciski Otwórz rekwizyty, przycisk zamykania jest dostosowany do w okrągły, środkowy przycisk:
dialog > form > header > button {
border-radius: var(--radius-round);
padding: .75ch;
aspect-ratio: 1;
flex-shrink: 0;
place-items: center;
stroke: currentColor;
stroke-width: 3px;
}
Określanie stylu okna dialogowego <article>
Element artykułu ma w tym oknie specjalną rolę: przewijane w przypadku długiego lub długiego okna.
W tym celu nadrzędny element formularza określił maksymalne wartości dla
, która określa ograniczenia, jakie dany element artykułu ma do osiągnięcia, jeśli otrzyma
są za wysokie. Ustaw overflow-y: auto
tak, aby paski przewijania były widoczne tylko wtedy, gdy są potrzebne.
zawiera przewijanie za pomocą overscroll-behavior: contain
, a pozostałe
zostaną użyte niestandardowe style prezentacji:
dialog > form > article {
overflow-y: auto;
max-block-size: 100%; /* safari */
overscroll-behavior-y: contain;
display: grid;
justify-items: flex-start;
gap: var(--size-3);
box-shadow: var(--shadow-2);
z-index: var(--layer-1);
padding-inline: var(--size-5);
padding-block: var(--size-3);
}
@media (prefers-color-scheme: light) {
dialog > form > article {
background: var(--surface-1);
}
}
Określanie stylu okna dialogowego <footer>
Stopka ma zawierać menu z przyciskami polecenia. Flexbox jest używany do: wyrównaj treść do końca wbudowanej osi stopki, a następnie ustaw odstępy zostawmy przyciskom trochę wolnego miejsca.
dialog > form > footer {
background: var(--surface-2);
display: flex;
flex-wrap: wrap;
gap: var(--size-3);
justify-content: space-between;
align-items: flex-start;
padding-inline: var(--size-5);
padding-block: var(--size-3);
}
@media (prefers-color-scheme: dark) {
dialog > form > footer {
background: var(--surface-1);
}
}
Określanie stylu menu stopki okna
menu
zawiera przyciski poleceń, które można wykorzystać w oknie. Wykorzystuje zawijanie
układ flexbox z elementem gap
zapewniający odstęp między przyciskami. Elementy menu
zawiera dopełnienie, takie jak <ul>
. Usuwam też ten styl, ponieważ nie jest mi potrzebny.
dialog > form > footer > menu {
display: flex;
flex-wrap: wrap;
gap: var(--size-3);
padding-inline-start: 0;
}
dialog > form > footer > menu:only-child {
margin-inline-start: auto;
}
Animacja
Elementy okien są często animowane, ponieważ otwierają i zamykają okno. Zachęcanie widzów do ruchu w ramach ruchu przychodzącego i wyjścia pomaga użytkownikom nie orientują się w przepływie treści.
Zwykle element okna może być animowany tylko wewnątrz, ale nie na zewnątrz. Dzieje się tak, ponieważ
przeglądarka przełącza właściwość display
w elemencie. Wcześniej w przewodniku
ustawia wyświetlanie na siatkę, ale nigdy nie ustawia go na brak. Dzięki temu możesz:
pojawia się i znika.
Otwarte rekwizyty obejmują wiele klatek kluczowych animacji do użycia, co sprawia, a ich administracja jest łatwa i czytelna. Oto cele animacji i nałożone na nią warstwy podejście wybrane przeze mnie:
- Zmniejszony ruch to domyślne przejście, proste przejście od nieprzezroczystości, które pojawia się lub znika.
- Jeśli ruch jest prawidłowy, zostaną dodane animacje przesuwania i skalowania.
- Elastyczny układ megaokna na urządzenia mobilne jest dostosowany do wysuwania.
bezpieczne i wartościowe przeniesienie domyślne;
Choć otwarte rekwizyty są wyposażone w klatki kluczowe do zanikania i znikania, wolę ten
warstwowego podejścia do przejść jako domyślnego, przy czym animacje klatek kluczowych
potencjalnych aktualizacji. Wcześniej określiliśmy styl widoczności okna
przezroczystość, administrowanie 1
lub 0
w zależności od atrybutu [open]
. Do
między 0% a 100%, poinformuj przeglądarkę, jak długo i jakiego rodzaju
wygładzanie, które chcesz zmienić:
dialog {
transition: opacity .5s var(--ease-3);
}
Dodawanie ruchu do przejścia
Jeśli użytkownikowi nie przeszkadza ruch, powinny się przesuwać zarówno duże okna, jak i miniatury
jako wejście i zwiększać skalę
jako wyjście. Możesz to osiągnąć za pomocą
prefers-reduced-motion
zapytanie o multimedia i kilka otwartych rekwizytów:
@media (prefers-reduced-motion: no-preference) {
dialog {
animation: var(--animation-scale-down) forwards;
animation-timing-function: var(--ease-squish-3);
}
dialog[open] {
animation: var(--animation-slide-in-up) forwards;
}
}
Dostosowanie animacji wyjściowej do wyświetlania na urządzeniach mobilnych
Wcześniej w sekcji dotyczącej stylów został przystosowany styl megaokna do wyświetlania na urządzeniach mobilnych. gdy urządzenia przypominają arkusz działań, jak kartka papieru od dołu do góry i nadal jest przymocowana u dołu. Skala na zewnątrz nie pasuje dobrze do nowego układu. Możemy ją dostosować w taki sposób, kilka zapytań o media i kilka otwartych rekwizytów:
@media (prefers-reduced-motion: no-preference) and @media (max-width: 768px) {
dialog[modal-mode="mega"] {
animation: var(--animation-slide-out-down) forwards;
animation-timing-function: var(--ease-squish-2);
}
}
JavaScript
W JavaScripcie jest jeszcze kilka rzeczy, które można dodać:
// dialog.js
export default async function (dialog) {
// add light dismiss
// add closing and closed events
// add opening and opened events
// add removed event
// removing loading attribute
}
Te dodatki mają związek z chęcią lekkiego zamknięcia okna (kliknięcie okna dialogowego w tle), animację i kilka dodatkowych zdarzeń, aby szybciej wyświetlać reklamy dane formularza.
Dodaję oświetlenie zamknięte
To proste zadanie i świetne uzupełnienie elementu dialogowego,
nie były animowane. Interakcja jest rejestrowana przez obserwowanie kliknięć okna.
i wykorzystanie zdarzeń
bąbelki
ocenia kliknięcia. Bierzemy przy tym pod uwagę tylko
close()
.
jeśli jest to element znajdujący się najwyżej:
export default async function (dialog) {
dialog.addEventListener('click', lightDismiss)
}
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
Powiadomienie: dialog.close('dismiss')
. Zdarzenie jest wywoływane i podano ciąg znaków.
Ten ciąg może być pobierany przez inny kod JavaScript, aby uzyskać informacje o tym, jak
zostało zamknięte. Za każdym razem, gdy dzwonię,
funkcji za pomocą różnych przycisków, aby zapewnić kontekst dotyczący aplikacji
interakcji użytkownika.
Dodaję zdarzenia zamykające i zamknięte
Z elementem dialogu towarzyszy zdarzenie zamknięcia, które pojawia się natychmiast, gdy
funkcja okna close()
jest wywoływana. Trzeba animować ten element,
dobrze jest mieć zdarzenia przed animacją i po niej, by zmienić
lub zresetować formularz. Używam go do zarządzania dodawaniem
inert
w zamkniętym oknie i w wersji demonstracyjnej używam ich do modyfikowania
listę awatarów, jeśli użytkownik przesłał nowe zdjęcie.
W tym celu utwórz 2 nowe zdarzenia o nazwach closing
i closed
. Potem
nasłuchuje wbudowanego zdarzenia zamknięcia w oknie. W tym miejscu ustaw okno dialogowe na
inert
i wysyłaj zdarzenie closing
. Następne zadanie to poczekać na
animacji i przejść, by zakończyć ich działanie w oknie, a następnie wysłać
closed
zdarzenie.
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent = new Event('closed')
export default async function (dialog) {
…
dialog.addEventListener('close', dialogClose)
}
const dialogClose = async ({target:dialog}) => {
dialog.setAttribute('inert', '')
dialog.dispatchEvent(dialogClosingEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogClosedEvent)
}
const animationsComplete = element =>
Promise.allSettled(
element.getAnimations().map(animation =>
animation.finished))
funkcja animationsComplete
, która jest też wykorzystywana podczas tworzenia toastu.
komponent, zwraca obietnicę na podstawie
do końca animacji i przejścia. Dlatego dialogClose
jest asynchroniczny
funkcji;
może następnie
await
obietnica została zwrócona i z większą pewnością przejdziemy do etapu zamkniętego.
Dodawanie otwieranych i otwartych zdarzeń
Dodanie tych zdarzeń nie jest łatwe, ponieważ wbudowany element okna zdarzenia otwartego, tak jak w przypadku zamknięcia. Korzystam z MutationObserver aby uzyskać wgląd w zmiany atrybutów okna. W tym obserwatorze Będę śledzić zmiany w atrybucie otwieranie i zarządzać zdarzeniami niestandardowymi odpowiednio się zmienia.
Podobnie jak w przypadku wydarzeń zamykających i zakończonych, utwórz 2 nowe wydarzenia.
o nazwach opening
i opened
. w miejscach, w których wcześniej nasłuchiwaliśmy zamknięcia okna.
tym razem za pomocą utworzonego obserwatora mutacji
.
…
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent = new Event('opened')
export default async function (dialog) {
…
dialogAttrObserver.observe(dialog, {
attributes: true,
})
}
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(async mutation => {
if (mutation.attributeName === 'open') {
const dialog = mutation.target
const isOpen = dialog.hasAttribute('open')
if (!isOpen) return
dialog.removeAttribute('inert')
// set focus
const focusTarget = dialog.querySelector('[autofocus]')
focusTarget
? focusTarget.focus()
: dialog.querySelector('button').focus()
dialog.dispatchEvent(dialogOpeningEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogOpenedEvent)
}
})
})
Funkcja wywołania zwrotnego obserwatora mutacji jest wywoływana, gdy okno dialogowe
atrybuty zmieniają się, co powoduje wyświetlenie listy zmian w postaci tablicy. Przeprowadź iterację
atrybut się zmienia, szukając elementu attributeName
. Następnie
czy element ma atrybut czy nie: to ustawienie informuje, czy okno
została otwarta. Jeśli została otwarta, usuń atrybut inert
i ustaw fokus
do elementu żądającego
autofocus
.
lub pierwszy element button
znaleziony w oknie. Na koniec, podobnie jak w przypadku zamknięcia
i zdarzenia zamkniętego, wyślij od razu zdarzenie otwierające, poczekaj na animacje
aby zakończyć, a następnie wysłać otwarte zdarzenie.
Dodawanie usuniętego wydarzenia
W aplikacjach jednostronicowych okna dialogowe są często dodawane i usuwane na podstawie tras lub inne potrzeby związane z aplikacją. Przydaje się, jeśli wyczyścisz zdarzenia lub dane po usunięciu okna.
Możesz to osiągnąć, korzystając z innego obserwatora mutacji. Tym razem zamiast obserwując atrybuty w elemencie dialogowym, obserwujemy elementy podrzędne i sprawdź, czy elementy dialogowe zostaną usunięte.
…
const dialogRemovedEvent = new Event('removed')
export default async function (dialog) {
…
dialogDeleteObserver.observe(document.body, {
attributes: false,
subtree: false,
childList: true,
})
}
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
mutation.removedNodes.forEach(removedNode => {
if (removedNode.nodeName === 'DIALOG') {
removedNode.removeEventListener('click', lightDismiss)
removedNode.removeEventListener('close', dialogClose)
removedNode.dispatchEvent(dialogRemovedEvent)
}
})
})
})
Wywołanie zwrotne obserwatora mutacji jest wywoływane za każdym razem, gdy elementy podrzędne są dodawane lub usuwane
z treści dokumentu. Konkretne mutacje, które oglądasz, służą
removedNodes
, które zawierają
nodeName
z
okno. Jeśli okno zostało usunięte, zdarzenia kliknięcia i zamknięcia są usuwane z
zwolnienie pamięci i wysłanie niestandardowego zdarzenia usuniętego.
Usunięcie atrybutu wczytywania
Aby uniemożliwić odtwarzanie animacji wyjściowej okna po dodaniu do lub podczas wczytywania strony, do okna został dodany atrybut wczytywania. następujący skrypt czeka na zakończenie działania animacji dialogów, a następnie usuwa atrybut. Okno dialogowe można teraz swobodnie animować. ukrycie animacji, która w przeciwnym razie może rozpraszać uwagę.
export default async function (dialog) {
…
await animationsComplete(dialog)
dialog.removeAttribute('loading')
}
Dowiedz się więcej o zapobieganiu pojawianiu się animacji klatek kluczowych podczas wczytywania strony. tutaj.
Wszystko razem
Oto dialog.js
w całości, skoro objaśniliśmy już każdą sekcję
pojedynczo:
// custom events to be added to <dialog>
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent = new Event('closed')
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent = new Event('opened')
const dialogRemovedEvent = new Event('removed')
// track opening
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(async mutation => {
if (mutation.attributeName === 'open') {
const dialog = mutation.target
const isOpen = dialog.hasAttribute('open')
if (!isOpen) return
dialog.removeAttribute('inert')
// set focus
const focusTarget = dialog.querySelector('[autofocus]')
focusTarget
? focusTarget.focus()
: dialog.querySelector('button').focus()
dialog.dispatchEvent(dialogOpeningEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogOpenedEvent)
}
})
})
// track deletion
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
mutations.forEach(mutation => {
mutation.removedNodes.forEach(removedNode => {
if (removedNode.nodeName === 'DIALOG') {
removedNode.removeEventListener('click', lightDismiss)
removedNode.removeEventListener('close', dialogClose)
removedNode.dispatchEvent(dialogRemovedEvent)
}
})
})
})
// wait for all dialog animations to complete their promises
const animationsComplete = element =>
Promise.allSettled(
element.getAnimations().map(animation =>
animation.finished))
// click outside the dialog handler
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
const dialogClose = async ({target:dialog}) => {
dialog.setAttribute('inert', '')
dialog.dispatchEvent(dialogClosingEvent)
await animationsComplete(dialog)
dialog.dispatchEvent(dialogClosedEvent)
}
// page load dialogs setup
export default async function (dialog) {
dialog.addEventListener('click', lightDismiss)
dialog.addEventListener('close', dialogClose)
dialogAttrObserver.observe(dialog, {
attributes: true,
})
dialogDeleteObserver.observe(document.body, {
attributes: false,
subtree: false,
childList: true,
})
// remove loading attribute
// prevent page load @keyframes playing
await animationsComplete(dialog)
dialog.removeAttribute('loading')
}
Korzystanie z modułu dialog.js
Wyeksportowana funkcja z modułu oczekuje, że zostanie wywołana i przekazana okno element, do którego chce dodać te nowe zdarzenia i funkcje:
import GuiDialog from './dialog.js'
const MegaDialog = document.querySelector('#MegaDialog')
const MiniDialog = document.querySelector('#MiniDialog')
GuiDialog(MegaDialog)
GuiDialog(MiniDialog)
Te 2 okna dialogowe zostały udoskonalone – lekkie zamknięcie, animacja, poprawek błędów i innych zdarzeń, nad którymi trzeba pracować.
Nasłuchiwanie nowych zdarzeń niestandardowych
Każdy uaktualniony element okna może nasłuchiwać 5 nowych zdarzeń, na przykład:
MegaDialog.addEventListener('closing', dialogClosing)
MegaDialog.addEventListener('closed', dialogClosed)
MegaDialog.addEventListener('opening', dialogOpening)
MegaDialog.addEventListener('opened', dialogOpened)
MegaDialog.addEventListener('removed', dialogRemoved)
Oto 2 przykłady obsługi tych zdarzeń:
const dialogOpening = ({target:dialog}) => {
console.log('Dialog opening', dialog)
}
const dialogClosed = ({target:dialog}) => {
console.log('Dialog closed', dialog)
console.info('Dialog user action:', dialog.returnValue)
if (dialog.returnValue === 'confirm') {
// do stuff with the form values
const dialogFormData = new FormData(dialog.querySelector('form'))
console.info('Dialog form data', Object.fromEntries(dialogFormData.entries()))
// then reset the form
dialog.querySelector('form')?.reset()
}
}
W utworzonej przeze mnie wersji demonstracyjnej z elementem dialogu użyję zdarzenia zamkniętego, dane formularza, aby dodać do listy nowy element awatara. Odpowiednia pora jest że okno dialogowe zakończyło animację wyjścia, a potem były animowane niektóre skrypty w nowym awatarze. Dzięki nowym wydarzeniom administrowanie wrażeniami użytkowników może działać płynniej.
Uwaga dialog.returnValue
: zawiera ciąg zamykający przekazywany, gdy
zdarzenie w oknie dialogowym close()
. W przypadku zdarzenia dialogClosed
bardzo ważne jest,
czy okno zostało zamknięte, anulowane lub potwierdzone. Jeśli dane te są potwierdzone,
pobiera wartości formularza i resetuje formularz. Zresetowanie jest przydatne, więc
aby po ponownym wyświetleniu okno było puste i gotowe do przesłania nowego pliku.
Podsumowanie
Wiesz już, jak to zrobiłem. Jak Ty? 🙂
Stosujmy różne podejścia i poznajmy sposoby budowania obecności w internecie.
Utwórz wersję demonstracyjną, a potem dodaj linki do funkcji tweetuj mi. znajdziesz poniżej w sekcji z remiksami na karcie Społeczność.
Remiksy utworzone przez społeczność
- @GrimLink z aparatem 3 w 1 .
- @mikemai2awesome i ładny
remiks, który nie zmienia
display
. - @geoffrich_ z Smukłe i ładne Skrócony lakier FLIP.
Zasoby
- Kod źródłowy w GitHubie
- Awatary doodle