要素の外側のクリックを検出するにはどうすればよいですか?


2486

HTMLメニューがいくつかあり、ユーザーがこれらのメニューの先頭をクリックすると完全に表示されます。ユーザーがメニュー領域の外側をクリックしたときに、これらの要素を非表示にしたいと思います。

このようなことはjQueryで可能ですか?

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});

47
これがこの戦略のサンプルです:jsfiddle.net/tedp/aL7Xe/1
Ted

18
トムが述べたように、このアプローチを使用する前にcss-tricks.com/dangers-stopping-event-propagationを読むことをお勧めします。そのjsfiddleツールはかなりクールです。
Jon Coombs、2015年

3
要素への参照を取得し、event.target、そして最後に=または==それらの両方は、それに応じ..コードを実行!
のRohitクマール

私は使用をお勧めしますgithub.com/gsantiago/jquery-clickout :)
ロムロM.ファリアス

2
バニラJSソリューションevent.targetせずに event.stopPropagation
lowtechsun 2017

回答:


1812

注:を使用するstopEventPropagation()と、DOMの通常のイベントフローが中断されるため、使用を避けてください。詳細については、この記事参照してください。代わりにこの方法の使用を検討しください

ウィンドウを閉じるドキュメント本文にクリックイベントをアタッチします。ドキュメント本文への伝播を停止する別のクリックイベントをコンテナにアタッチします。

$(window).click(function() {
//Hide the menus if visible
});

$('#menucontainer').click(function(event){
    event.stopPropagation();
});

708
これにより、ボタンやリンクなど、#menucontainerに含まれる多くの標準的な動作が中断されます。この回答がとても人気があるのには驚きました。
アート

75
これは#menucontainer内のすべての振る舞いを壊しません。これは、その内部のすべての伝播チェーンの最下部にあるためです。
エランガルペリン

94
その非常に美しさですが、$('html').click()体ではなく使用する必要があります。ボディは常にコンテンツの高さを持っています。コンテンツが多くないか、画面が非常に高いため、体で満たされた部分でのみ機能します。
meo

103
このソリューションが非常に多くの票を獲得したことにも驚いています。これは、のstopPropagation持つ任意の要素の外側のために失敗しますjsfiddle.net/Flandre/vaNFw/3を
アンドレ

140
フィリップウォルトンは、この回答が最善の解決策ではない理由を非常によく説明しています。css
Tom

1386

クリックイベントをリッスンし、を使用して祖先またはクリックされた要素のターゲットでないdocumentことを確認#menucontainerできます .closest()

そうでない場合、クリックされた要素はの外にあり、#menucontainer安全に非表示にできます。

$(document).click(function(event) { 
  $target = $(event.target);
  if(!$target.closest('#menucontainer').length && 
  $('#menucontainer').is(":visible")) {
    $('#menucontainer').hide();
  }        
});

編集– 2017-06-23

メニューを閉じる予定があり、イベントのリスニングを停止する場合は、イベントリスナーの後にクリーンアップすることもできます。この関数は、新しく作成されたリスナーのみをクリーンアップし、の他のクリックリスナーを保持しdocumentます。ES2015構文の場合:

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)
}

編集– 2018-03-11

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()

編集– 2020-05-21

ユーザーが要素の内側をクリックアンドドラッグできるようにする場合は、要素を閉じずに、要素の外側でマウスを離します。

      ...
      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()
        }
    }

28
私は他の多くの答えを試しましたが、これだけがうまくいきました。ありがとう。私が最終的に使用したコードはこれです:$(document).click(function(event){if($(event.target).closest( '。window')。length == 0){$( '。window' ).fadeOut( 'fast');}});
ピストス

39
最初は開いているときに2番目のメニューをクリックすると最初のメニューがstopPropagationソリューションで開いたままになるため、同じページで複数のメニューをより適切にサポートするため、私は実際にこのソリューションを採用しました。
umassthrower 2012

13
すばらしい答えです。これは、閉じたいアイテムが複数ある場合の方法です。
ジョン

