Yayınlanma tarihi: 7 Mart 2025
Düğmeler, dinamik web uygulamaları oluşturmak için gereklidir. Düğmeler menüleri açar, işlemleri açar/kapatır ve formları gönderir. Bunlar, web'de etkileşimin temelini oluşturur. Düğmeleri basit ve erişilebilir hale getirmek bazı şaşırtıcı zorluklara yol açabilir. Mikro ön uç veya bileşen sistemleri oluşturan geliştiriciler, gereğinden daha karmaşık hale gelen çözümlerle karşılaşabilir. Çerçeveler yardımcı olsa da web bu konuda daha fazlasını yapabilir.
Chrome 135, yeni command
ve commandfor
özellikleriyle açıklayıcı davranış sağlamak için yeni özellikler sunar, popovertargetaction
ve popovertarget
özelliklerini geliştirir ve değiştirir. Bu yeni özellikler düğmelere eklenebilir. Böylece tarayıcı, basitlik ve erişilebilirlikle ilgili bazı temel sorunları giderebilir ve yerleşik ortak işlevler sunabilir.
Geleneksel desenler
Bir çerçeve olmadan düğme davranışları oluşturmak, üretim kodu geliştikçe bazı ilginç zorluklar ortaya çıkarabilir. HTML, düğmeler için onclick
işleyicileri sunsa da İçerik Güvenliği Politikası (CSP) kuralları nedeniyle bunlara genellikle demolar veya eğitici içerikler dışında izin verilmez. Bu etkinlikler düğme öğelerine gönderilir. Düğmeler genellikle bir sayfaya yerleştirilerek diğer öğeleri kontrol eder. Bu da aynı anda iki öğeyi kontrol etmek için kod gerektirir. Ayrıca, bu etkileşimin yardımcı teknoloji kullanıcıları tarafından erişilebilir olduğundan emin olmanız gerekir. Bu genellikle kodun şu şekilde görünmesine neden olur:
<div class="menu-wrapper">
<button class="menu-opener" aria-expanded="false">
Open Menu
</button>
<div popover class="menu-content">
<!-- ... -->
</div>
</div>
<script type="module">
document.addEventListener('click', e => {
const button = e.target;
if (button.matches('.menu-opener')) {
const menu = button
.closest('.menu-wrapper')
.querySelector('.menu-content');
if (menu) {
button.setAttribute('aria-expanded', 'true');
menu.showPopover();
menu.addEventListener('toggle', e => {
// reset back to aria-expanded=false on close
if (e.newState == 'closed') {
button.setAttribute('aria-expanded', 'false');
}
}, {once: true})
}
}
});
</script>
Bu yaklaşım biraz kırılgan olabilir ve çerçeveler ergonomiyi iyileştirmeyi amaçlar. React gibi bir çerçevede yaygın olarak kullanılan bir kalıp, tıklamayı bir durum değişikliğiyle eşlemeyi içerebilir:
function MyMenu({ children }) {
const [isOpen, setIsOpen] = useState(false);
const open = useCallback(() => setIsOpen(true), []);
const handleToggle = useCallback((e) => {
// popovers have light dismiss which influences our state
setIsOpen(e.newState === 'open')
}, []);
const popoverRef = useRef(null);
useEffect(() => {
if (popoverRef.current) {
if (isOpen) {
popoverRef.current.showPopover();
} else {
popoverRef.current.hidePopover();
}
}
}, [popoverRef, isOpen]);
return (
<>
<button onClick={open} aria-expanded={isOpen}>
Open Menu
</button>
<div popover onToggle={handleToggle} ref={popoverRef}>
{children}
</div>
</>
);
}
Diğer birçok çerçeve de benzer bir ergonomi sunmayı amaçlar. Örneğin, bu kod AlpineJS'de şu şekilde yazılabilir:
<div x-data="{open: false}">
<button @click="open = !open; $refs.popover.showPopover()" :aria-expanded="open">
Open Menu
</button>
<div popover x-ref="popover" @toggle="open = $event.newState === 'open'">
<!-- ... -->
</div>
</div>
Svelte'te bu kod şöyle görünebilir:
<script>
let popover;
let open = false;
function togglePopover() {
open ? popover.hidePopover() : popover.showPopover();
open = !open;
}
</script>
<button on:click={togglePopover} aria-expanded={open}>
Open Menu
</button>
<div bind:this={popover} popover>
<!-- ... -->
</div>
Bazı tasarım sistemleri veya kitaplıkları, durum değişikliklerini kapsayan düğme öğelerinin etrafına sarmalayıcılar ekleyerek bir adım daha ileri gidebilir. Bu, tetikleyici bileşeninin arkasındaki durum değişikliklerini soyutlar ve daha iyi ergonomi için biraz esneklik sağlar:
import {MenuTrigger, MenuContent} from 'my-design-system';
function MyMenu({children}) {
return (
<MenuTrigger>
<button>Open Menu</button>
</MenuTrigger>
<MenuContent>{children}</MenuContent>
);
}
Komut ve commandfor kalıbı
command
ve commandfor
özellikleri sayesinde düğmeler artık diğer öğelerde işlemleri açık bir şekilde gerçekleştirebilir. Böylece, esneklikten ödün vermeden bir çerçevenin ergonomisini sağlayabilirsiniz. commandfor
düğmesi, for
özelliğine benzer şekilde bir kimlik alır. command
ise yerleşik değerleri kabul ederek daha taşınabilir ve sezgisel bir yaklaşım sağlar.
Örnek: command ve commandfor içeren açık bir menü düğmesi
Aşağıdaki HTML, düğme ile menü arasında açıklayıcı ilişkiler oluşturur. Bu sayede mantık ve erişilebilirlik tarayıcı tarafından sizin için yönetilir. aria-expanded
öğesini yönetmeniz veya başka JavaScript eklemeniz gerekmez.
<button commandfor="my-menu" command="show-popover">
Open Menu
</button>
<div popover id="my-menu">
<!-- ... -->
</div>
command
ve commandfor
ile popovertargetaction
ve popovertarget
'i karşılaştırma
Daha önce popover
kullandıysanız popovertarget
ve popovertargetaction
özelliklerini biliyor olabilirsiniz. Bunlar, commandfor
ve command
'e benzer şekilde çalışır ancak pop-up'lara özeldir. command
ve commandfor
özellikleri, bu eski özelliklerin yerini tamamen alır. Yeni özellikler, eski özelliklerin tümünü destekler ve yeni özellikler ekler.
Yerleşik komutlar
command
özelliği, etkileşimli öğeler için çeşitli API'lerle eşleşen bir dizi yerleşik davranışa sahiptir:
show-popover
:el.showPopover()
ile eşlenir.hide-popover
:el.hidePopover()
ile eşlenir.toggle-popover
:el.togglePopover()
ile eşlenir.show-modal
:dialogEl.showModal()
ile eşlenir.close
:dialogEl.close()
ile eşlenir.
Bu komutlar, JavaScript eşdeğerleriyle eşlenir ve erişilebilirliği (ör. aria-details
ve aria-expanded
eşdeğer ilişkilerini sağlama), odak yönetimini ve daha fazlasını kolaylaştırır.
Örnek: command
ve commandfor
içeren bir onay iletişim kutusu
<button commandfor="confirm-dialog" command="show-modal">
Delete Record
</button>
<dialog id="confirm-dialog">
<header>
<h1>Delete Record?</h1>
<button commandfor="confirm-dialog" command="close" aria-label="Close" value="close">
<img role="none" src="/close-icon.svg">
</button>
</header>
<p>Are you sure? This action cannot be undone</p>
<footer>
<button commandfor="confirm-dialog" command="close" value="cancel">
Cancel
</button>
<button commandfor="confirm-dialog" command="close" value="delete">
Delete
</button>
</footer>
</dialog>
Kaydı Sil düğmesini tıkladığınızda iletişim kutusu modal olarak açılır. Kapat, İptal veya Sil düğmelerini tıkladığınızda ise iletişim kutusu kapatılır ve düğmenin değeriyle eşleşen bir returnValue
mülkü içeren bir "close"
etkinliği gönderilir.
Bu sayede, bir sonraki işlemi belirlemek için iletişim kutusunda tek bir etkinlik dinleyicinin ötesinde JavaScript'e ihtiyaç duyulmaz:
dialog.addEventListener("close", (event) => {
if (event.target.returnValue == "cancel") {
console.log("cancel was clicked");
} else if (event.target.returnValue == "close") {
console.log("close was clicked");
} else if (event.target.returnValue == "delete") {
console.log("delete was clicked");
}
});
Özel komutlar
Yerleşik komutlara ek olarak, --
ön ekiyle özel komutlar da tanımlayabilirsiniz. Özel komutlar, hedef öğede "command"
etkinliği gönderir (tıpkı yerleşik komutlar gibi), ancak yerleşik değerler gibi hiçbir zaman ek mantık gerçekleştirmez. Bu, sarmalayıcı bileşenler sağlamak, hedef öğe için DOM'u taramak veya düğme tıklamalarını durum değişiklikleriyle eşlemek zorunda kalmadan düğmelere çeşitli şekillerde tepki verebilecek bileşenler oluşturma esnekliği sağlar. Bu sayede bileşenleriniz için HTML'de bir API sağlayabilirsiniz:
<button commandfor="the-image" command="--rotate-landscape">
Landscape
</button>
<button commandfor="the-image" command="--rotate-portrait">
Portrait
</button>
<img id="the-image" src="photo.jpg">
<script type="module">
const image = document.getElementById("the-image");
image.addEventListener("command", (event) => {
if ( event.command == "--rotate-landscape" ) {
image.style.rotate = "-90deg"
} else if ( event.command == "--rotate-portrait" ) {
image.style.rotate = "0deg"
}
});
</script>
ShadowDOM'daki komutlar
commandfor
özelliği bir kimlik aldığından, gölge DOM'u geçmeyle ilgili kısıtlamalar vardır. Bu tür durumlarda, gölge kökleri genelinde herhangi bir öğeyi ayarlayabilen .commandForElement
mülkünü ayarlamak için JavaScript API'yi kullanabilirsiniz:
<my-element>
<template shadowrootmode=open>
<button command="show-popover">Show popover</button>
<slot></slot>
</template>
<div popover><!-- ... --></div>
</my-element>
<script>
customElements.define("my-element", class extends HTMLElement {
connectedCallback() {
const popover = this.querySelector('[popover]');
// The commandForElement can set cross-shadow root elements.
this.shadowRoot.querySelector('button').commandForElement = popover;
}
});
</script>
Gelecekteki teklifler, Referans Hedef Teklifi gibi gölge sınırlar arasında referansları paylaşmanın açık bir yolunu sunabilir.
Sırada ne var?
Web sitelerinin kullandığı ortak işlevleri kapsayacak yeni yerleşik komutlar için olasılıkları keşfetmeye devam edeceğiz. Önerilen fikirler Açık Kullanıcı Arayüzü Önerisi bölümünde ele alınmıştır. Daha önce keşfedilen fikirlerden bazıları:
<details>
öğelerini açma ve kapatma.<input>
ve<select>
öğeleri içinshowPicker()
ile eşlenen bir"show-picker"
komutu.<video>
ve<audio>
öğeleri için oynatma komutları.- Öğelerdeki metin içeriğini kopyalama.
Topluluktan gelen geri bildirimleri memnuniyetle karşılıyoruz. Önerileriniz varsa Open UI Issue Tracker'da sorun kaydı oluşturabilirsiniz.
Daha fazla bilgi
command
ve commandfor
hakkında daha fazla bilgiyi spesifikasyonda ve MDN'de bulabilirsiniz.