2756

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

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

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

90 に答える 90

1948

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

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

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

$('#menucontainer').click(function(event){
  event.stopPropagation();
});
于 2008-09-30T13:38:11.133 に答える
1541

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

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

$(document).click(function(event) { 
  var $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) => {
    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)
}

編集 – 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()
        }
    }
于 2010-06-12T08:35:55.927 に答える
379

要素の外側のクリックを検出する方法は?

この質問が非常に人気があり、非常に多くの回答がある理由は、一見複雑だからです。ほぼ 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

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

これは多くの場合、「あると便利な」機能ですが、モーダルまたはポップアップがある場合、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 ロールとその他のアクセシビリティ サポート

この回答は、この機能のアクセス可能なキーボードとマウスのサポートの基本をカバーしていることを願っていますが、すでにかなりの規模になっているため、WAI-ARIA のロールと属性についての議論は避けますが、詳細については実装者が仕様を参照することを強くお勧めします。使用する役割とその他の適切な属性について。

于 2016-07-11T23:29:46.630 に答える
164

ここでの他の解決策は私にはうまくいかなかったので、私は使用しなければなりませんでした:

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

編集: プレーン Javascript バリアント (2021-03-31)

このメソッドを使用して、ドロップダウンメニューの外側をクリックしたときにドロップダウンメニューを閉じる処理を行いました。

まず、コンポーネントのすべての要素にカスタム クラス名を作成しました。このクラス名は、メニュー ウィジェットを構成するすべての要素に追加されます。

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);
于 2009-07-06T23:10:07.377 に答える
133

メニューを開くときにクリックイベントを本文に添付することを除いて、Eranの例と同様に機能するアプリケーションがあります...次のように:

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

  event.stopPropagation();
});

jQueryの機能の詳細one()

于 2008-09-30T18:13:39.537 に答える
53

調査の結果、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>

2 番目の解決策

<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>
于 2015-11-02T08:33:34.373 に答える
43
$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

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

于 2009-11-17T06:13:44.147 に答える
38

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

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

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

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

于 2010-04-05T10:07:36.267 に答える
33

これは私にとって完璧に機能しました!!

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});
于 2012-06-04T14:08:27.180 に答える
28

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

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

于 2015-12-15T03:50:58.497 に答える
26

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

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

    })
});
于 2011-12-22T11:59:02.103 に答える
26

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

上記の満足のいく答えが見つからなかったため、先日このブログ投稿を書くようになりました。よりペダンティックな場合は、注意すべき点がいくつかあります。

  1. クリック時にクリック イベント ハンドラーを body 要素にアタッチする場合は、2 回目のクリックを待ってからメニューを閉じ、イベントのバインドを解除してください。そうしないと、メニューを開いたクリック イベントが、メニューを閉じなければならないリスナーにバブリングします。
  2. クリック イベントで event.stopPropogation() を使用する場合、ページ内の他の要素にクリックで閉じる機能を持たせることはできません。
  3. クリック イベント ハンドラーを body 要素に無期限にアタッチすることは、パフォーマンスの高いソリューションではありません
  4. イベントのターゲットとその親をハンドラーの作成者と比較すると、実際にはページのどこかをクリックしたときにメニューを閉じる必要があるのに、クリックしたときにメニューを閉じることが必要であると想定されます。
  5. body 要素でイベントをリッスンすると、コードがより脆弱になります。これが壊れるのと同じくらい無邪気なスタイリング:body { margin-left:auto; margin-right: auto; width:960px;}
于 2011-05-18T21:15:19.563 に答える
22

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

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

于 2008-09-30T13:20:30.023 に答える
19

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

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

于 2010-12-02T09:53:12.453 に答える
16

バリアントとして:

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

于 2014-07-24T09:41:53.297 に答える
14

これは、将来の視聴者向けの標準的な 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>

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

  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>