5
他のソリューションにはevent.stopPropagation()の欠陥があるため、これは受け入れられる答えになるはずです。
トマト

20
jQueryのなし - !element.contains(event.target)使用してNode.contains()
アレックス・ロス

304

要素の外側のクリックを検出するにはどうすればよいですか?

この質問が非常に人気があり、非常に多くの回答がある理由は、一見複雑なためです。ほぼ8年と数十の回答を経て、アクセシビリティにほとんど注意が払われていないことに本当に驚いています。

ユーザーがメニュー領域の外側をクリックしたときに、これらの要素を非表示にしたいと思います。

これは高貴な原因であり、実際問題です。質問のタイトル(ほとんどの回答が対処しようとしているように見えるもの)には、不幸な赤いニシンが含まれています。

ヒント:「クリック」という言葉ですです。

実際にはクリックハンドラーをバインドする必要はありません。

クリックハンドラーをバインドしてダイアログを閉じる場合は、既に失敗しています。失敗した理由は、誰もがclickイベントをトリガーするとは限らないためです。マウスを使用していないユーザーは、を押してダイアログをエスケープすることができ(ポップアップメニューは間違いなく一種のダイアログです)、Tabその後、をトリガーしない限り、ダイアログの背後にあるコンテンツを読み取ることができません。clickイベントを。

では、質問を言い換えましょう。

ユーザーがダイアログを使い終わったら、ダイアログをどのように閉じますか?

これが目標です。残念ながら、今はuserisfinishedwiththedialogイベントをバインドする必要があり、そのバインドはそれほど簡単ではありません。

では、ユーザーがダイアログの使用を終了したことをどのように検出できますか?

focusout 出来事

最初に、フォーカスがダイアログから離れたかどうかを確認します。

ヒント:blurイベントに注意してください。イベントblurがバブリングフェーズにバインドされている場合は伝播しません。

jQuery focusoutは問題なく動作します。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'));
  }
});

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'));  
  }
});


Esc キー

フォーカス状態を処理することで完了したと思った場合は、ユーザーエクスペリエンスを簡略化するためにできることが他にもあります。

これは多くの場合「便利な機能」ですが、モーダルまたはポップアップが何らかの種類である場合、Escキーがそれを閉じるのが一般的です。

keydown: function (e) {
  if (e.which === 27) {
    $(this).removeClass('active');
    e.preventDefault();
  }
}


ダイアログ内にフォーカス可能な要素があることがわかっている場合は、ダイアログに直接フォーカスする必要はありません。メニューを作成している場合は、代わりに最初のメニュー項目にフォーカスできます。

click: function (e) {
  $(this.hash)
    .toggleClass('submenu--active')
    .find('a:first')
    .focus();
  e.preventDefault();
}


WAI-ARIAの役割とその他のアクセシビリティサポート

この回答は、この機能のアクセス可能なキーボードとマウスのサポートの基本をカバーしていると思いますが、かなりかなりの大きさなので、WAI-ARIAの役割と属性についての説明は避けますが、詳細については、仕様を参照することを強くお勧めします彼らが使用すべき役割とその他の適切な属性について。


29
これは、説明とアクセシビリティを念頭に置いた最も完全な回答です。他のほとんどの回答はクリックのみを処理し、説明なしでドロップされたコードスニペットであるため、これは受け入れられる回答であると思います。
シリール、

4
素晴らしい、よく説明されています。私はReactコンポーネントでこのアプローチを使用し、完全に機能しました
David Lavieri

2
@zzzzBov深い回答をありがとう、私はバニラJSでそれを達成しようとしています、そして私はすべてのjqueryのもので少し迷っています。バニラjsで同様のものはありますか?
HendrikEng 2017年

2
@zzzzBovいいえ、もちろんjQueryを含まないバージョンを書くためにあなたを探しているわけではありません。そうしようと思います。本当に行き詰まったら、ここで新しい質問をするのが最善だと思います。どうもありがとうございました。
HendrikEng 2017年

