Saya memiliki beberapa HTML menu, yang saya lihat benar-benar ketika pengguna mengklik pada kepala dari menu ini. Saya ingin menyembunyikan elemen-elemen ini ketika pengguna mengklik luar menu' daerah.
Adalah sesuatu seperti ini mungkin dengan jQuery?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
CATATAN: Menggunakan
stopEventPropagation()
adalah sesuatu yang harus dihindari, istirahat normal acara arus dalam DOM. Lihat artikel ini untuk informasi lebih lanjut. Pertimbangkan untuk menggunakan metode bukan
Melampirkan acara klik untuk dokumen tubuh yang menutup jendela. Melampirkan terpisah acara klik untuk kontainer yang berhenti propagasi untuk dokumen tubuh.
$(window).click(function() {
//Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
Anda dapat mendengarkan klik acara dokumen
dan kemudian pastikan #menucontainer
tidak leluhur atau target yang diklik elemen dengan menggunakan .terdekat()
.
Jika tidak, maka elemen yang diklik adalah di luar dari #menucontainer
dan anda dapat dengan aman menyembunyikannya.
$(document).click(function(event) {
$target = $(event.target);
if(!$target.closest('#menucontainer').length &&
$('#menucontainer').is(":visible")) {
$('#menucontainer').hide();
}
});
Anda juga dapat membersihkan setelah acara pendengar jika anda berencana untuk memberhentikan menu dan ingin berhenti mendengarkan acara. Fungsi ini akan membersihkan hanya baru dibuat pendengar, melestarikan lainnya klik pendengar pada dokumen
. Dengan ES2015 sintaks:
export function hideOnClickOutside(selector) {
const outsideClickListener = (event) => {
$target = $(event.target);
if (!$target.closest(selector).length && $(selector).is(':visible')) {
$(selector).hide();
removeClickListener();
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
Bagi mereka yang don't ingin menggunakan jQuery. Berikut ini's kode di atas di depan vanillaJS (ECMAScript6).
function hideOnClickOutside(element) {
const outsideClickListener = event => {
if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
element.style.display = 'none'
removeClickListener()
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ) // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
CATATAN:
Hal ini didasarkan pada Alex komentar untuk hanya menggunakan !elemen.berisi(event.target)
bukan jQuery bagian.
Tapi elemen.terdekat()
sekarang juga tersedia di semua browser utama (W3C versi berbeda sedikit dari jQuery satu).
Polyfills dapat ditemukan di sini: Elemen.terdekat()
Cara mendeteksi klik di luar unsur?
Alasan bahwa pertanyaan ini begitu populer dan belum begitu banyak jawaban adalah bahwa hal itu tampak kompleks. Setelah hampir delapan tahun dan puluhan jawaban, saya benar-benar terkejut melihat betapa sedikit perawatan yang telah diberikan untuk aksesibilitas.
saya ingin menyembunyikan elemen-elemen ini ketika pengguna mengklik luar menu' daerah.
Ini adalah tujuan mulia dan aktual masalah. Judul pertanyaan—yang adalah apa yang kebanyakan jawaban yang muncul untuk mencoba untuk alamat—berisi malang red herring.
Petunjuk: itu's dengan kata "klik"!
Jika anda're mengikat klik penangan untuk menutup dialog ini, anda've sudah gagal. Alasan anda've gagal adalah bahwa tidak semua orang pemicu klik
acara. Pengguna tidak menggunakan mouse akan dapat melarikan diri anda dialog (dan pop-up menu ini bisa dibilang jenis dialog) dengan menekan Tab, dan mereka kemudian memenangkan't dapat membaca konten di balik dialog tanpa kemudian memicu klik
acara.
Jadi let's ulangi pertanyaannya.
Bagaimana cara menutup dialog ketika pengguna selesai dengan itu?
Ini adalah tujuan. Sayangnya, sekarang kita perlu untuk mengikat userisfinishedwiththedialog
acara, dan yang mengikat isn't begitu mudah.
Jadi bagaimana kita bisa mendeteksi bahwa user telah selesai menggunakan dialog?
focusout
acaraAwal yang baik adalah untuk menentukan jika fokus kiri dialog.
Petunjuk: hati-hati dengan blur
acara blur
doesn't menyebarkan jika acara ini terikat untuk menggelegak fase!
jQuery's focusout
akan baik-baik saja. Jika anda dapat't menggunakan jQuery, maka anda dapat menggunakan blur
selama menangkap fase:
element.addEventListener('blur', ..., true);
// use capture: ^^^^
Juga, selama bertahun-dialog anda'll perlu untuk memungkinkan wadah untuk mendapatkan fokus. Tambahkan tabindex="-1"
untuk memungkinkan dialog untuk menerima fokus secara dinamis tanpa jika tidak mengganggu tabbing aliran.
$('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>
Jika anda bermain dengan demo itu selama lebih dari satu menit anda harus dengan cepat mulai melihat masalah.
Yang pertama adalah bahwa link dalam dialog isn't diklik. Mencoba untuk klik di atasnya atau tab untuk itu akan mengarah pada dialog penutupan sebelum interaksi berlangsung. Hal ini karena fokus dalam elemen memicu focusout
acara sebelum memicu focusin
event lagi.
Perbaikan untuk antrian negara perubahan pada loop acara. Hal ini dapat dilakukan dengan menggunakan setImmediate(...)
, atau setTimeout(..., 0)
untuk browser yang don't dukungan setImmediate
. Setelah antri itu dapat dibatalkan oleh berikutnya 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>
Masalah kedua adalah bahwa dialog tidak't dekat ketika link ditekan lagi. Hal ini karena dialog kehilangan fokus, memicu penutupan perilaku, setelah klik link memicu dialog untuk membuka kembali.
Mirip dengan masalah sebelumnya, fokus negara perlu dikelola. Mengingat bahwa negara perubahan sudah antri, it's hanya masalah penanganan fokus pada acara dialog pemicu:
$('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>
Jika anda pikir anda adalah yang dilakukan oleh penanganan yang fokus serikat, ada's lebih banyak yang dapat anda lakukan untuk menyederhanakan pengalaman pengguna.
Hal ini sering "bagus untuk memiliki" fitur, tetapi itu's umum bahwa ketika anda memiliki modal atau popup apapun yang Spasi kunci akan menutupnya.
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>
Jika anda tahu anda memiliki focusable unsur-unsur dalam dialog ini, anda tidak't perlu fokus dialog langsung. Jika anda're gedung menu, anda bisa fokus pertama item menu sebagai gantinya.
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.
Jawaban ini mudah-mudahan mencakup dasar-dasar yang dapat diakses oleh keyboard dan mouse dukungan untuk fitur ini, tapi sebagai it's sudah cukup cukup aku'm akan untuk menghindari pembahasan WAI-ARIA peran dan atribut, namun saya sangat merekomendasikan bahwa pelaksana lihat spec untuk rincian tentang peran apa yang harus mereka gunakan dan setiap atribut yang sesuai.
Saya memiliki sebuah aplikasi yang bekerja sama dengan Eran's contoh, kecuali saya lampirkan acara klik untuk tubuh ketika aku membuka menu... seperti ini:
$('#menucontainer').click(function(event) {
$('html').one('click',function() {
// Hide the menus
});
event.stopPropagation();
});
Informasi lebih lanjut tentang jQuery's satu()
fungsi
Setelah penelitian, saya telah menemukan tiga solusi bekerja (saya lupa halaman link untuk referensi)
<script>
//The good thing about this solution is it doesn't stop event propagation.
var clickFlag = 0;
$('body').on('click', function () {
if(clickFlag == 0) {
console.log('hide element here');
/* Hide element here */
}
else {
clickFlag=0;
}
});
$('body').on('click','#testDiv', function (event) {
clickFlag = 1;
console.log('showed the element');
/* Show the element */
});
</script>
<script>
$('body').on('click', function(e) {
if($(e.target).closest('#testDiv').length == 0) {
/* Hide dropdown here */
}
});
</script>
<script>
var specifiedElement = document.getElementById('testDiv');
document.addEventListener('click', function(event) {
var isClickInside = specifiedElement.contains(event.target);
if (isClickInside) {
console.log('You clicked inside')
}
else {
console.log('You clicked outside')
}
});
</script>
Sekarang ada plugin untuk itu: luar acara (blog post)
Berikut ini terjadi ketika clickoutside handler (WLOG) adalah terikat untuk suatu elemen:
Jadi tidak ada acara berhenti dari dakwah dan tambahan klik penangan dapat digunakan "di" elemen dengan luar-handler.
Saya don't pikir apa yang benar-benar anda butuhkan adalah untuk menutup menu ketika user mengklik luar; yang anda butuhkan adalah untuk menu untuk menutup ketika pengguna mengklik di mana saja di semua halaman. Jika anda klik pada menu, atau dari menu itu harus dekat kan?
Tidak menemukan jawaban yang memuaskan atas mendorong saya untuk menulis posting blog ini hari yang lain. Untuk lebih bertele-tele, ada sejumlah gotchas untuk mencatat:
body { margin-left:auto; margin-right: auto; width:960px;}
Sebagai poster lain mengatakan ada banyak yang perlu diperhatikan, terutama jika elemen anda menampilkan (dalam hal ini menu) memiliki elemen interaktif. I've ditemukan metode berikut untuk menjadi cukup kuat:
$('#menuscontainer').click(function(event) {
//your code that shows the menus fully
//now set up an event listener so that clicking anywhere outside will close the menu
$('html').click(function(event) {
//check up the tree of the click target to check whether user has clicked outside of menu
if ($(event.target).parents('#menuscontainer').length==0) {
// your code to hide menu
//this event listener has done its job so we can unbind it.
$(this).unbind(event);
}
})
});
Sebuah solusi sederhana untuk situasi ini:
$(document).mouseup(function (e)
{
var container = $("YOUR SELECTOR"); // Give you class or ID
if (!container.is(e.target) && // If the target of the click is not the desired div or section
container.has(e.target).length === 0) // ... nor a descendant-child of the container
{
container.hide();
}
});
Script di atas akan menyembunyikan div
jika di luar div
klik peristiwa dipicu.
Anda dapat melihat blog berikut ini untuk informasi lebih lanjut : http://www.codecanal.com/detect-click-outside-div-using-javascript/
Alih-alih menggunakan acara tersebut.stopPropagation() yang dapat mempengaruhi sisi, hanya mendefinisikan sederhana bendera variabel dan tambahkan satu jika
kondisi. Ini saya diuji dan bekerja dengan baik tanpa ada sisi mempengaruhi stopPropagation:
var flag = "1";
$('#menucontainer').click(function(event){
flag = "0"; // flag 0 means click happened in the area where we should not do any action
});
$('html').click(function() {
if(flag != "0"){
// Hide the menus if visible
}
else {
flag = "1";
}
});
Hanya dengan sederhana jika
kondisi:
$(document).on('click', function(event){
var container = $("#menucontainer");
if (!container.is(event.target) && // If the target of the click isn't the container...
container.has(event.target).length === 0) // ... nor a descendant of the container
{
// Do whatever you want to do when click is outside the element
}
});
Memeriksa jendela acara klik target (harus menyebarkan ke jendela, asalkan itu's tidak ditangkap di tempat lain), dan memastikan bahwa itu's tidak ada menu elemen. Jika itu's tidak, maka anda're luar menu anda.
Atau memeriksa posisi klik, dan melihat apakah itu's yang terkandung dalam menu harian.
I've telah sukses dengan sesuatu seperti ini:
var $menuscontainer = ...;
$('#trigger').click(function() {
$menuscontainer.show();
$('body').click(function(event) {
var $target = $(event.target);
if ($target.parents('#menuscontainer').length == 0) {
$menuscontainer.hide();
}
});
});
Logika adalah: ketika #menuscontainer
ditampilkan, mengikat handler klik untuk tubuh yang menyembunyikan #menuscontainer
hanya jika target (klik) isn't anak itu.
Sebagai varian:
var $menu = $('#menucontainer');
$(document).on('click', function (e) {
// If element is opened and click target is outside it, hide it
if ($menu.is(':visible') && !$menu.is(e.target) && !$menu.has(e.target).length) {
$menu.hide();
}
});
Ia tidak memiliki masalah dengan menghentikan acara propagasi dan lebih baik mendukung beberapa menu pada halaman yang sama di mana mengklik pada menu kedua, sedangkan yang pertama adalah pembukaan akan meninggalkan terbuka pertama di stopPropagation solusi.
Saya menemukan metode ini dalam beberapa kalender jQuery plugin.
function ClickOutsideCheck(e)
{
var el = e.target;
var popup = $('.popup:visible')[0];
if (popup==undefined)
return true;
while (true){
if (el == popup ) {
return true;
} else if (el == document) {
$(".popup").hide();
return false;
} else {
el = $(el).parent()[0];
}
}
};
$(document).bind('mousedown.popup', ClickOutsideCheck);
Acara ini memiliki properti yang disebut acara.jalur dari elemen yang merupakan "statis daftar memerintahkan semua nenek moyangnya di pohon order". Untuk memeriksa apakah sebuah acara yang berasal dari tertentu DOM elemen atau salah satu dari anak-anak, hanya memeriksa jalan bagi yang spesifik elemen DOM. Hal ini juga dapat digunakan untuk memeriksa beberapa elemen dengan logis ATAU a'ing elemen cek di
beberapa` fungsi.
$("body").click(function() {
target = document.getElementById("main");
flag = event.path.some(function(el, i, arr) {
return (el == target)
})
if (flag) {
console.log("Inside")
} else {
console.log("Outside")
}
});
#main {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
<ul>
<li>Test-Main</li>
<li>Test-Main</li>
<li>Test-Main</li>
<li>Test-Main</li>
<li>Test-Main</li>
</ul>
</div>
<div id="main2">
Outside Main
</div>
Jadi untuk kasus anda Itu harus
$("body").click(function() {
target = $("#menuscontainer")[0];
flag = event.path.some(function(el, i, arr) {
return (el == target)
});
if (!flag) {
// Hide the menus
}
});
Berikut ini adalah vanilla JavaScript solusi untuk masa depan pemirsa.
Setelah mengklik setiap elemen dalam dokumen, jika diklik elemen's id diaktifkan, atau elemen tersembunyi yang tidak tersembunyi dan disembunyikan elemen tidak mengandung elemen yang diklik, beralih elemen.
(function () {
"use strict";
var hidden = document.getElementById('hidden');
document.addEventListener('click', function (e) {
if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
}, false);
})();
(function () {
"use strict";
var hidden = document.getElementById('hidden');
document.addEventListener('click', function (e) {
if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
}, false);
})();
<a href="javascript:void(0)" id="toggle">Toggle Hidden Div</a>
<div id="hidden" style="display: none;">This content is normally hidden. click anywhere other than this content to make me disappear</div>
Jika anda akan memiliki beberapa matikan pada halaman yang sama, anda dapat menggunakan sesuatu seperti ini:
tersembunyi
untuk dilipat item.(function () {
"use strict";
var hiddenItems = document.getElementsByClassName('hidden'), hidden;
document.addEventListener('click', function (e) {
for (var i = 0; hidden = hiddenItems[i]; i++) {
if (!hidden.contains(e.target) && hidden.style.display != 'none')
hidden.style.display = 'none';
}
if (e.target.getAttribute('data-toggle')) {
var toggle = document.querySelector(e.target.getAttribute('data-toggle'));
toggle.style.display = toggle.style.display == 'none' ? 'block' : 'none';
}
}, false);
})();
<a href="javascript:void(0)" data-toggle="#hidden1">Toggle Hidden Div</a>
<div class="hidden" id="hidden1" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden2">Toggle Hidden Div</a>
<div class="hidden" id="hidden2" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden3">Toggle Hidden Div</a>
<div class="hidden" id="hidden3" style="display: none;" data-hidden="true">This content is normally hidden</div>
Saya terkejut tidak ada yang benar-benar diakui focusout
acara:
var button = document.getElementById('button');
button.addEventListener('click', function(e){
e.target.style.backgroundColor = 'green';
});
button.addEventListener('focusout', function(e){
e.target.style.backgroundColor = '';
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<button id="button">Click</button>
</body>
</html>