于 2015-05-19T22:52:49.757 に答える
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);
于 2011-07-28T14:06:37.990 に答える
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();
});
于 2011-01-09T23:47:54.120 に答える
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;}
于 2009-08-14T14:08:21.577 に答える
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");
    }
});
于 2012-05-03T09:21:57.377 に答える
6

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

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

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

于 2015-07-25T21:33:38.590 に答える
5

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

祖先がウィジェット要素コンテナーで
ないかどうか、ターゲットがウィジェット/要素を開いている
かどうか、閉じたいウィジェット/要素が既に開いているかどうかを確認しています (それほど重要ではありません)。

于 2011-12-06T13:42:42.560 に答える
5

上記のユーザーからのコメントに部分的に基づいて、ソリューションを実装しました。これは私たちにとって完全に機能します。これらの要素の外側をクリックすると、元の要素を除いて検索ボックス/結果を非表示にするために使用します。

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

最初に検索ボックスが既に表示されているかどうかも確認します。この場合、検索の非表示/表示ボタンでアクティブなクラスも削除されます。

于 2015-07-08T01:05:46.007 に答える
4

これがこの問題に対する私の解決策です。

$(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();
  });
});
于 2012-03-21T16:24:38.463 に答える
4

これはうまくいくはずです:

$('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
    }
});
于 2013-01-12T11:43:29.937 に答える
4

関数:

$(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();
}
于 2010-06-17T14:58:05.713 に答える
4

ここでのソリューションは、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();
    });
于 2013-05-31T17:49:31.527 に答える
4

私は最終的に次のようなことをしました:

$(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 を呼び出すこともできます。いずれにせよ、それは私にとってはうまくいきます。ちょうど私が欲しかったもの。

于 2013-11-19T01:40:52.687 に答える
3

もう1つの解決策は次のとおりです。

http://jsfiddle.net/zR76D/

使用法:

<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);
于 2013-05-30T17:51:19.177 に答える
3

これは私のために働く

$("body").mouseup(function(e) {
    var subject = $(".main-menu");
    if(e.target.id != subject.attr('id') && !subject.has(e.target).length) {
        $('.sub-menu').hide();
    }
});
于 2017-09-13T02:11:50.150 に答える
2

これが私のコードです:

// 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();
});
于 2012-03-06T13:13:55.787 に答える
2
jQuery().ready(function(){
    $('#nav').click(function (event) {
        $(this).addClass('activ');
        event.stopPropagation();
    });

    $('html').click(function () {
        if( $('#nav').hasClass('activ') ){
            $('#nav').removeClass('activ');
        }
    });
});
于 2012-04-09T14:02:00.430 に答える
2

正直なところ、以前のソリューションはどれも好きではありませんでした。

これを行う最善の方法は、「クリック」イベントをドキュメントにバインドし、そのクリックが本当に要素の外側にあるかどうかを比較することです (アートが彼の提案で述べたように)。

ただし、そこにはいくつかの問題があります。バインドを解除することはできず、その要素を開閉するための外部ボタンを使用することはできません。

そのため、これらのタスクを簡素化するために、この小さなプラグイン (リンクするにはここをクリック)を作成しました。もっと簡単にできますか?

<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>
于 2012-11-25T20:29:12.353 に答える
2

これは私にとって時間内に完全にうまくいきました:

$('body').click(function() {
    // Hide the menus if visible.
});
于 2010-06-02T19:15:33.213 に答える
2

私はそれを行う最善の方法はこのようなものだと信じています。

$(document).on("click", function(event) {
  clickedtarget = $(event.target).closest('#menuscontainer');
  $("#menuscontainer").not(clickedtarget).hide();
});

このタイプのソリューションは、複数のメニューや、JavaScript によって動的に追加されるメニューに対しても簡単に機能するように作成できます。基本的に、ドキュメント内の任意の場所をクリックして、クリックした要素をチェックし、最も近い「#menuscontainer」を選択するだけです。次に、すべてのメニューコンテナを非表示にしますが、クリックしたものは除外します.

