いくつかのHTMLメニューがあり、ユーザーがこれらのメニューの先頭をクリックすると完全に表示されます。ユーザーがメニュー領域の外側をクリックしたときに、これらの要素を非表示にしたいと思います。
このようなことはjQueryで可能ですか?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
いくつかのHTMLメニューがあり、ユーザーがこれらのメニューの先頭をクリックすると完全に表示されます。ユーザーがメニュー領域の外側をクリックしたときに、これらの要素を非表示にしたいと思います。
このようなことはjQueryで可能ですか?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
注:使用
stopPropagation
は、DOMの通常のイベントフローを中断するため、避ける必要があります。詳細については、このCSSTricksの記事を参照してください。代わりにこの方法を使用することを検討してください。
ウィンドウを閉じるドキュメント本文にクリックイベントを添付します。ドキュメント本文への伝播を停止する別のクリックイベントをコンテナにアタッチします。
$(window).click(function() {
//Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
でクリックイベントをリッスンし、を使用してクリックされた要素の祖先またはターゲットではない document
ことを確認できます。#menucontainer
.closest()
そうでない場合、クリックされた要素は の外側にあり、#menucontainer
安全に非表示にすることができます。
$(document).click(function(event) {
var $target = $(event.target);
if(!$target.closest('#menucontainer').length &&
$('#menucontainer').is(":visible")) {
$('#menucontainer').hide();
}
});
メニューを閉じる予定があり、イベントのリッスンを停止する場合は、イベント リスナーの後にクリーンアップすることもできます。この関数は、新しく作成されたリスナーのみをクリーンアップし、他のクリック リスナーを保持しますdocument
。ES2015 構文の場合:
export function hideOnClickOutside(selector) {
const outsideClickListener = (event) => {
const $target = $(event.target);
if (!$target.closest(selector).length && $(selector).is(':visible')) {
$(selector).hide();
removeClickListener();
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
jQueryを使いたくない人向け。これは、プレーンな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
注:!element.contains(event.target)
これは、jQuery 部分の代わりに
使用するだけの Alex のコメントに基づいています。
しかしelement.closest()
、現在ではすべての主要なブラウザーでも利用できます (W3C バージョンは jQuery バージョンとは少し異なります)。ポリフィルはここにあります: Element.closest()
ユーザーが要素内をクリックしてドラッグできるようにしたい場合は、要素を閉じずに、要素の外でマウスを放します。
...
let lastMouseDownX = 0;
let lastMouseDownY = 0;
let lastMouseDownWasOutside = false;
const mouseDownListener = (event: MouseEvent) => {
lastMouseDownX = event.offsetX
lastMouseDownY = event.offsetY
lastMouseDownWasOutside = !$(event.target).closest(element).length
}
document.addEventListener('mousedown', mouseDownListener);
そしてでoutsideClickListener
:
const outsideClickListener = event => {
const deltaX = event.offsetX - lastMouseDownX
const deltaY = event.offsetY - lastMouseDownY
const distSq = (deltaX * deltaX) + (deltaY * deltaY)
const isDrag = distSq > 3
const isDragException = isDrag && !lastMouseDownWasOutside
if (!element.contains(event.target) && isVisible(element) && !isDragException) { // or use: event.target.closest(selector) === null
element.style.display = 'none'
removeClickListener()
document.removeEventListener('mousedown', mouseDownListener); // Or add this line to removeClickListener()
}
}
要素の外側のクリックを検出する方法は?
この質問が非常に人気があり、非常に多くの回答がある理由は、一見複雑だからです。ほぼ 8 年間にわたって何十回もの回答をいただいてきましたが、アクセシビリティがほとんど考慮されていないことに本当に驚いています。
ユーザーがメニュー領域の外をクリックしたときに、これらの要素を非表示にしたいと思います。
これは崇高な原因であり、実際の問題です。質問のタイトル(ほとんどの回答が対処しようとしているように見えるもの)には、不幸な赤いニシンが含まれています.
ヒント: 「クリック」という言葉です。
ダイアログを閉じるためにクリック ハンドラーをバインドしている場合は、既に失敗しています。あなたが失敗した理由は、すべての人がclick
イベントをトリガーするわけではないからです。マウスを使用していないユーザーは、 を押すことでダイアログ (ポップアップ メニューはおそらくダイアログの一種) を回避できます。Tabその後、click
イベントをトリガーしない限り、ダイアログの背後にあるコンテンツを読むことはできません。
それでは、質問を言い換えましょう。
ユーザーがダイアログを終了したときに、ダイアログを閉じるにはどうすればよいですか?
これが目標です。残念ながら、今度はuserisfinishedwiththedialog
イベントをバインドする必要がありますが、そのバインドはそれほど単純ではありません。
では、ユーザーがダイアログの使用を終了したことをどのように検出できるのでしょうか?
focusout
イベントまず、フォーカスがダイアログから離れたかどうかを判断することから始めます。
blur
ヒント:イベントに注意してください。blur
イベントがバブリング フェーズにバインドされている場合は伝播しません。
jQueryfocusout
は問題なく動作します。jQuery を使用できない場合はblur
、キャプチャ段階で使用できます。
element.addEventListener('blur', ..., true);
// use capture: ^^^^
また、多くのダイアログでは、コンテナーがフォーカスを取得できるようにする必要があります。tabindex="-1"
タブ移動フローを中断することなく、ダイアログが動的にフォーカスを受け取ることができるようにするために追加します。
$('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>
そのデモを 1 分以上プレイすると、すぐに問題が発生するはずです。
1 つ目は、ダイアログ内のリンクをクリックできないことです。クリックまたはタブで移動しようとすると、対話が行われる前にダイアログが閉じます。これは、内側の要素にフォーカスすると、イベントが再度focusout
トリガーされる前にイベントがトリガーされるためです。focusin
修正は、イベント ループで状態変更をキューに入れることです。これは、 を使用するsetImmediate(...)
かsetTimeout(..., 0)
、 をサポートしていないブラウザで行うことができますsetImmediate
。キューに入れられると、後続の によってキャンセルできます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>
2 つ目の問題は、リンクをもう一度押してもダイアログが閉じないことです。これは、ダイアログがフォーカスを失い、閉じる動作がトリガーされ、その後、リンクのクリックによってダイアログが再度開かれるためです。
前の問題と同様に、フォーカス状態を管理する必要があります。状態の変更が既にキューに入れられていることを考えると、ダイアログ トリガーでフォーカス イベントを処理するだけです。
これはおなじみのはずです$('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キーがそれを閉じるのが一般的です。
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>
ダイアログ内にフォーカス可能な要素があることがわかっている場合は、ダイアログに直接フォーカスする必要はありません。メニューを作成している場合は、代わりに最初のメニュー項目にフォーカスできます。
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 のロールと属性についての議論は避けますが、詳細については実装者が仕様を参照することを強くお勧めします。使用する役割とその他の適切な属性について。
ここでの他の解決策は私にはうまくいかなかったので、私は使用しなければなりませんでした:
if(!$(event.target).is('#foo'))
{
// hide menu
}
このメソッドを使用して、ドロップダウンメニューの外側をクリックしたときにドロップダウンメニューを閉じる処理を行いました。
まず、コンポーネントのすべての要素にカスタム クラス名を作成しました。このクラス名は、メニュー ウィジェットを構成するすべての要素に追加されます。
const className = `dropdown-${Date.now()}-${Math.random() * 100}`;
クリックとクリックされた要素のクラス名をチェックする関数を作成します。クリックした要素に上で生成したカスタム クラス名が含まれていない場合は、show
フラグを に設定する必要がfalse
あり、メニューが閉じます。
const onClickOutside = (e) => {
if (!e.target.className.includes(className)) {
show = false;
}
};
次に、クリック ハンドラーをウィンドウ オブジェクトにアタッチしました。
// add when widget loads
window.addEventListener("click", onClickOutside);
...そして最後にハウスキーピング
// remove listener when destroying the widget
window.removeEventListener("click", onClickOutside);
調査の結果、3 つの有効な解決策を見つけました (参照用のページ リンクを忘れてしまいました)。
<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>
$("#menuscontainer").click(function() {
$(this).focus();
});
$("#menuscontainer").blur(function(){
$(this).hide();
});
私にとってはうまくいきます。
clickoutsideハンドラー (WLOG) が要素にバインドされると、次のことが起こります。
そのため、イベントの伝播が停止されることはなく、追加のクリックハンドラを要素の「上」で outside-handler とともに使用できます。
これは私にとって完璧に機能しました!!
$('html').click(function (e) {
if (e.target.id == 'YOUR-DIV-ID') {
//do something
} else {
//do something
}
});
この状況の簡単な解決策は次のとおりです。
$(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();
}
});
上記のスクリプトは、クリック イベントdiv
の外側がトリガーされた場合を非表示にします。div
詳細については、次のブログを参照してください。http://www.codecanal.com/detect-click-outside-div-using-javascript/
別のポスターが言ったように、特に表示している要素(この場合はメニュー)にインタラクティブな要素がある場合は、多くの落とし穴があります。次の方法はかなり堅牢であることがわかりました。
$('#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);
}
})
});
ユーザーが外部をクリックしたときにメニューを閉じることが本当に必要だとは思いません。必要なのは、ユーザーがページのどこかをクリックしたときにメニューを閉じることです。メニューをクリックしたり、メニューから離れたりすると、右に閉じますか?
上記の満足のいく答えが見つからなかったため、先日このブログ投稿を書くようになりました。よりペダンティックな場合は、注意すべき点がいくつかあります。
body { margin-left:auto; margin-right: auto; width:960px;}
ウィンドウ クリック イベントのターゲットを確認し (他の場所にキャプチャされていない限り、ウィンドウに伝播する必要があります)、それがメニュー要素のいずれでもないことを確認します。そうでない場合は、メニューから外れています。
または、クリックの位置を確認して、メニュー領域内に含まれているかどうかを確認します。
私はこのようなもので成功しました:
var $menuscontainer = ...;
$('#trigger').click(function() {
$menuscontainer.show();
$('body').click(function(event) {
var $target = $(event.target);
if ($target.parents('#menuscontainer').length == 0) {
$menuscontainer.hide();
}
});
});
ロジックは次のとおりです。#menuscontainer
が表示されたら、クリックハンドラーを本文にバインドします。本文は#menuscontainer
、(クリックの)ターゲットがその子ではない場合にのみ非表示になります。
バリアントとして:
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();
}
});
イベントの伝播を停止しても問題はなく、同じページで複数のメニューをより適切にサポートします。最初のメニューが開いているときに 2 番目のメニューをクリックすると、stopPropagation ソリューションで最初のメニューが開いたままになります。
これは、将来の視聴者向けの標準的な JavaScript ソリューションです。
ドキュメント内の任意の要素をクリックしたときに、クリックした要素の ID が切り替えられた場合、または非表示の要素が非表示になっておらず、非表示の要素にクリックされた要素が含まれていない場合は、要素を切り替えます。
(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>
同じページに複数のトグルを配置する場合は、次のようなものを使用できます。
hidden
折りたたみ可能なアイテムにクラス名を追加します。(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>
このメソッドは、いくつかの jQuery カレンダー プラグインで見つかりました。
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);
使用する:
var go = false;
$(document).click(function(){
if(go){
$('#divID').hide();
go = false;
}
})
$("#divID").mouseover(function(){
go = false;
});
$("#divID").mouseout(function (){
go = true;
});
$("btnID").click( function(){
if($("#divID:visible").length==1)
$("#divID").hide(); // Toggle
$("#divID").show();
});
IE および FF 3.* 用のスクリプトを作成していて、クリックが特定のボックス領域内で発生したかどうかを知りたい場合は、次のようなものを使用することもできます。
this.outsideElementClick = function(objEvent, objElement){
var objCurrentElement = objEvent.target || objEvent.srcElement;
var blnInsideX = false;
var blnInsideY = false;
if (objCurrentElement.getBoundingClientRect().left >= objElement.getBoundingClientRect().left && objCurrentElement.getBoundingClientRect().right <= objElement.getBoundingClientRect().right)
blnInsideX = true;
if (objCurrentElement.getBoundingClientRect().top >= objElement.getBoundingClientRect().top && objCurrentElement.getBoundingClientRect().bottom <= objElement.getBoundingClientRect().bottom)
blnInsideY = true;
if (blnInsideX && blnInsideY)
return false;
else
return true;}
ドキュメントにクリック イベント リスナーをフックします。イベント リスナー内では、イベント オブジェクト、特にevent.targetを見て、どの要素がクリックされたかを確認できます。
$(document).click(function(e){
if ($(e.target).closest("#menuscontainer").length == 0) {
// .closest can help you determine if the element
// or one of its ancestors is #menuscontainer
console.log("hide");
}
});
最も人気のある回答に賛成票を投じますが、追加します
&& (e.target != $('html').get(0)) // ignore the scrollbar
したがって、スクロールバーをクリックしても、ターゲット要素は [非表示など] になりません。
YUI 3 では次のようにしました。
// Detect the click anywhere other than the overlay element to close it.
Y.one(document).on('click', function (e) {
if (e.target.ancestor('#overlay') === null && e.target.get('id') != 'show' && overlay.get('visible') == true) {
overlay.hide();
}
});
祖先がウィジェット要素コンテナーで
ないかどうか、ターゲットがウィジェット/要素を開いている
かどうか、閉じたいウィジェット/要素が既に開いているかどうかを確認しています (それほど重要ではありません)。
上記のユーザーからのコメントに部分的に基づいて、ソリューションを実装しました。これは私たちにとって完全に機能します。これらの要素の外側をクリックすると、元の要素を除いて検索ボックス/結果を非表示にするために使用します。
// HIDE SEARCH BOX IF CLICKING OUTSIDE
$(document).click(function(event){
// IF NOT CLICKING THE SEARCH BOX OR ITS CONTENTS OR SEARCH ICON
if ($("#search-holder").is(":visible") && !$(event.target).is("#search-holder *, #search")) {
$("#search-holder").fadeOut('fast');
$("#search").removeClass('active');
}
});
最初に検索ボックスが既に表示されているかどうかも確認します。この場合、検索の非表示/表示ボタンでアクティブなクラスも削除されます。
これがこの問題に対する私の解決策です。
$(document).ready(function() {
$('#user-toggle').click(function(e) {
$('#user-nav').toggle();
e.stopPropagation();
});
$('body').click(function() {
$('#user-nav').hide();
});
$('#user-nav').click(function(e){
e.stopPropagation();
});
});
これはうまくいくはずです:
$('body').click(function (event) {
var obj = $(event.target);
obj = obj['context']; // context : clicked element inside body
if ($(obj).attr('id') != "menuscontainer" && $('#menuscontainer').is(':visible') == true) {
//hide menu
}
});
関数:
$(function() {
$.fn.click_inout = function(clickin_handler, clickout_handler) {
var item = this;
var is_me = false;
item.click(function(event) {
clickin_handler(event);
is_me = true;
});
$(document).click(function(event) {
if (is_me) {
is_me = false;
} else {
clickout_handler(event);
}
});
return this;
}
});
使用法:
this.input = $('<input>')
.click_inout(
function(event) { me.ShowTree(event); },
function() { me.Hide(); }
)
.appendTo(this.node);
関数は非常に単純です。
ShowTree: function(event) {
this.data_span.show();
}
Hide: function() {
this.data_span.hide();
}
ここでのソリューションは、1 つの要素のみを管理する場合にうまく機能します。ただし、要素が複数ある場合、問題はさらに複雑になります。e.stopPropagation() を使用したトリックと他のすべてのトリックは機能しません。
私は解決策を思いつきました。おそらくそれほど簡単ではありませんが、何もないよりはましです。見てください:
$view.on("click", function(e) {
if(model.isActivated()) return;
var watchUnclick = function() {
rootView.one("mouseleave", function() {
$(document).one("click", function() {
model.deactivate();
});
rootView.one("mouseenter", function() {
watchUnclick();
});
});
};
watchUnclick();
model.activate();
});
私は最終的に次のようなことをしました:
$(document).on('click', 'body, #msg_count_results .close',function() {
$(document).find('#msg_count_results').remove();
});
$(document).on('click','#msg_count_results',function(e) {
e.preventDefault();
return false;
});
エンド ユーザーが使いやすい UI の目的で、新しいコンテナー内に閉じるボタンがあります。通過しないためには、 return false を使用する必要がありました。もちろん、どこかに連れて行ってくれる A HREF があると便利ですが、代わりに ajax を呼び出すこともできます。いずれにせよ、それは私にとってはうまくいきます。ちょうど私が欲しかったもの。
もう1つの解決策は次のとおりです。
使用法:
<div onClick="$('#menu').toggle();$('#menu').clickOutside(function() { $(this).hide(); $(this).clickOutside('disable'); });">Open / Close Menu</div>
<div id="menu" style="display: none; border: 1px solid #000000; background: #660000;">I am a menu, whoa is me.</div>
プラグイン:
(function($) {
var clickOutsideElements = [];
var clickListener = false;
$.fn.clickOutside = function(options, ignoreFirstClick) {
var that = this;
if (ignoreFirstClick == null) ignoreFirstClick = true;
if (options != "disable") {
for (var i in clickOutsideElements) {
if (clickOutsideElements[i].element[0] == $(this)[0]) return this;
}
clickOutsideElements.push({ element : this, clickDetected : ignoreFirstClick, fnc : (typeof(options) != "function") ? function() {} : options });
$(this).on("click.clickOutside", function(event) {
for (var i in clickOutsideElements) {
if (clickOutsideElements[i].element[0] == $(this)[0]) {
clickOutsideElements[i].clickDetected = true;
}
}
});
if (!clickListener) {
if (options != null && typeof(options) == "function") {
$('html').click(function() {
for (var i in clickOutsideElements) {
if (!clickOutsideElements[i].clickDetected) {
clickOutsideElements[i].fnc.call(that);
}
if (clickOutsideElements[i] != null) clickOutsideElements[i].clickDetected = false;
}
});
clickListener = true;
}
}
}
else {
$(this).off("click.clickoutside");
for (var i = 0; i < clickOutsideElements.length; ++i) {
if (clickOutsideElements[i].element[0] == $(this)[0]) {
clickOutsideElements.splice(i, 1);
}
}
}
return this;
}
})(jQuery);
これは私のために働く
$("body").mouseup(function(e) {
var subject = $(".main-menu");
if(e.target.id != subject.attr('id') && !subject.has(e.target).length) {
$('.sub-menu').hide();
}
});
これが私のコードです:
// Listen to every click
$('html').click(function(event) {
if ( $('#mypopupmenu').is(':visible') ) {
if (event.target.id != 'click_this_to_show_mypopupmenu') {
$('#mypopupmenu').hide();
}
}
});
// Listen to selector's clicks
$('#click_this_to_show_mypopupmenu').click(function() {
// If the menu is visible, and you clicked the selector again we need to hide
if ( $('#mypopupmenu').is(':visible') {
$('#mypopupmenu').hide();
return true;
}
// Else we need to show the popup menu
$('#mypopupmenu').show();
});
jQuery().ready(function(){
$('#nav').click(function (event) {
$(this).addClass('activ');
event.stopPropagation();
});
$('html').click(function () {
if( $('#nav').hasClass('activ') ){
$('#nav').removeClass('activ');
}
});
});
正直なところ、以前のソリューションはどれも好きではありませんでした。
これを行う最善の方法は、「クリック」イベントをドキュメントにバインドし、そのクリックが本当に要素の外側にあるかどうかを比較することです (アートが彼の提案で述べたように)。
ただし、そこにはいくつかの問題があります。バインドを解除することはできず、その要素を開閉するための外部ボタンを使用することはできません。
そのため、これらのタスクを簡素化するために、この小さなプラグイン (リンクするにはここをクリック)を作成しました。もっと簡単にできますか?
<a id='theButton' href="#">Toggle the menu</a><br/>
<div id='theMenu'>
I should be toggled when the above menu is clicked,
and hidden when user clicks outside.
</div>
<script>
$('#theButton').click(function(){
$('#theMenu').slideDown();
});
$("#theMenu").dClickOutside({ ignoreList: $("#theButton") }, function(clickedObj){
$(this).slideUp();
});
</script>
これは私にとって時間内に完全にうまくいきました:
$('body').click(function() {
// Hide the menus if visible.
});
私はそれを行う最善の方法はこのようなものだと信じています。
$(document).on("click", function(event) {
clickedtarget = $(event.target).closest('#menuscontainer');
$("#menuscontainer").not(clickedtarget).hide();
});
このタイプのソリューションは、複数のメニューや、JavaScript によって動的に追加されるメニューに対しても簡単に機能するように作成できます。基本的に、ドキュメント内の任意の場所をクリックして、クリックした要素をチェックし、最も近い「#menuscontainer」を選択するだけです。次に、すべてのメニューコンテナを非表示にしますが、クリックしたものは除外します.
メニューがどのように構築されているか正確にはわかりませんが、自由に私のコードを JSFiddle にコピーしてください。これは非常にシンプルですが、完全に機能するメニュー/モーダル システムです。html-menus をビルドするだけで、コードが作業を行います。
これは、一部の人にとってはより良い修正かもしれません。
$(".menu_link").click(function(){
// show menu code
});
$(".menu_link").mouseleave(function(){
//hide menu code, you may add a timer for 3 seconds before code to be run
});
mouseleave は、外側をクリックすることを意味するだけでなく、その要素の領域を離れることも意味します。
メニュー自体が要素内にあるとmenu_link
、メニュー自体をクリックしたり移動したりするのに問題はありません。
外部のクリックを検出するための完璧なソリューションをまだお探しですか? これ以上探さない!クリックアウトやその他の同様のイベントのユニバーサル サポートを提供するパッケージであるClickout-Eventを紹介します。プレーンな HTML属性、バニラ JavaScript、jQuery、Vue.js のディレクティブなど、あらゆるシナリオで機能します。フロントエンド フレームワークがイベントを処理するために内部的に使用している限り、Clickout-Event はそれに対して機能します。ページの任意の場所にスクリプト タグを追加するだけで、魔法のように機能します。onclickout
.addEventListener('clickout')
.on('clickout')
v-on:clickout
addEventListener
HTML 属性
<div onclickout="console.log('clickout detected')">...</div>
バニラ JavaScript
document.getElementById('myId').addEventListener('clickout', myListener);
jQuery
$('#myId').on('clickout', myListener);
Vue.js
<div v-on:clickout="open=false">...</div>
角度
<div (clickout)="close()">...</div>
これは、複数の要素を監視し、要素をキューに動的に追加および削除できる、より一般的なソリューションです。
グローバル キュー ( autoCloseQueue ) を保持します。これは、外部のクリックで閉じる必要がある要素のオブジェクト コンテナーです。
各キュー オブジェクト キーは DOM 要素 ID である必要があり、値は 2 つのコールバック関数を持つオブジェクトである必要があります。
{onPress: someCallbackFunction, onOutsidePress: anotherCallbackFunction}
これをドキュメントの準備ができたコールバックに入れます:
window.autoCloseQueue = {}
$(document).click(function(event) {
for (id in autoCloseQueue){
var element = autoCloseQueue[id];
if ( ($(e.target).parents('#' + id).length) > 0) { // This is a click on the element (or its child element)
console.log('This is a click on an element (or its child element) with id: ' + id);
if (typeof element.onPress == 'function') element.onPress(event, id);
} else { //This is a click outside the element
console.log('This is a click outside the element with id: ' + id);
if (typeof element.onOutsidePress == 'function') element.onOutsidePress(event, id); //call the outside callback
delete autoCloseQueue[id]; //remove the element from the queue
}
}
});
次に、id ' menuscontainer ' を持つ DOM 要素が作成されたら、このオブジェクトをキューに追加します。
window.autoCloseQueue['menuscontainer'] = {onOutsidePress: clickOutsideThisElement}
シンプルなプラグイン:
$.fn.clickOff = function(callback, selfDestroy) {
var clicked = false;
var parent = this;
var destroy = selfDestroy || true;
parent.click(function() {
clicked = true;
});
$(document).click(function(event) {
if (!clicked && parent.is(':visible')) {
if(callback) callback.call(parent, event)
}
if (destroy) {
//parent.clickOff = function() {};
//parent.off("click");
//$(document).off("click");
parent.off("clickOff");
}
clicked = false;
});
};
使用する:
$("#myDiv").clickOff(function() {
alert('clickOff');
});
そのfileTreeClass
外側をクリックすると非表示にするには
jQuery(document).mouseup(function (e) {
var container = $(".fileTreeClass");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
container.hide();
}
});
を呼び出す要素のクリックを処理するために、クリックのキャプチャ フェーズをサブスクライブしますpreventDefault
。
別の名前を使用してドキュメント要素で再トリガーしますclick-anywhere
。
document.addEventListener('click', function (event) {
event = $.event.fix(event);
event.type = 'click-anywhere';
$document.trigger(event);
}, true);
次に、クリックの外側の機能が必要な場所でclick-anywhere
イベントを購読しdocument
、クリックが関心のある要素の外側にあるかどうかを確認します。
$(document).on('click-anywhere', function (event) {
if (!$(event.target).closest('#smth').length) {
// Do anything you need here
}
});
いくつかのメモ:
document
クリックが発生した外側のすべての要素でイベントをトリガーするには、パフォーマンス違反になるため、使用する必要があります。
この機能は特別なプラグインにラップすることができ、外部クリックで何らかのコールバックを呼び出します。
jQuery 自体を使用してキャプチャ フェーズをサブスクライブすることはできません。
document
サブスクリプションがオンになっている場合でも、サブスクライブするためにドキュメントのロードは必要ありませんbody
。
$('html').click(function() {
//Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<button id='#menucontainer'>Ok</button>
</html>
これを使用するという警告:
$('html').click(function() {
// Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
これにより、Ruby on Rails UJS ドライバーが正しく動作しなくなります。たとえば、動作しlink_to 'click', '/url', :method => :delete
ません。
これは回避策かもしれません:
$('html').click(function() {
// Hide the menus if visible
});
$('#menucontainer').click(function(event){
if (!$(event.target).data('method')) {
event.stopPropagation();
}
});
これを試してください:
$('html').click(function(e) {
if($(e.target).parents('#menuscontainer').length == 0) {
$('#menuscontainer').hide();
}
});
https://jsfiddle.net/4cj4jxy0/
html
ただし、クリック イベントがタグに到達できない場合、これは機能しないことに注意してください。(多分他の要素は持っていますstopPropagation()
)。
$(document).on('click.menu.hide', function(e){
if ( !$(e.target).closest('#my_menu').length ) {
$('#my_menu').find('ul').toggleClass('active', false);
}
});
$(document).on('click.menu.show', '#my_menu li', function(e){
$(this).find('ul').toggleClass('active');
});
div {
float: left;
}
ul {
padding: 0;
position: relative;
}
ul li {
padding: 5px 25px 5px 10px;
border: 1px solid silver;
cursor: pointer;
list-style: none;
margin-top: -1px;
white-space: nowrap;
}
ul li ul:before {
margin-right: -20px;
position: absolute;
top: -17px;
right: 0;
content: "\25BC";
}
ul li ul li {
visibility: hidden;
height: 0;
padding-top: 0;
padding-bottom: 0;
border-width: 0 0 1px 0;
}
ul li ul li:last-child {
border: none;
}
ul li ul.active:before {
content: "\25B2";
}
ul li ul.active li {
display: list-item;
visibility: visible;
height: inherit;
padding: 5px 25px 5px 10px;
}
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<div>
<ul id="my_menu">
<li>Menu 1
<ul>
<li>subMenu 1</li>
<li>subMenu 2</li>
<li>subMenu 3</li>
<li>subMenu 4</li>
</ul>
</li>
<li>Menu 2
<ul>
<li>subMenu 1</li>
<li>subMenu 2</li>
<li>subMenu 3</li>
<li>subMenu 4</li>
</ul>
</li>
<li>Menu 3</li>
<li>Menu 4</li>
<li>Menu 5</li>
<li>Menu 6</li>
</ul>
</div>
ここにjsbinバージョンがありますhttp://jsbin.com/xopacadeni/edit?html,css,js,output
Art からのこのすばらしい回答のラッパーとして、またOP によって最初に要求された構文を使用するために、セット要素の外部でクリックが発生したかどうかを記録できる jQuery 拡張機能を次に示します。
$.fn.clickOutsideThisElement = function (callback) {
return this.each(function () {
var self = this;
$(document).click(function (e) {
if (!$(e.target).closest(self).length) {
callback.call(self, e)
}
})
});
};
次に、次のように呼び出すことができます。
$("#menuscontainer").clickOutsideThisElement(function() {
// handle menu toggle
});
$('#menucontainer').click(function(e){
e.stopPropagation();
});
$(document).on('click', function(e){
// code
});
DOM要素に tabindex を設定できます。これにより、ユーザーが DOM 要素の外側をクリックすると、blur イベントがトリガーされます。
<div tabindex="1">
Focus me
</div>
document.querySelector("div").onblur = function(){
console.log('clicked outside')
}
document.querySelector("div").onfocus = function(){
console.log('clicked inside')
}
このコードを試してください:
if ($(event.target).parents().index($('#searchFormEdit')) == -1 &&
$(event.target).parents().index($('.DynarchCalendar-topCont')) == -1 &&
(_x < os.left || _x > (os.left + 570) || _y < os.top || _y > (os.top + 155)) &&
isShowEditForm) {
setVisibleEditForm(false);
}
これは、HTML を微調整したほうがよい解決策になる典型的なケースです。メニュー項目を含まない要素にクリックを設定しないのはなぜですか? その後、伝播を追加する必要はありません。
$('.header, .footer, .main-content').click(function() {
//Hide the menus if visible
});
$("html").click(function(){
if($('#info').css("opacity")>0.9) {
$('#info').fadeOut('fast');
}
});
<div class="feedbackCont" onblur="hidefeedback();">
<div class="feedbackb" onclick="showfeedback();" ></div>
<div class="feedbackhide" tabindex="1"> </div>
</div>
function hidefeedback(){
$j(".feedbackhide").hide();
}
function showfeedback(){
$j(".feedbackhide").show();
$j(".feedbackCont").attr("tabindex",1).focus();
}
これは私が思いついた最も簡単な解決策です。