7
これはカスタムドロップダウンまたは他の入力のクリックアウトを検出するための優れた方法ですが、2つの理由から、モーダルまたはポップアップの推奨方法にはならないはずです。ユーザーが別のタブまたはウィンドウに切り替えるか、コンテキストメニューを開くと、モーダルは閉じます。さらに、 'click'イベントはマウスを離したときに発生し、 'focusout'イベントはマウスを押した瞬間に発生します。通常、ボタンは、マウスボタンを押して離した場合にのみアクションを実行します。モーダルでこれを行うための適切でアクセス可能な方法は、タブ可能な閉じるボタンを追加することです。
ケビン

144

ここでの他の解決策は私にとってはうまくいかなかったので、私は使用する必要がありました:

if(!$(event.target).is('#foo'))
{
    // hide menu
}

独自のポップオーバーボックスに埋め込むときに、event.targetを使用して他のJquery UIウィジェットの外部クリックHTMLハンドラーがトリガーされないようにする方法の別の実用的な例を投稿しました:元のターゲットを取得する最良の方法
Joey T

43
子要素がクリックされたときにメニューが閉じないよう&& !$(event.target).parents("#foo").is("#foo")IFステートメント内に追加した以外は、これは私にとってはうまくいきました。
ホニョフク2012

1
より良いより見つけることができません://我々している外部の$(event.target).parents( '#fooの')の長さ== 0。
AlexG

3
深い入れ子を処理する簡潔な改善はを使用すること.is('#foo, #foo *')ですが、この問題を解決するためにクリックハンドラーをバインドすることはお勧めしません
zzzzBov 2016

1
!$(event.target).closest("#foo").length@honyovkを追加する必要がなくなり、不要になります。
tvanc

127

Eranの例と同様に機能するアプリケーションがありますが、メニューを開くときにクリックイベントを本文に添付します...こんな感じです。

$('#menucontainer').click(function(event) {
  $('html').one('click',function() {
    // Hide the menus
  });

  event.stopPropagation();
});

jQueryの関数の詳細one()


9
しかし、メニュー自体をクリックすると、外では機能しません:)
vsync

3
クリックリスナーを本文にバインドする前にevent.stopProgagantion()を配置すると役立ちます。
Jasper Kennis 2012

4
この問題は、「1つ」がイベントを配列に複数回追加するjQueryメソッドに適用されることです。したがって、メニューをクリックして複数回開いた場合、イベントは再び本体にバインドされ、メニューを複数回非表示にしようとします。この問題を修正するには、フェイルセーフを適用する必要があります。
markyzm 2013年

使用してバインドした後.one- $('html')ハンドラー内で-を記述し$('html').off('click')ます。
コーディ

4
@Cody役に立たないと思います。oneハンドラが自動的に呼び出されますoff(それがjQueryのドキュメントに示されていますように)。
マリアーノデサンツェ

44

調査後、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>

3番目のソリューション

<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>

8
3番目のソリューションは、これまでで最もエレガントなチェック方法です。また、jQueryのオーバーヘッドも発生しません。非常に素晴らしい。それは大いに役立ちました。ありがとう。
dbarth

私は複数の要素を使用して3番目のソリューションを実現しようとしていdocument.getElementsByClassNameます。
lowtechsun 2017年

@lowtechsunそれぞれを確認するにはループする必要があります。
Donnie D'Amato

3番目のソリューションは本当に気に入りましたが、私のdivがそれを非表示にするものを表示し始める前にクリックがトリガーされます。理由は何ですか?
indiehjaerta

3つ目はconsole.logを許可しますが、メニューを表示する前にこれを行うため、displayをnoneに設定して実際に閉じることはできません。これに対する解決策はありますか?
RolandiXor

40
$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

私にとってはうまくいきます。


3
これは私が使用するものです。完璧ではないかもしれませんが、趣味のプログラマーとしては、明確に理解できるほど単純です。
kevtrout

ぼかしは、#menucontainerクリックに関する質問以外の動きです
ボレル2013

4
@borrelのぼかしはコンテナの外への移動ではありません。ぼかしはフォーカスの反対で、マウスアウトを考えています。このソリューションは、「クリックして編集する」テキストを作成していて、クリックするとプレーンテキストと入力フィールドを交互に入れ替えるときに特に効果的でした。
parker.sikand 2013年