メニューがどのように構築されているか正確にはわかりませんが、自由に私のコードを JSFiddle にコピーしてください。これは非常にシンプルですが、完全に機能するメニュー/モーダル システムです。html-menus をビルドするだけで、コードが作業を行います。

https://jsfiddle.net/zs6anrn7/

于 2016-11-12T13:57:44.710 に答える
2

これは、一部の人にとってはより良い修正かもしれません。

$(".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、メニュー自体をクリックしたり移動したりするのに問題はありません。

于 2016-11-01T09:55:50.227 に答える
2

外部のクリックを検出するための完璧なソリューションをまだお探しですか? これ以上探さない!クリックアウトやその他の同様のイベントのユニバーサル サポートを提供するパッケージであるClickout-Eventを紹介します。プレーンな HTML属性、バニラ JavaScript、jQuery、Vue.js のディレクティブなど、あらゆるシナリオで機能します。フロントエンド フレームワークがイベントを処理するために内部的に使用している限り、Clickout-Event はそれに対して機能します。ページの任意の場所にスクリプト タグを追加するだけで、魔法のように機能します。onclickout.addEventListener('clickout').on('clickout')v-on:clickoutaddEventListener

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>
于 2020-08-27T13:30:42.427 に答える
1

これは、複数の要素を監視し、要素をキューに動的に追加および削除できる、より一般的なソリューションです。

グローバル キュー ( 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}
于 2013-09-30T10:31:58.910 に答える
1

シンプルなプラグイン:

$.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');
});
于 2016-09-09T19:31:58.253 に答える
1

その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();
            }
        });
于 2016-08-17T13:00:21.187 に答える
0

を呼び出す要素のクリックを処理するために、クリックのキャプチャ フェーズをサブスクライブします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

于 2016-04-11T13:21:09.983 に答える
0

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

于 2016-01-28T07:24:29.653 に答える
0

これを使用するという警告:

$('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();
  }
});
于 2012-12-14T10:55:14.997 に答える
0

これを試してください:

$('html').click(function(e) {
  if($(e.target).parents('#menuscontainer').length == 0) {
    $('#menuscontainer').hide();
  }
});

https://jsfiddle.net/4cj4jxy0/

htmlただし、クリック イベントがタグに到達できない場合、これは機能しないことに注意してください。(多分他の要素は持っていますstopPropagation())。

于 2016-03-07T14:37:05.863 に答える
0
$(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

于 2016-05-11T21:13:40.107 に答える
-1

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

ここにフィドルのデモがあります

于 2014-07-07T14:11:52.260 に答える
-1
    $('#menucontainer').click(function(e){
        e.stopPropagation();
     });

    $(document).on('click',  function(e){
        // code
    });
于 2015-12-11T07:42:47.707 に答える
-2

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')
}
于 2014-03-13T18:06:37.350 に答える
-3

標準 HTML:

メニューを a で囲み、<label>フォーカス状態の変更をフェッチします。

http://jsfiddle.net/bK3gL/

プラス: でメニューを展開できますTab

于 2014-06-10T12:56:17.857 に答える
-3

このコードを試してください:

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);
}
于 2014-02-17T10:32:08.160 に答える
-3

これは、HTML を微調整したほうがよい解決策になる典型的なケースです。メニュー項目を含まない要素にクリックを設定しないのはなぜですか? その後、伝播を追加する必要はありません。

$('.header, .footer, .main-content').click(function() {
//Hide the menus if visible
});
于 2015-08-04T15:37:46.503 に答える
-3
$("html").click(function(){
    if($('#info').css("opacity")>0.9) {
        $('#info').fadeOut('fast');
    }
});
于 2014-12-18T01:12:27.700 に答える
-3
 <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();
}

これは私が思いついた最も簡単な解決策です。

于 2013-02-19T21:11:50.560 に答える