Bir öğenin dışındaki bir tıklamayı nasıl tespit edebilirim?
Bu sorunun çok popüler olmasının ve çok fazla cevabın olmasının nedeni, aldatıcı bir şekilde karmaşık olmasıdır. Neredeyse sekiz yıl ve onlarca cevaptan sonra erişilebilirliğe ne kadar az özen gösterildiğini görmek beni gerçekten şaşırttı.
Kullanıcı menülerin alanı dışına tıkladığında bu öğeleri gizlemek istiyorum.
Bu asil bir nedendir ve asıl konudur. Sorunun başlığı - çoğu yanıtın ele alınmaya çalıştığı şey - talihsiz bir kırmızı ringa içerir.
İpucu: "tıklama" kelimesi !
Aslında tıklama işleyicilerini bağlamak istemezsiniz.
İletişim kutusunu kapatmak için tıklama işleyicilerini bağlıyorsanız, zaten başarısız oldunuz. Başarısız olmanızın nedeni herkesin click
olayları tetiklememesidir . Fare kullanmayan kullanıcılar, düğmesine basarak iletişim kutusunuzdan (ve açılır menünüzün tartışmasız bir tür iletişim kutusundan) Tabçıkabileceğini ve daha sonra bir click
olayı tetiklemeden iletişim kutusunun arkasındaki içeriği okuyamayacaklardır .
Şimdi soruyu yeniden yazalım.
Kullanıcı bittiği zaman iletişim kutusu nasıl kapatılır?
Amaç bu. Ne yazık ki, şimdi userisfinishedwiththedialog
olayı bağlamamız gerekiyor ve bu bağlanma o kadar basit değil.
Öyleyse bir kullanıcının bir iletişim kutusunu kullanmayı bitirdiğini nasıl tespit edebiliriz?
focusout
Etkinlik
İyi bir başlangıç, odaklamanın iletişim kutusundan ayrılıp ayrılmadığını belirlemektir.
İpucu: blur
etkinliğe dikkat edin, etkinlik blur
köpürme aşamasına bağlıysa yayılmaz!
jQuery focusout
iyi olur. JQuery kullanamıyorsanız blur
, yakalama aşamasında kullanabilirsiniz:
element.addEventListener('blur', ..., true);
// use capture: ^^^^
Ayrıca, birçok iletişim kutusu için kapsayıcının odaklanmasına izin vermeniz gerekir. tabindex="-1"
Sekme akışını aksatmadan iletişim kutusunun odağı dinamik olarak almasına izin vermek için ekleyin .
$('a').on('click', function () {
$(this.hash).toggleClass('active').focus();
});
$('div').on('focusout', function () {
$(this).removeClass('active');
});
div {
display: none;
}
.active {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>
Bu demoda bir dakikadan fazla oynarsanız sorunları hızlı bir şekilde görmeye başlamalısınız.
Birincisi, iletişim kutusundaki bağlantının tıklanabilir olmamasıdır. Üzerine veya sekmesine tıklamaya çalışmak, etkileşim gerçekleşmeden önce iletişim kutusunun kapanmasına neden olur. Bunun nedeni, iç öğeye odaklanmanın focusout
bir focusin
olayı yeniden tetiklemeden önce bir olayı tetiklemesidir .
Düzeltme, olay döngüsünde durum değişikliğini sıralamaktır. Bu, kullanılarak setImmediate(...)
veya setTimeout(..., 0)
desteklenmeyen tarayıcılar için yapılabilir setImmediate
. Kuyruğa alındıktan sonra, bir sonraki işlem tarafından iptal edilebilir focusin
:
$('.submenu').on({
focusout: function (e) {
$(this).data('submenuTimer', setTimeout(function () {
$(this).removeClass('submenu--active');
}.bind(this), 0));
},
focusin: function (e) {
clearTimeout($(this).data('submenuTimer'));
}
});
$('a').on('click', function () {
$(this.hash).toggleClass('active').focus();
});
$('div').on({
focusout: function () {
$(this).data('timer', setTimeout(function () {
$(this).removeClass('active');
}.bind(this), 0));
},
focusin: function () {
clearTimeout($(this).data('timer'));
}
});
div {
display: none;
}
.active {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>
İkinci sorun, bağlantıya tekrar basıldığında iletişim kutusunun kapanmamasıdır. Bunun nedeni iletişim kutusunun odağı kaybetmesi ve yakın davranışı tetiklemesidir, bundan sonra bağlantı tıklaması iletişim kutusunu yeniden açmak için tetikler.
Önceki konuya benzer şekilde, odak durumunun yönetilmesi gerekiyor. Durum değişikliğinin zaten kuyruğa alınmış olduğu göz önüne alındığında, bu sadece diyalog tetikleyicilerindeki odak olaylarını ele almakla ilgilidir:
Bu tanıdık gelmeli
$('a').on({
focusout: function () {
$(this.hash).data('timer', setTimeout(function () {
$(this.hash).removeClass('active');
}.bind(this), 0));
},
focusin: function () {
clearTimeout($(this.hash).data('timer'));
}
});
$('a').on('click', function () {
$(this.hash).toggleClass('active').focus();
});
$('div').on({
focusout: function () {
$(this).data('timer', setTimeout(function () {
$(this).removeClass('active');
}.bind(this), 0));
},
focusin: function () {
clearTimeout($(this).data('timer'));
}
});
$('a').on({
focusout: function () {
$(this.hash).data('timer', setTimeout(function () {
$(this.hash).removeClass('active');
}.bind(this), 0));
},
focusin: function () {
clearTimeout($(this.hash).data('timer'));
}
});
div {
display: none;
}
.active {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>
Esc anahtar
Odak durumlarını ele alarak bittiğini düşünüyorsanız, kullanıcı deneyimini basitleştirmek için yapabileceğiniz daha çok şey var.
Bu genellikle "olması güzel" bir özelliktir, ancak herhangi bir türden bir modal veya pop-up'ınız olduğunda Escanahtarın onu kapatacağı yaygındır .
keydown: function (e) {
if (e.which === 27) {
$(this).removeClass('active');
e.preventDefault();
}
}
$('a').on('click', function () {
$(this.hash).toggleClass('active').focus();
});
$('div').on({
focusout: function () {
$(this).data('timer', setTimeout(function () {
$(this).removeClass('active');
}.bind(this), 0));
},
focusin: function () {
clearTimeout($(this).data('timer'));
},
keydown: function (e) {
if (e.which === 27) {
$(this).removeClass('active');
e.preventDefault();
}
}
});
$('a').on({
focusout: function () {
$(this.hash).data('timer', setTimeout(function () {
$(this.hash).removeClass('active');
}.bind(this), 0));
},
focusin: function () {
clearTimeout($(this.hash).data('timer'));
}
});
div {
display: none;
}
.active {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>
İletişim kutusunda odaklanabilir öğeleriniz olduğunu biliyorsanız, iletişim kutusunu doğrudan odaklamanız gerekmez. Bir menü oluşturuyorsanız, bunun yerine ilk menü öğesine odaklanabilirsiniz.
click: function (e) {
$(this.hash)
.toggleClass('submenu--active')
.find('a:first')
.focus();
e.preventDefault();
}
$('.menu__link').on({
click: function (e) {
$(this.hash)
.toggleClass('submenu--active')
.find('a:first')
.focus();
e.preventDefault();
},
focusout: function () {
$(this.hash).data('submenuTimer', setTimeout(function () {
$(this.hash).removeClass('submenu--active');
}.bind(this), 0));
},
focusin: function () {
clearTimeout($(this.hash).data('submenuTimer'));
}
});
$('.submenu').on({
focusout: function () {
$(this).data('submenuTimer', setTimeout(function () {
$(this).removeClass('submenu--active');
}.bind(this), 0));
},
focusin: function () {
clearTimeout($(this).data('submenuTimer'));
},
keydown: function (e) {
if (e.which === 27) {
$(this).removeClass('submenu--active');
e.preventDefault();
}
}
});
.menu {
list-style: none;
margin: 0;
padding: 0;
}
.menu:after {
clear: both;
content: '';
display: table;
}
.menu__item {
float: left;
position: relative;
}
.menu__link {
background-color: lightblue;
color: black;
display: block;
padding: 0.5em 1em;
text-decoration: none;
}
.menu__link:hover,
.menu__link:focus {
background-color: black;
color: lightblue;
}
.submenu {
border: 1px solid black;
display: none;
left: 0;
list-style: none;
margin: 0;
padding: 0;
position: absolute;
top: 100%;
}
.submenu--active {
display: block;
}
.submenu__item {
width: 150px;
}
.submenu__link {
background-color: lightblue;
color: black;
display: block;
padding: 0.5em 1em;
text-decoration: none;
}
.submenu__link:hover,
.submenu__link:focus {
background-color: black;
color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="menu">
<li class="menu__item">
<a class="menu__link" href="#menu-1">Menu 1</a>
<ul class="submenu" id="menu-1" tabindex="-1">
<li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
<li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
<li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
<li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
</ul>
</li>
<li class="menu__item">
<a class="menu__link" href="#menu-2">Menu 2</a>
<ul class="submenu" id="menu-2" tabindex="-1">
<li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
<li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
<li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
<li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
</ul>
</li>
</ul>
lorem ipsum <a href="http://example.com/">dolor</a> sit amet.
WAI-ARIA Rolleri ve Diğer Erişilebilirlik Desteği
Bu cevap umarım bu özellik için erişilebilir klavye ve fare desteğinin temellerini kapsar, ancak zaten oldukça büyük olduğundan, WAI-ARIA rolleri ve nitelikleri hakkında herhangi bir tartışmadan kaçınacağım , ancak uygulayıcıların ayrıntılar için spesifikasyonlara başvurmasını şiddetle tavsiye ediyorum hangi rolleri kullanmaları gerektiğine ve diğer uygun niteliklere