これを機能さtabindex="-1"せるには、を追加する#menuscontainer必要がありました。コンテナー内にinputタグを配置してクリックすると、コンテナーが非表示になるようです。
タイリオン2014

mouseleaveイベントは、メニューとコンテナーに適しています(参照:w3schools.com/jquery/…
Evgenia Manolova '19

38

今、そのためのプラグインがあります:外部イベントブログ投稿

clickoutsideハンドラー(WLOG)が要素にバインドされると、次のようになります。

  • 要素は、clickoutsideハンドラーですべての要素を保持する配列に追加されます
  • 名前空間クリックハンドラーがドキュメントにバインドされています(まだそこにない場合)
  • ドキュメント内の任意のクリックで、clickoutsideイベントが、その配列内のclick -eventsターゲットの親または親ではない要素に対してトリガーされます
  • さらに、clickoutsideイベントのevent.targetは、ユーザーがクリックした要素に設定されます(ユーザーが外側をクリックしただけでなく、ユーザーが何をクリックしたかもわかります)。

そのため、イベントの伝播が停止されることはなく、追加のクリックハンドラーを要素の「上」で外部ハンドラーとともに使用できます。


素敵なプラグイン。「外部イベント」のリンクは無効ですが、ブログ投稿リンクは代わりに有効であり、「clickoutside」種類のイベントに非常に便利なプラグインを提供します。また、MITのライセンスも取得しています。
TechNyquist 2017

素晴らしいプラグイン。完璧に働きました。使用方法は次のとおりです$( '#element' ).on( 'clickoutside', function( e ) { .. } );
ギャビン

33

これは完璧に機能しました!!

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});

この解決策は私がやっていることにうまく機能しましたが、おかげで、まだクリックイベントが発生していました。
マイク

26

ユーザーが外側をクリックしたときにメニューを閉じることは本当に必要だとは思いません。必要なのは、ユーザーがページのどこかをクリックしたときにメニューを閉じることです。メニューをクリックしたり、メニューから外したりすると、正しく閉じますか?

上記で満足のいく答えが見つからなかったため、先日このブログ投稿を書くようになりました。より知識を深めるために、注意すべきいくつかの落とし穴があります。

  1. クリック時にクリックイベントハンドラーをbody要素にアタッチする場合は、メニューを閉じる前に2回目のクリックを待ち、イベントのバインドを解除してください。それ以外の場合、メニューを開いたクリックイベントは、メニューを閉じる必要があるリスナーにバブルします。
  2. クリックイベントでevent.stopPropogation()を使用する場合、ページ内の他の要素はどこでもどこでもクリックできる機能を持つことができません。
  3. ボディ要素にクリックイベントハンドラーを無期限にアタッチすることは、パフォーマンスの高いソリューションではありません
  4. イベントのターゲットとその親をハンドラーの作成者と比較すると、ページのどこかをクリックしたときにメニューを閉じることが本当に必要な場合に、クリックしたときにメニューを閉じることが必要であると見なされます。
  5. body要素のイベントをリッスンすると、コードがより脆弱になります。これを壊すような無邪気なスタイル:body { margin-left:auto; margin-right: auto; width:960px;}

2
「メニューをクリックしたり、メニューから外したりすると、正しく閉じますか?」常にではない。要素からドラッグしてクリックをキャンセルしても、ドキュメントレベルのクリックはトリガーされますが、メニューを閉じ続けることは意図されていません。内部でクリックできる「クリックアウト」動作を使用できる他のタイプのダイアログもたくさんあります。
zzzzBov 2016

25

別のポスターが言ったように、特にあなたが表示している要素(この場合はメニュー)がインタラクティブな要素を持っている場合、多くの落とし穴があります。次の方法はかなり堅牢であることがわかりました。

$('#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);
        }

    })
});

25

この状況の簡単な解決策は次のとおりです。

$(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();
    }
});

上記のスクリプトはdivdivClickイベントの外部がトリガーされた場合に非表示にします。

