<dialog>
öğesiyle renk uyarlanabilir, duyarlı ve erişilebilir mini ve mega kalıcı modlar oluşturmaya temel bir genel bakış.
Bu yayında, <dialog>
öğesiyle renge uyarlanabilir, duyarlı ve erişilebilir
küçük ve mega modallerin nasıl oluşturulacağıyla ilgili düşüncelerimi paylaşmak istiyorum.
Demoyu deneyin ve kaynağı görüntüleyin.
Videoyu tercih ediyorsanız bu yayının YouTube sürümünü burada bulabilirsiniz:
Genel bakış
<dialog>
öğesi, sayfa içi bağlamsal bilgiler veya işlemler için idealdir. Kullanıcı deneyiminin birden çok sayfalı işlem yerine aynı sayfa işleminden faydalanabileceği durumları düşünün. Bu durum, formun küçük olması veya kullanıcının yapması gereken tek işlemin onaylama ya da iptal işlemi olması olabilir.
<dialog>
öğesi yakın zamanda farklı tarayıcılarda kararlı hale geldi:
Öğede birkaç şeyin eksik olduğunu fark ettim ve bu GUI Meydan Okuması'nda beklediğim geliştirici deneyimi öğelerini ekliyorum: ek etkinlikler, ışık kapatma, özel animasyonlar, mini ve mega tür.
Markup
<dialog>
öğesinin temel özellikleri mütevazıdır. Öğe otomatik olarak gizlenir ve içeriğinizin yerini alacak şekilde yerleşik stillere sahiptir.
<dialog>
…
</dialog>
Bu temel çizgiyi iyileştirebiliriz.
Geleneksel olarak, bir diyalog öğesi modal ile birçok şey paylaşır ve çoğu zaman adlar birbirinin yerine kullanılabilir. Burada diyalog öğesini hem küçük diyalog pop-up'ları (mini) hem de tam sayfa diyaloglar (mega) için
kullanma özgürlüğünü çektim. Bunları mega ve mini olarak adlandırdım. Her iki iletişim kutusu da farklı kullanım alanlarına birazdan uyarlanmış.
Türü belirtmenize olanak tanımak için bir modal-mode
özelliği ekledim:
<dialog id="MegaDialog" modal-mode="mega"></dialog>
<dialog id="MiniDialog" modal-mode="mini"></dialog>
Her zaman olmasa da, bazı etkileşim bilgilerini toplamak için genellikle iletişim öğeleri kullanılır. İletişim kutusu öğelerinin içindeki formlar bir araya gelecek şekilde oluşturulmuştur.
JavaScript'in kullanıcının girdiği verilere erişebilmesi için iletişim kutusu içeriğinizi bir form öğesinin sarmalaması önerilir. Dahası, method="dialog"
kullanan bir formun içindeki düğmeler JavaScript olmadan iletişim kutusunu kapatabilir ve veri aktarabilir.
<dialog id="MegaDialog" modal-mode="mega">
<form method="dialog">
…
<button value="cancel">Cancel</button>
<button value="confirm">Confirm</button>
</form>
</dialog>
Mega iletişim kutusu
Mega iletişim kutusunun form içinde üç öğesi vardır:
<header>
,
<article>
ve
<footer>
.
Bunlar, anlamsal kapsayıcılar ve iletişim kutusunun sunumu için stil hedefleri görevi görür. Başlık, kalıcı metin olarak adlandırılır ve bir kapat düğmesi sunar. Bu makale form girişleri ve bilgiler içindir. Alt bilgide işlem düğmelerinden oluşan bir <menu>
yer bulunur.
<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>
İlk menü düğmesinde autofocus
ve bir onclick
satır içi etkinlik işleyici vardır. İletişim kutusu açıldığında autofocus
özelliği odağı alır ve en iyi yöntem bunun onay düğmesine değil, iptal düğmesine yerleştirilmesidir. Bu şekilde doğrulama kasten yapılır,
hatalı olmasın.
Mini iletişim kutusu
Mini iletişim kutusu, mega iletişim kutusuna çok benziyor. Sadece bir <header>
öğesi eksik. Bu, reklam öğesinin daha küçük ve daha satır içi olmasını sağlar.
<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>
Diyalog öğesi, veri ve kullanıcı etkileşimi toplayabilen tam görüntü alanı öğesi için güçlü bir temel sağlar. Bu temel öğeler, sitenizde veya uygulamanızda oldukça ilginç ve güçlü etkileşimler sağlayabilir.
Erişilebilirlik
İletişim öğesi çok iyi bir yerleşik erişilebilirliğe sahip. Bu özellikleri her zaman yaptığım gibi eklemek yerine, birçoğu zaten orada.
Odak geri yükleniyor
Sidenav bileşeni oluşturma konusunda manuel olarak yaptığımız gibi, bir öğeyi doğru şekilde açıp kapatmanın, ilgili açma ve kapatma düğmelerine odaklanması önemlidir. Söz konusu yan gezinme çubuğu açıldığında odak, kapat düğmesine konur. Kapat düğmesine basıldığında, odak, düğmeyi açan düğmeye geri yüklenir.
İletişim öğesi kullanıldığında, yerleşik varsayılan davranıştır:
İletişim kutusunu içeri ve dışarı almak istediğinizde bu işlev kullanılamaz. JavaScript bölümünde bu işlevi geri yükleyeceğim.
Yakalama odağı
İletişim öğesi, dokümanda inert
özelliğini sizin için yönetir. inert
öncesinde JavaScript, odağın bir öğeden ayrılıp ayrılmadığına dikkat etmek için kullanılıyordu. Bu noktada öğe engellenip öğeyi geri koyuyor.
inert
tarihinden sonra, dokümanın herhangi bir bölümü artık odak hedefi olmayacak veya fareyle etkileşimli olacak şekilde "dondurulabilir". Odağı hapsetmek yerine odak, belgenin tek etkileşimli bölümüne yönlendirilir.
Bir öğeyi açma ve otomatik odaklama
Varsayılan olarak iletişim kutusu, odağı iletişim kutusu işaretlemesindeki ilk odaklanılabilir öğeye atar. Bu, kullanıcının varsayılan olarak kullanacağı en iyi öğe değilse autofocus
özelliğini kullanın. Daha önce de belirttiğim gibi bence en iyi uygulama, onay
düğmesine değil, iptal düğmesine yerleştirmek. Bu, onayın kasıtlı olmasını ve kazara yapılmadığını sağlar.
Escape tuşuyla kapatma
Çalışmayı kesintiye uğratabilecek bu unsurun kolayca kapatılabilmesi önemlidir. Neyse ki iletişim öğesi, çıkış anahtarını sizin yerinize hallederek sizi düzenleme yükünden kurtarır.
Stiller
Diyalog öğesinin ve sabit yolunun stillerini belirlemenin kolay bir yolu vardır. İletişimin görüntüleme özelliğinin değiştirilmemesi ve sınırlamalarına uyulmasıyla kolay yol sağlanır. İletişim kutusunu açıp kapatmak, display
özelliğini devralmak ve daha fazlasını yapmak için özel animasyonlar sağlamak üzere zor yoldan ilerliyorum.
Açık Sahnelerle Stil Oluşturma
Uyarlanabilir renkleri ve genel tasarım tutarlılığını hızlandırmak için Open Props adlı CSS değişken kitaplığımda utanç verici bir yol izledim. Ücretsiz sağlanan değişkenlere ek olarak, bir normalize dosyası ve bazı düğmeleri de içe aktarıyorum. Open Props'lar bunların her ikisini de isteğe bağlı içe aktarma işlemleri olarak sağlar. Bu içe aktarma işlemleri, iletişim kutusunu ve demoyu özelleştirmeye odaklanmama yardımcı oluyor. Ayrıca destek ve iyi görünmesini sağlamak için çok fazla stile gerek kalmıyor.
<dialog>
öğesinin stilini belirleme
Görüntülü reklam mülküne sahip olma
Bir iletişim öğesinin varsayılan gösterme ve gizleme davranışı, görüntülü reklam özelliğini block
değerinden none
değerine değiştirir. Maalesef bu, yalnızca içeri ve dışarı
çıkarılamayacağı anlamına gelir. Hem giriş hem de çıkış animasyonu yapmak istiyorum ve ilk adım kendi display özelliğimi ayarlamak:
dialog {
display: grid;
}
Yukarıdaki CSS snippet'inde gösterildiği gibi görüntülü reklam özelliği değerini değiştirip bu değer sahibi olarak, uygun kullanıcı deneyimini kolaylaştırmak için önemli miktarda stilin yönetilmesi gerekir. İlk olarak, iletişim kutusunun varsayılan durumu kapatılır. Bu durumu görsel olarak temsil edebilir ve iletişim kutusunun aşağıdaki stillerle etkileşimler almasını engelleyebilirsiniz:
dialog:not([open]) {
pointer-events: none;
opacity: 0;
}
Artık iletişim kutusu görünmez durumda ve açık değilken iletişim kutusuyla etkileşimde bulunulamaz. Daha sonra, iletişim kutusunda inert
özelliğini yönetmek için birkaç JavaScript
ekleyeceğim. Böylece, klavye ve ekran okuyucu kullanıcılarının da gizli iletişim kutusuna ulaşmasını engelleyeceğim.
İletişim kutusuna uyarlanabilir bir renk teması verme
color-scheme
dokümanınız için açık ve koyu sistem tercihlerine tarayıcı tarafından sağlanan uyarlanabilir bir renk temasını etkinleştirirken iletişim öğesini bundan daha fazla özelleştirmek istedim. Open Props, color-scheme
kullanımına benzer şekilde açık ve koyu sistem tercihlerine otomatik olarak uyum sağlayan birkaç yüzey rengi sağlar. Bunlar, bir tasarımda katman oluşturmak için çok kullanışlı. Katman yüzeylerinin bu görünümünü görsel olarak desteklemek için renkler kullanmayı seviyorum. Arka plan rengi var(--surface-1)
; bu katmanın üzerine oturmak için var(--surface-2)
değerini kullanın:
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);
}
}
İleride üstbilgi ve altbilgi gibi alt öğeler için daha fazla uyarlanabilir renkler eklenecektir. Bunları diyalog öğesi için ekstra bir şey olarak düşünüyorum, ancak ilgi çekici ve iyi tasarlanmış bir diyalog tasarımında gerçekten çok önemli.
Duyarlı iletişim kutusu boyutlandırma
İletişim kutusu, varsayılan olarak boyutu içeriğine yetki verir. Bu da genellikle harika bir yöntemdir. Buradaki amacım max-inline-size
alanını okunabilir bir boyutla (--size-content-3
= 60ch
) veya görüntü alanı genişliğinin% 90'ını sınırlamak. Bu, iletişimin mobil cihazlarda uçtan uca kaymamasını ve masaüstü ekranında
okumak zor olacak kadar geniş olmamasını sağlar. Ardından, iletişim kutusunun sayfanın yüksekliğini aşmaması için bir max-block-size
ekliyorum. Bu, uzun bir iletişim kutusu öğesi olması
durumunda iletişim kutusunun kaydırılabilir alanının nerede olduğunu da belirtmemiz gerektiği anlamına gelir.
dialog {
…
max-inline-size: min(90vw, var(--size-content-3));
max-block-size: min(80vh, 100%);
max-block-size: min(80dvb, 100%);
overflow: hidden;
}
İki kez max-block-size
işareti yaptığımı fark ettin mi? Birincisi, fiziksel bir görüntü alanı birimi olan 80vh
'i kullanır. Gerçekten istediğim şey, iletişim kutusunun uluslararası kullanıcılar için göreli akış içinde kalmasını sağlamak. Bu yüzden, daha istikrarlı hale geleceği zaman için ikinci bildirimde mantıksal, daha yeni ve yalnızca kısmen desteklenen dvb
birimini kullanıyorum.
Mega diyalog konumlandırması
Bir iletişim öğesinin yerleştirilmesine yardımcı olmak için, tam ekran arka planı ve iletişim kutusu kapsayıcısı olmak üzere iki parçasını ayırmak yararlı olacaktır. Arka plan, bu iletişim kutusunun önde olduğunu ve arkasındaki içeriğe erişilemediğini desteklemek için bir gölge efekti sağlayarak her şeyi kapsamalıdır. İletişim kutusu kapsayıcısı, kendini bu arka planın üzerinde ortalayabilir ve içeriği gereken şekli alabilir.
Aşağıdaki stiller, iletişim öğesini pencereye sabitleyerek her bir köşeye uzatır ve içeriği ortalamak için margin: auto
kullanır:
dialog {
…
margin: auto;
padding: 0;
position: fixed;
inset: 0;
z-index: var(--layer-important);
}
Mobil mega iletişim kutusu stilleri
Küçük görüntü alanlarında, bu tam sayfa mega kalıcı iletişimin stilini biraz farklı bir şekilde değiştiriyorum. Alt kenar boşluğunu 0
olarak ayarladım. Böylece iletişim kutusu içeriği görüntü alanının en altına taşınır. Birkaç stil ayarlamasıyla iletişim kutusunu kullanıcının
parmağına yakın bir eylem sayfasına dönüştürebilirim:
@media (max-width: 768px) {
dialog[modal-mode="mega"] {
margin-block-end: 0;
border-end-end-radius: 0;
border-end-start-radius: 0;
}
}
Mini diyalog konumlandırması
Masaüstü bilgisayar gibi daha büyük bir görüntü alanı kullanırken, mini iletişim kutularını bunları çağıran öğenin üzerine yerleştirmeyi seçtim. Bunun için JavaScript'e ihtiyacım var. Kullandığım tekniği burada bulabilirsiniz, ancak bunun bu makalenin kapsamı dışında olduğunu düşünüyorum. JavaScript olmadan, mini iletişim kutusu tıpkı mega iletişim kutusu gibi ekranın ortasında görünür.
Çarpıcı hale getirin
Son olarak, iletişim kutusunu, sayfanın çok yukarısında yumuşak bir yüzey gibi görünür bir hale getirin. İletişimin yumuşaklığı, iletişim kutusunun köşeleri yuvarlanarak elde edilir. Derinlik, Open Props'un dikkatlice hazırlanmış gölge bileşenlerinden biriyle elde edilir:
dialog {
…
border-radius: var(--radius-3);
box-shadow: var(--shadow-6);
}
Arka plan sözde öğesini özelleştirme
Arka planla çok hafif çalışmayı seçtim, yalnızca mega iletişim kutusuna
backdrop-filter
ile bulanıklaştırma efekti ekledim:
dialog[modal-mode="mega"]::backdrop {
backdrop-filter: blur(25px);
}
Ayrıca, tarayıcıların gelecekte arka plan öğesi geçişinin yapılmasına izin vereceğini umarak backdrop-filter
uygulamasında bir geçiş yapmayı da tercih ettik:
dialog::backdrop {
transition: backdrop-filter .5s ease;
}
Ek özelliklerin stil özelliklerini ayarlama
Bu bölüme "ekstralar" adını veriyorum çünkü genel olarak diyalog öğesi demomdan ziyade iletişim öğe demomla alakalı.
Kaydırma kapsama
İletişim kutusu görüntülendiğinde kullanıcı hâlâ sayfanın arkasındaki sayfayı kaydırabiliyor. Ben bunu istemiyorum:
Normalde overscroll-behavior
benim her zamanki çözümüm olurdu, ancak belirtilemeye göre, bir kaydırma bağlantı noktası olmadığından iletişim kutusu üzerinde hiçbir etkisi yoktur. Yani bir kaydırma çubuğu olmadığından engellenecek bir şey de yoktur. Bu kılavuzdaki "closed" ve "opened" gibi yeni etkinlikleri izlemek için JavaScript kullanabilir ve dokümanda overflow: hidden
ayarını açık duruma getirebilirim veya :has()
öğesinin tüm tarayıcılarda kararlı olmasını bekleyebilirim:
html:has(dialog[open][modal-mode="mega"]) {
overflow: hidden;
}
Artık bir mega iletişim kutusu açıkken html belgesinde overflow: hidden
var.
<form>
düzeni
Etkileşim bilgilerini kullanıcıdan toplamak için çok önemli bir öğe olmasının yanı sıra, burada üstbilgi, altbilgi ve makale öğelerini düzenlemek için kullanıyorum. Bu düzenle makale alt öğesini kaydırılabilir bir alan olarak belirtmeyi amaçlıyorum. Bunu grid-template-rows
ile elde ediyorum.
Makale öğesine 1fr
verilmiş ve formun kendisi iletişim öğesiyle aynı maksimum yüksekliğe sahip. Bu firma yüksekliğini ve firma satır boyutunun ayarlanması, makale öğesinin sınırlanmasına ve taştığında kaydırma yapmasına olanak tanır:
dialog > form {
display: grid;
grid-template-rows: auto 1fr auto;
align-items: start;
max-block-size: 80vh;
max-block-size: 80dvb;
}
<header>
iletişim kutusunun stilini belirleme
Bu öğenin rolü, iletişim kutusu içeriği için bir başlık ve kolayca bulunabilen bir kapat düğmesi sunmaktır. Ayrıca, iletişim kutusu makale içeriğinin arkasındaymış gibi bir yüzey rengi de verilir. Bu gereksinimler bir flexbox kapsayıcısına, kenarlarına yerleştirilmiş dikey olarak hizalanmış öğelere, başlık ve kapat düğmelerine biraz alan vermek için bir miktar dolgu ve boşluklara yol açar:
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);
}
}
Başlığı kapatma düğmesi stilini belirleme
Demo, Open Props düğmelerini kullandığından kapat düğmesi şu şekilde yuvarlak simge merkezli bir düğme şeklinde özelleştirilir:
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;
}
<article>
iletişim kutusunun stilini belirleme
Makale öğesinin bu iletişim kutusunda özel bir rolü vardır: Uzun veya uzun bir iletişim kutusu durumunda kaydırılması amaçlanan bir alandır.
Bunu başarmak için, üst form öğesi kendi kendine bazı maksimum değerler belirlemiştir. Bu değerler, öğe çok uzarsa ulaşılması için kısıtlamalar oluşturur. overflow-y: auto
öğesini, kaydırma çubuklarının yalnızca gerektiğinde gösterileceği şekilde ayarlayın. Kaydırma çubuklarının içinde overscroll-behavior: contain
ile kaydırma yapılmasını sağlayın. Geri kalanlar özel sunu stilleri olacaktır:
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);
}
}
<footer>
iletişim kutusunun stilini belirleme
Altbilginin rolü, işlem düğmelerinin menülerini içermektir. Flexbox, içeriği altbilgi satır içi ekseninin sonuna hizalamak ve ardından düğmelere yer açmak için biraz boşluk kullanılır.
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);
}
}
İletişim kutusu altbilgi menüsünün stilini belirleme
menu
öğesi, iletişim kutusunun işlem düğmelerini eklemek için kullanılır. Düğmeler arasında boşluk bırakmak için gap
ile sarmalama flexbox düzenini kullanır. Menü öğelerinde <ul>
gibi dolgu bulunur. Ayrıca, ihtiyacım olmadığı için bu stili kaldırıyorum.
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;
}
Animasyonlar
İletişim kutusu öğeleri, pencereye girip çıktıkları için genellikle canlandırılır. Diyaloglara bu giriş ve çıkışla ilgili destekleyici hareketler eklemek, kullanıcıların kendilerini akışa yönlendirmesine yardımcı olur.
Normalde iletişim kutusu yalnızca içinde canlandırılabilir, dışarı çıkmaz. Bunun nedeni, tarayıcının öğedeki display
özelliğini açıp kapatmasıdır. Daha önce kılavuz, ekranı ızgara olarak ayarlar ve hiçbir zaman "yok" olarak ayarlamaz. Bu sayede içeride ve dışarıya
animasyonlar eklenebilir.
Open Props, düzenlemeyi kolay ve okunabilir hale getiren birçok animasyon karesi animasyonu ile birlikte gelir. Benim kullandığım animasyon hedefleri ve katmanlı yaklaşımı şöyle:
- Azaltılmış hareket, varsayılan geçiştir; basit bir opaklık belirir ve kaybolur.
- Hareket uygunsa kaydırma ve ölçek animasyonları eklenir.
- Mega iletişim kutusunun duyarlı mobil düzeni, dışarı kayacak şekilde ayarlanmıştır.
Güvenli ve anlamlı bir varsayılan geçiş
Open Props'ta belirme ve kaybolma için animasyon kareleri sağlansa da, bu katmanlı geçiş yaklaşımını animasyon karesi animasyonlarıyla varsayılan olarak kullanmayı tercih ediyorum. İletişim kutusunun görünürlüğünü opaklık ile önceden biçimlendirmiştik. [open]
özelliğine bağlı olarak 1
veya 0
öğelerini düzenledik. %0 ile %100 arasında geçiş için tarayıcıya ne kadar süre ve ne tür bir yumuşatma istediğinizi söyleyin:
dialog {
transition: opacity .5s var(--ease-3);
}
Geçişe hareket ekleme
Kullanıcının hareket konusunda bir sıkıntısı yoksa hem mega hem de mini iletişim kutuları giriş olarak yukarı kaymalı ve çıkış olarak ölçek genişletilmelidir. Bunu prefers-reduced-motion
medya sorgusu ve birkaç Open Props ile yapabilirsiniz:
@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;
}
}
Çıkış animasyonunu mobil için uyarlama
Stil bölümünün önceki bölümlerinde mobil cihazlar için uyarlanan mega diyalog stili, sanki ekranın altından yukarı doğru kayan ve hâlâ alta bir sabitlenmiş gibi bir işlem sayfasına benzer. Ölçeklenen çıkış animasyonu, bu yeni tasarıma pek uymuyor. Bunu birkaç medya sorgusu ve bazı Open Props ile uyarlayabiliriz:
@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
JavaScript ile eklenecek çok sayıda şey vardır:
// 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
}
Bu eklemeler ışığın kapatılması isteğinden (iletişim kutusu arka planını tıklama), animasyondan ve form verilerini almanın daha iyi zamanlanması için bazı ek etkinliklerden kaynaklanmaktadır.
Işık kapatma özelliği ekleniyor
Bu görev basittir ve animasyonlu olmayan bir iletişim öğesi için mükemmel bir katkı sağlar. Etkileşim, iletişim öğesi tıklamaları izlenerek ve tıklanan öğeyi değerlendirmek için etkinlik baloncuklarından yararlanılarak gerçekleştirilir ve yalnızca en üstteki öğe olduğunda close()
işlemi gerçekleşir:
export default async function (dialog) {
dialog.addEventListener('click', lightDismiss)
}
const lightDismiss = ({target:dialog}) => {
if (dialog.nodeName === 'DIALOG')
dialog.close('dismiss')
}
Bildirim dialog.close('dismiss')
. Etkinlik çağrılır ve bir dize sağlanır.
Bu dize, iletişim kutusunun nasıl kapatıldığına dair bilgi almak için diğer JavaScript'ler tarafından alınabilir. Ayrıca, uygulamama kullanıcı etkileşimi hakkında bağlam sağlamak için işlevi çeşitli düğmelerden her çağırdığımda yakın dizeler sağladım.
Kapanış ve kapanış etkinlikleri ekleme
İletişim öğesi bir kapatma etkinliğiyle gelir: İletişim kutusu close()
işlevi çağrıldığı anda yayılır. Bu öğeye animasyon uyguladığımızdan, animasyondan önceki ve sonraki etkinliklerin verilerini almak veya iletişim kutusunu sıfırlamak için
bir değişiklik yapılması yararlı olur. Bunu kapalı iletişim kutusuna eklenen inert
özelliğini yönetmek için kullanıyorum. Kullanıcı yeni bir resim gönderdiğinde ise demoda avatar listesini değiştirmek için bu özelliği kullanıyorum.
Bunun için closing
ve closed
adında iki yeni etkinlik oluşturun. Daha sonra, iletişim kutusunda yerleşik kapatma etkinliğini dinleyin. Buradan iletişim kutusunu inert
olarak ayarlayıp closing
etkinliğini dağıtın. Bir sonraki görev, animasyonların ve geçişlerin iletişim kutusundaki çalışmasının tamamlanmasını bekleyip closed
etkinliğini dağıtmaktır.
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))
Durum hata bildirimi bileşeni oluşturma'da da kullanılan animationsComplete
işlevi, animasyon ve geçiş vaatlerinin tamamlanmasına dayalı bir söz döndürür. Bu nedenle dialogClose
, eş zamansız işlev olur. Ardından, sözü await
döndürüp güvenle kapalı etkinliğe devam edebilir.
Açılış ve açılan etkinlik ekleme
Yerleşik iletişim kutusu öğesi kapatmada olduğu gibi açık bir etkinlik sağlamadığından bu etkinlikleri eklemek kolay değildir. İletişim kutusunun özniteliklerinin değişimi hakkında detaylı bilgi sağlamak için MutationObserver kullanıyorum. Bu gözlemcide, açık özellikte yapılan değişiklikleri izler ve özel etkinlikleri buna göre yönetirim.
Kapanış ve kapanış etkinliklerini başlattığımıza benzer şekilde, opening
ve opened
adında iki yeni etkinlik oluşturun. Daha önce iletişim kutusu kapatma etkinliğini dinlediğimiz yerde, bu kez iletişim kutusunun özelliklerini izlemek için oluşturulmuş bir mutasyon gözlemleyicisi kullanılır.
…
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)
}
})
})
İletişim kutusu özellikleri değiştirildiğinde mutasyon gözlemleyici geri çağırma işlevi çağrılır ve değişikliklerin listesi bir dizi olarak sunulur. Özellik değişikliklerini tekrarlayarak attributeName
öğesinin açık olup olmadığını kontrol edin. Daha sonra, öğenin özelliğe sahip olup olmadığını kontrol edin: Bu bilgi, iletişim kutusunun açılıp açılmadığını bildirir. Açılmışsa inert
özelliğini kaldırın ve odağı, autofocus
isteğinde bulunan bir öğeye veya iletişim kutusunda bulunan ilk button
öğesine ayarlayın. Son olarak, kapatma ve kapatma etkinliğine benzer şekilde, açılış etkinliğini hemen dağıtın, animasyonların tamamlanmasını bekleyin, ardından açılan etkinliği dağıtın.
Kaldırılan bir etkinliği ekleme
Tek sayfalık uygulamalarda iletişim kutuları genellikle rotalara veya diğer uygulama ihtiyaçlarına ve durumuna göre eklenir ve kaldırılır. Bir iletişim kutusu kaldırıldığında etkinliklerin veya verilerin temizlenmesi faydalı olabilir.
Bunu başka bir mutasyon gözlemleyiciyle yapabilirsiniz. Bu kez, bir iletişim öğesindeki özellikleri gözlemlemek yerine, gövde öğesinin alt öğelerini gözlemleyip iletişim öğelerinin kaldırılmasını izleyeceğiz.
…
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)
}
})
})
})
Belgenin gövdesinden alt öğeler eklendiğinde veya kaldırıldığında mutasyon gözlemleyici geri çağırması çağrılır. İzlenen belirli mutasyonlar,
nodeName
iletişim kutusuna sahip olan
removedNodes
içindir. Bir iletişim kutusu kaldırıldıysa bellekte yer açmak için tıklama ve kapatma etkinlikleri kaldırılır ve özel kaldırılan etkinlik gönderilir.
Yükleme özelliğini kaldırma
İletişim kutusu animasyonunun sayfaya eklendiğinde veya sayfa yüklenirken çıkış animasyonunu oynatmasını önlemek için iletişim kutusuna bir yükleme özelliği eklenmiştir. Aşağıdaki komut dosyası, iletişim kutusu animasyonlarının tamamlanmasını bekler ve daha sonra özelliği kaldırır. Artık iletişim kutusunda animasyon eklemek ve çıkış yapmak serbesttir. Dikkat dağıtıcı bir animasyonu etkili bir şekilde gizledik.
export default async function (dialog) {
…
await animationsComplete(dialog)
dialog.removeAttribute('loading')
}
Sayfa yüklenirken animasyon karesi animasyonlarını engelleme sorunu hakkında daha fazla bilgiyi buradan edinebilirsiniz.
Hepsi bir arada
Her bölümü tek tek açıkladığımıza göre dialog.js
öğesinin tamamını aşağıda görebilirsiniz:
// 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')
}
dialog.js
modülünü kullanma
Modülden dışa aktarılan işlevin çağrılması ve bu yeni etkinliklerin ve işlevlerin eklenmesini isteyen bir iletişim öğesi geçirilmesi beklenir:
import GuiDialog from './dialog.js'
const MegaDialog = document.querySelector('#MegaDialog')
const MiniDialog = document.querySelector('#MiniDialog')
GuiDialog(MegaDialog)
GuiDialog(MiniDialog)
Bu şekilde, iki iletişim kutusu ışık kapatma, animasyon yükleme düzeltmeleri ve çalışabilecek daha fazla etkinlikle yeni sürüme geçirildi.
Yeni özel etkinlikleri dinleme
Yeni sürüme geçirilen her iletişim öğesi artık aşağıdaki gibi beş yeni etkinliği dinleyebilir:
MegaDialog.addEventListener('closing', dialogClosing)
MegaDialog.addEventListener('closed', dialogClosed)
MegaDialog.addEventListener('opening', dialogOpening)
MegaDialog.addEventListener('opened', dialogOpened)
MegaDialog.addEventListener('removed', dialogRemoved)
Aşağıda, bu etkinliklerin ele alınmasına dair iki örnek verilmiştir:
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()
}
}
İletişim öğesiyle oluşturduğum demoda, listeye yeni bir avatar öğesi eklemek için bu kapalı etkinliği ve form verilerini kullanıyorum. Zamanlama, iletişim kutusunun çıkış animasyonunu tamamladığı ve ardından bazı komut dosyaları yeni avatarla canlandırıldığı için iyidir. Yeni etkinlikler sayesinde kullanıcı deneyimini daha sorunsuz şekilde düzenleyebilirsiniz.
Uyarı dialog.returnValue
: Bu, close()
iletişim kutusu etkinliği çağrıldığında iletilen kapatma dizesini içerir. dialogClosed
etkinliğinde iletişimin kapatılıp kapatılmadığını, iptal edilip edilmediğini veya onaylanıp onaylanmadığını öğrenmek çok önemlidir. Onaylanırsa komut dosyası, form değerlerini alır ve formu sıfırlar. Sıfırlama işlemi, iletişim kutusu tekrar gösterildiğinde boş ve yeni bir gönderim için hazır olması açısından yararlıdır.
Sonuç
Nasıl yaptığımı artık bildiğine göre siz de nasıl yapardınız? 🙂
Yaklaşımlarımızı çeşitlendirelim ve web'de geliştirme yapmanın tüm yollarını öğrenelim.
Bir demo oluşturun, bana tweet atın bağlantıları, aşağıdaki topluluk remiksleri bölümüne ekleyeceğim.
Topluluk remiksleri
- 3'ü 1 arada iletişim kutusu ile @GrimLink.
display
özelliğini değiştirmeyen güzel bir remiks ile @mikemai2awesome.- @geoffrich_ ile Svelte ve güzel bir Svelte FLIP.
Kaynaklar
- GitHub'daki kaynak kodu
- Doodle Avatarları