詳細については、次のブログを参照してください。 ください。http //www.codecanal.com/detect-click-outside-div-using-javascript/


22

Solution1

副作用のあるevent.stopPropagation()を使用する代わりに、単純なフラグ変数を定義して1つのif条件を追加します。私はこれをテストし、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";
    }
});

Solution2

単純なif条件で:

$(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
    }
});

私はブールフラグでこのソリューションを使用しました。また、アーティキュレートされたDOmで、そして#menucontainer内に他の要素がたくさんある場合も良好です
Migio B

ソリューション1は、イベントがドキュメントに伝播するまでにクリックターゲットがDOMから削除される場合を処理するため、より適切に機能します。
アリス

21

ウィンドウクリックイベントターゲットをチェックし(他の場所でキャプチャされない限り、ウィンドウに伝播する必要があります)、メニュー要素ではないことを確認します。そうでない場合は、メニューの外にいます。

または、クリックの位置を確認し、それがメニュー領域内に含まれているかどうかを確認します。


18

私はこのようなもので成功しました:

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(クリックの)ターゲットがその子ではない場合にのみ非表示にします。


17

バリエーションとして:

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ソリューションで開いたままになります。


16

イベントには要素のevent.pathと呼ばれるプロパティがあり、これは「すべての祖先がツリー順に静的に順序付けられたリスト」です。イベントが特定のDOM要素またはその子の1つから発生したかどうかを確認するには、その特定のDOM要素のパスを確認します。関数のOR要素チェックを論理的に実行することで、複数の要素のチェックにも使用できsomeます。

$("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;
  background:yellow;
}
<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>

だからあなたのケースでは

$("body").click(function() {
  target = $("#menuscontainer")[0];
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  });
  if (!flag) {
    // Hide the menus
  }
});

1
Edgeでは機能しません。
Legends

event.path事ではありません。
アンドリュー

14

このメソッドはいくつかの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);

13

これは、将来の視聴者向けのバニラ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);
})();

同じページに複数のトグルを配置する場合は、次のようなものを使用できます。

  1. クラス名hiddenを折りたたみ可能なアイテムに追加します。
  2. ドキュメントをクリックすると、クリックされた要素を含まず、非表示になっていないすべての非表示要素を閉じます
  3. クリックされた要素がトグルの場合、指定された要素をトグルします。

(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>


13

誰も実際にfocusoutイベントを認めていないことに驚いています:

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>


あなたは私の答えを見逃していると思います。stackoverflow.com/a/47755925/6478359
Muhammet Can TONBUL

これは、端的に言えば、受け入れられる答えになるはずです。ありがとう!!!
ラッキーラム

8

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;}

8

フローの中断、ぼかし/フォーカスイベント、またはその他のトリッキーなテクニックを使用する代わりに、イベントフローを要素の親族と単に一致させます。

$(document).on("click.menu-outside", function(event){
    // Test if target and it's parent aren't #menuscontainer
    // That means the click event occur on other branch of document tree
    if(!$(event.target).parents().andSelf().is("#menuscontainer")){
        // Click outisde #menuscontainer
        // Hide the menus (but test if menus aren't already hidden)
    }
});

イベントリスナーの外側のクリックを削除するには、次の手順に従います。

$(document).off("click.menu-outside");

私にとってこれは最良の解決策でした。アニメーション後のコールバックでそれを使用したので、イベントを切り離す必要がありました。+1
qwertzman 2014年

ここには冗長チェックがあります。要素が実際にある#menuscontainer場合は、まだその親を通過しています。最初にそれを確認し、それがその要素でない場合は、DOMツリーを上に移動します。
vsync 2014

正しい !条件をに変更できますif(!($(event.target).is("#menuscontainer") || $(event.target).parents().is("#menuscontainer"))){。これは小さな最適化ですが、プログラムの存続期間中に数回だけ発生しますclick.menu-outside。イベントが登録されている場合は、クリックごとに発生します。より長く(+32文字)、メソッドチェーンを使用しません
mems

8

使用する:

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();
});

7

ここで知りたい人がjavascriptソリューション(es6)である場合:

window.addEventListener('mouseup', e => {
        if (e.target != yourDiv && e.target.parentNode != yourDiv) {
            yourDiv.classList.remove('show-menu');
            //or yourDiv.style.display = 'none';
        }
    })

念のためにes5:

window.addEventListener('mouseup', function (e) {
if (e.target != yourDiv && e.target.parentNode != yourDiv) {
    yourDiv.classList.remove('show-menu'); 
    //or yourDiv.style.display = 'none';
}

});


7

これは、純粋なJavaScriptによる簡単な解決策です。それは最新ES6で

var isMenuClick = false;
var menu = document.getElementById('menuscontainer');
document.addEventListener('click',()=>{
    if(!isMenuClick){
       //Hide the menu here
    }
    //Reset isMenuClick 
    isMenuClick = false;
})
menu.addEventListener('click',()=>{
    isMenuClick = true;
})

「ES6の最新情報」はかなり大胆な主張であり、ES6の最新情報のみがの() => {}代わりに実行されfunction() {}ます。あなたが持っているものは、ES6をひねったプレーンJavaScriptとして分類されます。
MortenMoulder 2017年

@MortenMoulder:はい。それは実際にはES6ですが、注意のためです。しかし、解決策を見てください。いいと思います。
Duannx 2017年

それはバニラJSであり、DOMから削除されたイベントターゲットに対して機能します(たとえば、内部ポップアップからの値が選択された場合、すぐにポップアップを閉じます)。私から+1!
アリス

6

ドキュメントのクリックイベントリスナーをフックします。イベントリスナー内では、イベントオブジェクト、特に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");
    }
});

6

以下のスクリプトを使用して、jQueryを実行しました。

jQuery(document).click(function(e) {
    var target = e.target; //target div recorded
    if (!jQuery(target).is('#tobehide') ) {
        jQuery(this).fadeOut(); //if the click element is not the above id will hide
    }
})

以下はHTMLコードを見つけます

<div class="main-container">
<div> Hello I am the title</div>
<div class="tobehide">I will hide when you click outside of me</div>
</div>

ここでチュートリアルを読むことができます


5
$(document).click(function() {
    $(".overlay-window").hide();
});
$(".overlay-window").click(function() {
    return false;
});

ドキュメントをクリックした場合、同じ要素をクリックしない限り、特定の要素を非表示にします。


5

最も人気のある回答に賛成投票するが、追加

&& (e.target != $('html').get(0)) // ignore the scrollbar

そのため、スクロールバーをクリックしても、ターゲット要素は[非表示またはその他]になりません。


5

使いやすく、より表現力豊かなコードを作成するために、このためのjQueryプラグインを作成しました。

$('div.my-element').clickOut(function(target) { 
    //do something here... 
});

注: targetは、ユーザーが実際にクリックした要素です。しかし、コールバックは、まだ元の要素のコンテキストで実行されるので、あなたが利用することができ、これを使用すると、jQueryのコールバックで期待通り。

プラグイン:

$.fn.clickOut = function (parent, fn) {
    var context = this;
    fn = (typeof parent === 'function') ? parent : fn;
    parent = (parent instanceof jQuery) ? parent : $(document);

    context.each(function () {
        var that = this;
        parent.on('click', function (e) {
            var clicked = $(e.target);
            if (!clicked.is(that) && !clicked.parents().is(that)) {
                if (typeof fn === 'function') {
                    fn.call(that, clicked);
                }
            }
        });

    });
    return context;
};

デフォルトでは、クリックイベントリスナーはドキュメントに配置されます。ただし、イベントリスナーのスコープを制限する場合は、クリックがリッスンされる最上位の親になる親レベルの要素を表すjQueryオブジェクトを渡すことができます。これにより、不要なドキュメントレベルのイベントリスナーが防止されます。明らかに、提供された親要素が初期要素の親でない限り、機能しません。

そのように使用してください:

$('div.my-element').clickOut($('div.my-parent'), function(target) { 
    //do something here...
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.