簡體   English   中英

如何檢測元素外的點擊?

[英]How do I detect a click outside an element?

我有一些 HTML 菜單,當用戶點擊這些菜單的頭部時,我會完整地顯示這些菜單。 當用戶在菜單區域外單擊時,我想隱藏這些元素。

jQuery 可能會出現這樣的情況嗎?

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

注意:應該避免使用stopPropagation ,因為它會破壞 DOM 中的正常事件流。 有關更多信息,請參閱這篇 CSS 技巧文章 請考慮改用此方法

將單擊事件附加到關閉窗口的文檔正文。 將單獨的單擊事件附加到容器,以停止傳播到文檔正文。

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

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

您可以偵聽document上的單擊事件,然后使用.closest()確保#menucontainer不是被單擊元素的祖先或目標。

如果不是,則單擊的元素位於#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 

注意:這是基於 Alex 的評論,只使用!element.contains(event.target)而不是 jQuery 部分。

但是element.closest()現在也可以在所有主要瀏覽器中使用(W3C 版本與 jQuery 版本略有不同)。 Polyfill 可以在這里找到: 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()
        }
    }

如何檢測元素外的點擊?

這個問題如此受歡迎並且有這么多答案的原因是它看起來很復雜。 經過近八年和幾十個答案,我真的很驚訝看到對可訪問性的關注如此之少。

當用戶在菜單區域之外單擊時,我想隱藏這些元素。

這是一個崇高的事業,也是真正的問題。 問題的標題——這是大多數答案似乎試圖解決的問題——包含一個不幸的紅鯡魚。

提示:是“點擊”這個詞!

您實際上並不想綁定點擊處理程序。

如果您正在綁定單擊處理程序以關閉對話框,那么您已經失敗了。 你失敗的原因是不是每個人都會觸發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>


如果您使用該演示超過一分鍾,您應該很快就會開始看到問題。

首先是對話框中的鏈接不可點擊。 嘗試單擊它或使用選項卡將導致對話框在交互發生之前關閉。 這是因為聚焦內部元素會在再次觸發focusout事件之前觸發focusin事件。

解決方法是在事件循環中對狀態更改進行排隊。 這可以通過對不支持setImmediate的瀏覽器使用setImmediate(...)setTimeout(..., 0)來完成。 一旦排隊,它可以被后續的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>

第二個問題是再次按下鏈接時對話框不會關閉。 這是因為對話框失去焦點,觸發關閉行為,之后點擊鏈接觸發對話框重新打開。

與上一期類似,需要對焦點狀態進行管理。 鑒於狀態更改已經排隊,只需處理對話框觸發器上的焦點事件即可:

這應該看起來很熟悉
$('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 角色和屬性,但是我強烈建議實施者參考規范以獲取詳細信息他們應該使用什么角色以及任何其他適當的屬性。

這里的其他解決方案對我不起作用,所以我不得不使用:

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

我有一個與 Eran 的示例類似的應用程序,除了我在打開菜單時將點擊事件附加到正文......有點像這樣:

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

  event.stopPropagation();
});

更多關於jQuery 的one()函數的信息

現在是 2020 年,您可以使用event.composedPath()

來自: https ://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath

Event 接口的composedPath() 方法返回事件的路徑,該路徑是一個對象數組,將在其上調用偵聽器。

 const target = document.querySelector('#myTarget') document.addEventListener('click', (event) => { const withinBoundaries = event.composedPath().includes(target) if (withinBoundaries) { target.innerText = 'Click happened inside element' } else { target.innerText = 'Click happened **OUTSIDE** element' } })
 /* just to make it good looking. you don't need this */ #myTarget { margin: 50px auto; width: 500px; height: 500px; background: gray; border: 10px solid black; }
 <div id="myTarget"> click me (or not!) </div>

經過研究,我找到了三個可行的解決方案(我忘記了參考的頁面鏈接)

第一個解決方案

<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) 綁定到元素時會發生以下情況:

  • 該元素被添加到一個數組中,該數組包含所有帶有clickoutside處理程序的元素
  • 命名空間點擊處理程序綁定到文檔(如果還沒有)
  • 在文檔中的任何點擊上,都會為該數組中不等於click -events 目標或它的父級的那些元素觸發clickoutside事件
  • 此外, clickoutside事件的 event.target 設置為用戶單擊的元素(因此您甚至知道用戶單擊了什么,而不僅僅是他單擊了外部)

因此,不會停止傳播任何事件,並且可以在帶有外部處理程序的元素“上方”使用額外的單擊處理程序。

這對我來說非常有效!

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

我認為您真正需要的不是當用戶在外面點擊時關閉菜單; 您需要的是當用戶單擊頁面上的任何位置時關閉菜單。 如果您單擊菜單,或關閉菜單,它應該關閉嗎?

在上面找不到令人滿意的答案促使我前幾天寫了這篇博文。 對於更迂腐的人,有一些問題需要注意:

  1. 如果在單擊時將單擊事件處理程序附加到正文元素,請確保在關閉菜單之前等待第二次單擊,並取消綁定事件。 否則,打開菜單的單擊事件將冒泡到必須關閉菜單的偵聽器。
  2. 如果您在單擊事件上使用 event.stopPropogation(),則頁面中的任何其他元素都不能具有單擊任意位置關閉功能。
  3. 無限期地將單擊事件處理程序附加到正文元素不是一個高性能的解決方案
  4. 將事件的目標及其父事件與處理程序的創建者進行比較,假設您想要的是在您單擊它時關閉菜單,而您真正想要的是在您單擊頁面上的任何位置時關閉它。
  5. 監聽 body 元素上的事件會使你的代碼更脆弱。 像這樣無辜的造型會破壞它: body { margin-left:auto; margin-right: auto; width:960px;} body { margin-left:auto; margin-right: auto; width:960px;}

正如另一位海報所說,有很多陷阱,特別是如果您正在顯示的元素(在本例中為菜單)具有交互元素。 我發現以下方法相當穩健:

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

    })
});

檢查窗口單擊事件目標(它應該傳播到窗口,只要它沒有在其他任何地方捕獲),並確保它不是任何菜單元素。 如果不是,那么你就在你的菜單之外。

或者檢查點擊的位置,看看它是否包含在菜單區域內。

解決方案1

不要使用可能有一些副作用的 event.stopPropagation(),只需定義一個簡單的標志變量並添加一個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";
    }
});

解決方案2

只需一個簡單的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
    }
});

我很驚訝沒有人真正承認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>

我在這樣的事情上取得了成功:

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的主體。

該事件有一個名為 event.path 的元素的屬性,它是“按樹順序排列其所有祖先的靜態有序列表” 要檢查事件是否源自特定 DOM 元素或其子元素之一,只需檢查該特定 DOM 元素的路徑。 它還可用於通過在some函數中對元素檢查進行邏輯OR來檢查多個元素。

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

作為變體:

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

它在停止事件傳播方面沒有問題,並且更好地支持同一頁面上的多個菜單,其中在第一個菜單打開時單擊第二個菜單將使第一個菜單在 stopPropagation 解決方案中保持打開狀態。

使用focusout實現可訪問性

這里有一個答案說(非常正確)關注click事件是一個可訪問性問題,因為我們想要滿足鍵盤用戶的需求。 focusout事件是在這里使用的正確方法,但它可以比其他答案更簡單(也可以在純 javascript 中):

一種更簡單的方法:

使用focusout的“問題”是,如果對話框/模態/菜單中的元素失去焦點,那么事件仍然會被觸發。 我們可以通過查看event.relatedTarget (它告訴我們哪個元素將獲得焦點)來檢查情況是否如此。

dialog = document.getElementById("dialogElement")

dialog.addEventListener("focusout", function (event) {
    if (
        // we are still inside the dialog so don't close
        dialog.contains(event.relatedTarget) ||
        // we have switched to another tab so probably don't want to close 
        !document.hasFocus()  
    ) {
        return;
    }
    dialog.close();  // or whatever logic you want to use to close
});

上面有一個小問題,就是relatedTarget可能是null 如果用戶在對話框外部單擊,這很好,但如果除非用戶在對話框內部單擊並且對話框碰巧不可聚焦,那么這將是一個問題。 要解決此問題,您必須確保設置tabIndex=0以便您的對話框可聚焦。

我在一些 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);

這是面向未來觀眾的 vanilla 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>

2020 年使用原生 JS API 最接近方法的解決方案。

 document.addEventListener('click', ({ target }) => { if (!target.closest('#menupop')) { document.querySelector('#menupop').style.display = 'none' } })
 #menupop { width: 300px; height: 300px; background-color: red; }
 <div id="menupop"> clicking outside will close this </div>

如果有人在這里好奇是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';
}

});

如果您正在為 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; }

而不是使用流中斷、模糊/焦點事件或任何其他棘手的技術,只需將事件流與元素的親屬關系匹配:

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

利用:

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

這是純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;
})

我使用了下面的腳本並使用 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>

你可以在這里閱讀教程

在文檔上掛鈎一個單擊事件偵聽器。 在事件偵聽器中,您可以查看事件對象,特別是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

因此,單擊滾動條不會[隱藏或其他]您的目標元素。

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

如果單擊文檔,則隱藏給定元素,除非單擊同一元素。

我們實施了一個解決方案,部分基於上述用戶的評論,這對我們來說非常有效。 當在這些元素之外單擊時,我們使用它來隱藏搜索框/結果,不包括最初的元素。

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

它首先檢查搜索框是否已經可見,在我們的例子中,它還刪除了隱藏/顯示搜索按鈕上的活動類。

為了更易於使用和更具表現力的代碼,我為此創建了一個 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...
});

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

我正在檢查祖先是否不是小部件元素容器,
如果目標不是打開小部件/元素的,
如果我要關閉的小部件/元素已經打開(不是那么重要)。

這應該有效:

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

當只管理一個元素時,這里的解決方案可以正常工作。 但是,如果有多個元素,問題就會復雜得多。 使用 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 為了不通過。 當然,有一個 HREF 可以帶你去某個地方會很好,或者你可以調用一些 ajax 的東西。 無論哪種方式,它都適合我。 正是我想要的。

功能:

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

這是我為解決問題所做的。

$(window).click(function (event) {
    //To improve performance add a checklike 
    //if(myElement.isClosed) return;
    var isClickedElementChildOfMyBox = isChildOfElement(event,'#id-of-my-element');

    if (isClickedElementChildOfMyBox)
        return;

    //your code to hide the element 
});

var isChildOfElement = function (event, selector) {
    if (event.originalEvent.path) {
        return event.originalEvent.path[0].closest(selector) !== null;
    }

    return event.originalEvent.originalTarget.closest(selector) !== null;
}

我只是想讓@Pistos 的答案更明顯,因為它隱藏在評論中。

這個解決方案對我來說非常有效。 純JS:

var elementToToggle = $('.some-element');
$(document).click( function(event) {
  if( $(event.target).closest(elementToToggle).length === 0 ) {
    elementToToggle.hide();
  }
});

在 CoffeeScript 中:

elementToToggle = $('.some-element')
$(document).click (event) ->
  if $(event.target).closest(elementToToggle).length == 0
    elementToToggle.hide()

假設您要檢測用戶是否在外部或內部單擊的 div 具有 id,例如:“my-special-widget”。

監聽body點擊事件:

document.body.addEventListener('click', (e) => {
    if (isInsideMySpecialWidget(e.target, "my-special-widget")) {
        console.log("user clicked INSIDE the widget");
    }
    console.log("user clicked OUTSIDE the widget");
});

function isInsideMySpecialWidget(elem, mySpecialWidgetId){
    while (elem.parentElement) {
        if (elem.id === mySpecialWidgetId) {
            return true;
        }
        elem = elem.parentElement;
    }
    return false;
}

在這種情況下,您不會破壞點擊頁面中某些元素的正常流程,因為您沒有使用“stopPropagation”方法。

這是我對這個問題的解決方案:

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

另一種解決方案在這里:

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

標記為已接受答案的答案沒有考慮到您可以在元素上覆蓋,如對話框、彈出框、日期選擇器等。單擊這些不應隱藏元素。

我已經制作了自己的版本,它確實考慮到了這一點。 它被創建為一個KnockoutJS綁定,但它可以很容易地轉換為僅限 jQuery。

它通過第一次查詢具有可見的 z-index 或絕對位置的所有元素起作用。 然后,如果在外部單擊,它會針對我想要隱藏的元素對這些元素進行測試。 如果它是一個命中,我會計算一個新的邊界矩形,它會考慮覆蓋邊界。

ko.bindingHandlers.clickedIn = (function () {
    function getBounds(element) {
        var pos = element.offset();
        return {
            x: pos.left,
            x2: pos.left + element.outerWidth(),
            y: pos.top,
            y2: pos.top + element.outerHeight()
        };
    }

    function hitTest(o, l) {
        function getOffset(o) {
            for (var r = { l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth, b: o.offsetHeight };
                o = o.offsetParent; r.l += o.offsetLeft, r.t += o.offsetTop);
            return r.r += r.l, r.b += r.t, r;
        }

        for (var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j ? l = [l] : l).length; i;
            b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r : b.l <= a.r))
                && (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) && (r[r.length] = l[i]));
        return j ? !!r.length : r;
    }

    return {
        init: function (element, valueAccessor) {
            var target = valueAccessor();
            $(document).click(function (e) {
                if (element._clickedInElementShowing === false && target()) {
                    var $element = $(element);
                    var bounds = getBounds($element);

                    var possibleOverlays = $("[style*=z-index],[style*=absolute]").not(":hidden");
                    $.each(possibleOverlays, function () {
                        if (hitTest(element, this)) {
                            var b = getBounds($(this));
                            bounds.x = Math.min(bounds.x, b.x);
                            bounds.x2 = Math.max(bounds.x2, b.x2);
                            bounds.y = Math.min(bounds.y, b.y);
                            bounds.y2 = Math.max(bounds.y2, b.y2);
                        }
                    });

                    if (e.clientX < bounds.x || e.clientX > bounds.x2 ||
                        e.clientY < bounds.y || e.clientY > bounds.y2) {

                        target(false);
                    }
                }
                element._clickedInElementShowing = false;
            });

            $(element).click(function (e) {
                e.stopPropagation();
            });
        },
        update: function (element, valueAccessor) {
            var showing = ko.utils.unwrapObservable(valueAccessor());
            if (showing) {
                element._clickedInElementShowing = true;
            }
        }
    };
})();

對於 iPad 和 iPhone 等觸控設備,我們可以使用以下代碼:

$(document).on('touchstart', function (event) {
    var container = $("YOUR CONTAINER SELECTOR");

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

我知道這個問題有一百萬個答案,但我一直喜歡使用 HTML 和 CSS 來完成大部分工作。 在這種情況下,z-index 和定位。 我發現執行此操作的最簡單方法如下:

 $("#show-trigger").click(function(){ $("#element").animate({width: 'toggle'}); $("#outside-element").show(); }); $("#outside-element").click(function(){ $("#element").hide(); $("#outside-element").hide(); });
 #outside-element { position:fixed; width:100%; height:100%; z-index:1; display:none; } #element { display:none; padding:20px; background-color:#ccc; width:300px; z-index:2; position:relative; } #show-trigger { padding:20px; background-color:#ccc; margin:20px auto; z-index:2; }
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="outside-element"></div> <div id="element"> <div class="menu-item"><a href="#1">Menu Item 1</a></div> <div class="menu-item"><a href="#2">Menu Item 1</a></div> <div class="menu-item"><a href="#3">Menu Item 1</a></div> <div class="menu-item"><a href="#4">Menu Item 1</a></div> </div> <div id="show-trigger">Show Menu</div>

這創建了一個安全的環境,因為除非菜單實際打開並且 z-index 保護元素內的任何內容不會在被單擊時產生任何失火,否則不會觸發任何內容。

此外,您不需要 jQuery 用傳播調用覆蓋所有基礎,也不需要清除所有內部元素的失火。

$(document).on("click",function (event)   
 {   
     console.log(event);
   if ($(event.target).closest('.element').length == 0)
     {
    //your code here
      if ($(".element").hasClass("active"))
      {
        $(".element").removeClass("active");
      }
     }
 });

嘗試此編碼以獲得解決方案。

這對我有用

$("body").mouseup(function(e) {
    var subject = $(".main-menu");
    if(e.target.id != subject.attr('id') && !subject.has(e.target).length) {
        $('.sub-menu').hide();
    }
});

如果您使用“Pop-up”之類的工具,則可以使用“onFocusOut”事件。

 window.onload=function(){ document.getElementById("inside-div").focus(); } function loseFocus(){ alert("Clicked outside"); }
 #container{ background-color:lightblue; width:200px; height:200px; } #inside-div{ background-color:lightgray; width:100px; height:100px; }
 <div id="container"> <input type="text" id="inside-div" onfocusout="loseFocus()"> </div>

所有這些答案都解決了這個問題,但我想貢獻一個完全符合需要的現代 es6 解決方案。 我只是希望能讓有人對這個可運行的演示感到滿意。

 window.clickOutSide = (element, clickOutside, clickInside) => { document.addEventListener('click', (event) => { if (!element.contains(event.target)) { if (typeof clickInside === 'function') { clickOutside(); } } else { if (typeof clickInside === 'function') { clickInside(); } } }); }; window.clickOutSide(document.querySelector('.block'), () => alert('clicked outside'), () => alert('clicked inside'));
 .block { width: 400px; height: 400px; background-color: red; }
 <div class="block"></div>

這是我對這個問題找到的最簡單的答案:

window.addEventListener('click', close_window = function () {
  if(event.target !== windowEl){
    windowEl.style.display = "none";
    window.removeEventListener('click', close_window, false);
  }
});

您會看到我將函數命名為“close_window”,以便在窗口關閉時刪除事件偵聽器。

一種用純 JavaScript 編寫的方法

let menu = document.getElementById("menu");

document.addEventListener("click", function(){
    // Hide the menus
    menu.style.display = "none";
}, false);

document.getElementById("menuscontainer").addEventListener("click", function(e){
    // Show the menus
    menu.style.display = "block";
    e.stopPropagation();
}, false);
jQuery().ready(function(){
    $('#nav').click(function (event) {
        $(this).addClass('activ');
        event.stopPropagation();
    });

    $('html').click(function () {
        if( $('#nav').hasClass('activ') ){
            $('#nav').removeClass('activ');
        }
    });
});

老實說,我不喜歡以前的任何解決方案。

最好的方法是將“點擊”事件綁定到文檔,並比較該點擊是否真的在元素之外(就像 Art 在他的建議中所說的那樣)。

但是,您會遇到一些問題:您將永遠無法解除綁定,並且您無法使用外部按鈕來打開/關閉該元素。

這就是為什么我寫了這個小插件(點擊這里鏈接) ,以簡化這些任務。 能不能簡單點?

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

最廣泛的方法是選擇網頁上的所有內容,除了您不希望檢測到點擊的元素,並在打開菜單時綁定點擊事件。

然后當菜單關閉時刪除綁定。

使用 .stopPropagation 防止事件影響菜單容器的任何部分。

$("*").not($("#menuscontainer")).bind("click.OutsideMenus", function ()
{
    // hide the menus

    //then remove all of the handlers
    $("*").unbind(".OutsideMenus");
});

$("#menuscontainer").bind("click.OutsideMenus", function (event) 
{
    event.stopPropagation(); 
});

這對我來說及時很好:

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

對於某些人來說,這可能是一個更好的解決方案。

$(".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元素內,那么菜單本身就不應該成為單擊或繼續前進的問題。

我相信最好的方法是這樣的。

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

這種類型的解決方案可以很容易地用於多個菜單以及通過 javascript 動態添加的菜單。 基本上它只允許您單擊文檔中的任何位置,並檢查您單擊的元素,並選擇它最接近的“#menuscontainer”。 然后它隱藏所有菜單容器,但不包括您單擊的那個。

不確定您的菜單是如何構建的,但請隨意在 JSFiddle 中復制我的代碼。 這是一個非常簡單但功能齊全的菜單/模式系統。 您需要做的就是構建 html 菜單,代碼將為您完成工作。

https://jsfiddle.net/zs6anrn7/

$('#propertyType').on("click",function(e){
          self.propertyTypeDialog = !self.propertyTypeDialog;
          b = true;
          e.stopPropagation();
          console.log("input clicked");
      });

      $(document).on('click','body:not(#propertyType)',function (e) {
          e.stopPropagation();
          if(b == true)  {
              if ($(e.target).closest("#configuration").length == 0) {
                  b = false;
                  self.propertyTypeDialog = false;
                  console.log("outside clicked");
              }
          }
        // console.log($(e.target).closest("#configuration").length);
      });

 const button = document.querySelector('button') const box = document.querySelector('.box'); const toggle = event => { event.stopPropagation(); if (!event.target.closest('.box')) { console.log('Click outside'); box.classList.toggle('active'); box.classList.contains('active') ? document.addEventListener('click', toggle) : document.removeEventListener('click', toggle); } else { console.log('Click inside'); } } button.addEventListener('click', toggle);
 .box { position: absolute; display: none; margin-top: 8px; padding: 20px; background: lightgray; } .box.active { display: block; }
 <button>Toggle box</button> <div class="box"> <form action=""> <input type="text"> <button type="button">Search</button> </form> </div>

還在尋找檢測外部點擊的完美解決方案嗎? 不要再看了! 介紹Clickout-Event ,一個為 clickout 和其他類似事件提供通用支持的包,它適用於所有場景:純 HTML onclickout屬性、 .addEventListener('clickout') JavaScript 的.on('clickout') 、jQuery 的 .on('clickout') , v-on:clickout的 Vue.js 指令,隨便你。 只要前端框架內部使用addEventListener來處理事件,Clickout-Event 就可以工作。 只需在頁面的任何位置添加腳本標簽,它就可以像魔術一樣工作。

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>

這是我的代碼:

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

這是一個更通用的解決方案,它允許監視多個元素,並從隊列中動態添加和刪除元素

它擁有一個全局隊列 ( 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}

如果在外部單擊,則隱藏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();
            }
        });

簡單的插件:

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

只是一個警告,使用這個:

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

當您單擊/關閉元素時,這將切換導航菜單。

 $(document).on('click', function(e) { var elem = $(e.target).closest('#menu'), box = $(e.target).closest('#nav'); if (elem.length) { e.preventDefault(); $('#nav').toggle(); } else if (!box.length) { $('#nav').hide(); } });
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <li id="menu"> <a></a> </li> <ul id="nav"> <!--Nav will toggle when you Click on Menu(it can be an icon in this example)--> <li class="page"><a>Page1</a></li> <li class="page"><a>Page2</a></li> <li class="page"><a>Page3</a></li> <li class="page"><a>Page4</a></li> </ul>

外點擊插件!

用法:

$('.target-element').outsideClick(function(event){
    //code that fires when user clicks outside the element
    //event = the click event
    //$(this) = the '.target-element' that is firing this function 
}, '.excluded-element')

它的代碼:

(function($) {

//when the user hits the escape key, it will trigger all outsideClick functions
$(document).on("keyup", function (e) {
    if (e.which == 27) $('body').click(); //escape key
});

//The actual plugin
$.fn.outsideClick = function(callback, exclusions) {
    var subject = this;

    //test if exclusions have been set
    var hasExclusions = typeof exclusions !== 'undefined';

    //switches click event with touch event if on a touch device
    var ClickOrTouchEvent = "ontouchend" in document ? "touchend" : "click";

    $('body').on(ClickOrTouchEvent, function(event) {
        //click target does not contain subject as a parent
        var clickedOutside = !$(event.target).closest(subject).length;

        //click target was on one of the excluded elements
        var clickedExclusion = $(event.target).closest(exclusions).length;

        var testSuccessful;

        if (hasExclusions) {
            testSuccessful = clickedOutside && !clickedExclusion;
        } else {
            testSuccessful = clickedOutside;
        }

        if(testSuccessful) {
            callback.call(subject, event);
        }
    });

    return this;
};

}(jQuery));

改編自這個答案https://stackoverflow.com/a/3028037/1611058

 $('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(e) {
  if($(e.target).parents('#menuscontainer').length == 0) {
    $('#menuscontainer').hide();
  }
});

https://jsfiddle.net/4cj4jxy0/

但請注意,如果單擊事件無法到達html標記,則此方法不起作用。 (也許其他元素有stopPropagation() )。

訂閱點擊捕獲階段以處理點擊調用preventDefault的元素。
使用另一個名稱click-anywhere在文檔元素上重新觸發它。

document.addEventListener('click', function (event) {
  event = $.event.fix(event);
  event.type = 'click-anywhere';
  $document.trigger(event);
}, true);

然后在需要點擊外部功能的地方訂閱document上的click-anywhere事件並檢查點擊是否在您感興趣的元素之外:

$(document).on('click-anywhere', function (event) {
  if (!$(event.target).closest('#smth').length) {
    // Do anything you need here
  }
});

一些注意事項:

  • 您必須使用document ,因為在單擊發生之外的所有元素上觸發事件將是性能錯誤。

  • 此功能可以包裝到特殊插件中,該插件在外部點擊時調用一些回調。

  • 您不能使用 jQuery 本身訂閱捕獲階段。

  • 您不需要文檔加載來訂閱,因為訂閱是在document上,甚至不在它的body上,所以它總是獨立存在 ащкь 腳本放置和加載狀態。

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

如果您只想在單擊按鈕時顯示一個窗口,並在單擊外部時取消顯示此窗口。(或再次在按鈕上)此波紋管效果很好

document.body.onclick = function() { undisp_menu(); };
var menu_on = 0;

function menu_trigger(event){

    if (menu_on == 0)
    {
        // otherwise u will call the undisp on body when 
        // click on the button
        event.stopPropagation(); 

        disp_menu();
    }

    else{
        undisp_menu();
    }

}


function disp_menu(){

    menu_on = 1;
    var e = document.getElementsByClassName("menu")[0];
    e.className = "menu on";

}

function undisp_menu(){

    menu_on = 0;
    var e = document.getElementsByClassName("menu")[0];
    e.className = "menu";

}

不要忘記這個按鈕

<div class="button" onclick="menu_trigger(event)">

<div class="menu">

和CSS:

.menu{
    display: none;
}

.on {
    display: inline-block;
}

這對我來說很好。 我不是專家。

$(document).click(function(event) {
  var $target = $(event.target);
  if(!$target.closest('#hamburger, a').length &&
  $('#hamburger, a').is(":visible")) {
    $('nav').slideToggle();
  }
});

我已經閱讀了 2021 年的所有內容,但如果沒有錯的話,沒有人建議像這樣簡單的方法來取消綁定和刪除事件。 使用上述兩個答案和一個小技巧,將所有內容合二為一(也可以將參數添加到函數中以傳遞選擇器,以獲得更多彈出窗口)。 可能有人知道這個笑話也可以這樣做是有用的:

<div id="container" style="display:none"><h1>my menu is nice but disappear if i click outside it</h1></div>

<script>
 function printPopup(){
  $("#container").css({ "display":"block" });
  var remListener = $(document).mouseup(function (e) {
   if ($(e.target).closest("#container").length === 0 && (e.target != $('html').get(0))) 
   {
    //alert('closest call');
    $("#container").css({ "display":"none" });
    remListener.unbind('mouseup'); // isn't it?
   } 
  });
 }

 printPopup();

</script>

干杯

對於Vue用戶(也可能使用 vanilla 或其他框架)。
如果有人遇到這個問題,我會根據Art 的修復使用@focusout而不是綁定到點擊,在幾個小時后讓它工作。

模板中

<div @tabindex="0" @focusout="clickOutside">
  //other content such as inputs, buttons, divs etc.
</div>

腳本中

 clickOutside: (event) => {
    //currentTarget is our div, relatedTarget is the clicked children inside
    if (!event.currentTarget.contains(event.relatedTarget)) {
      //clicked outside the element and outside it's children, ex: now here we can close a menu
    }
  },

如果元素沒有任何子元素,則 if不是必需的。

我嘗試了很多其他的東西,有些工作,問題是我有一個元素列表,每個元素都有一個可以打開的菜單,其他解決方案的問題是我可以打開所有菜單,當在元素外點擊所有菜單立即關閉。

使用此解決方案一次只能打開一個菜單,當嘗試打開另一個菜單時,前一個菜單會關閉,就像我想要的那樣。 但這只是一個例子。

你不需要(很多)JavaScript,只需要:focus-within選擇器:

  • 使用.sidebar:focus-within來顯示你的側邊欄。
  • 在側邊欄和正文元素上設置tabindex=-1以使它們可聚焦。
  • 使用sidebarEl.focus()document.body.focus()設置側邊欄可見性。

 const menuButton = document.querySelector('.menu-button'); const sidebar = document.querySelector('.sidebar'); menuButton.onmousedown = ev => { ev.preventDefault(); (sidebar.contains(document.activeElement) ? document.body : sidebar).focus(); };
 * { box-sizing: border-box; } .sidebar { position: fixed; width: 15em; left: -15em; top: 0; bottom: 0; transition: left 0.3s ease-in-out; background-color: #eef; padding: 3em 1em; } .sidebar:focus-within { left: 0; } .sidebar:focus { outline: 0; } .menu-button { position: fixed; top: 0; left: 0; padding: 1em; background-color: #eef; border: 0; } body { max-width: 30em; margin: 3em; }
 <body tabindex='-1'> <nav class='sidebar' tabindex='-1'> Sidebar content <input type="text"/> </nav> <button class="menu-button">☰</button> Body content goes here, Lorem ipsum sit amet, etc </body>

對於那些想要一個簡短的解決方案來集成到他們的 JS 代碼中的人 - 一個沒有 JQuery 的小型庫:

用法:

// demo code
var htmlElem = document.getElementById('my-element')
function doSomething(){ console.log('outside click') }

// use the lib
var removeListener = new elemOutsideClickListener(htmlElem, doSomething);

// deregister on your wished event
$scope.$on('$destroy', removeListener);

這是庫:


function elemOutsideClickListener (element, outsideClickFunc, insideClickFunc) {
   function onClickOutside (e) {
      var targetEl = e.target; // clicked element
      do {
         // click inside
         if (targetEl === element) {
            if (insideClickFunc) insideClickFunc();
            return;

         // Go up the DOM
         } else {
            targetEl = targetEl.parentNode;
         }
      } while (targetEl);

      // click outside
      if (!targetEl && outsideClickFunc) outsideClickFunc();
   }

   window.addEventListener('click', onClickOutside);

   return function () {
      window.removeEventListener('click', onClickOutside);
   };
}

我從這里獲取代碼並將其放入一個函數中: https ://www.w3docs.com/snippets/javascript/how-to-detect-a-click-outside-an-element.html

$('yourContainer').on('click', function (e) {
        if (!$(e.target).hasClass('yourClass')){
            $('.yourClass').hide();
        }
    });

作為來自 Art 的這個出色答案的包裝,並使用 OP 最初請求的語法,這里有一個 jQuery 擴展,可以記錄是否在 set 元素之外發生了點擊。

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

首先,您必須使用 mouseenter 和 mouseleave 事件來跟蹤鼠標是在 element1 內部還是外部。 然后,您可以創建一個覆蓋整個屏幕的 element2 以檢測任何點擊,並根據您是在 element1 內部還是外部做出相應的反應。

我強烈建議同時處理初始化和清理,並且由於顯而易見的原因,element2 盡可能地是臨時的。

在下面的示例中,疊加層是位於某處的元素,可以通過單擊內部來選擇它,並且可以通過單擊外部來取消選擇。 _init 和 _release 方法被稱為自動初始化/清理過程的一部分。 該類繼承自具有內部和外部元素的 ClickOverlay,不用擔心。 我使用了 outerElement.parentNode.appendChild 來避免沖突。

import ClickOverlay from './ClickOverlay.js'

/* CSS */
// .unselect-helper {
//  position: fixed; left: -100vw; top: -100vh;
//  width: 200vw; height: 200vh;
// }
// .selected {outline: 1px solid black}

export default class ResizeOverlay extends ClickOverlay {
    _init(_opts) {
        this.enterListener = () => this.onEnter()
        this.innerElement.addEventListener('mouseenter', this.enterListener)
        this.leaveListener = () => this.onLeave()
        this.innerElement.addEventListener('mouseleave', this.leaveListener)
        this.selectListener = () => {
            if (this.unselectHelper)
                return
            this.unselectHelper = document.createElement('div')
            this.unselectHelper.classList.add('unselect-helper')
            this.unselectListener = () => {
                if (this.mouseInside)
                    return
                this.clearUnselectHelper()
                this.onUnselect()
            }
            this.unselectHelper.addEventListener('pointerdown'
                , this.unselectListener)
            this.outerElement.parentNode.appendChild(this.unselectHelper)
            this.onSelect()
        }
        this.innerElement.addEventListener('pointerup', this.selectListener)
    }

    _release() {
        this.innerElement.removeEventListener('mouseenter', this.enterListener)
        this.innerElement.removeEventListener('mouseleave', this.leaveListener)
        this.innerElement.removeEventListener('pointerup', this.selectListener)
        this.clearUnselectHelper()
    }

    clearUnselectHelper() {
        if (!this.unselectHelper)
            return
        this.unselectHelper.removeEventListener('pointerdown'
            , this.unselectListener)
        this.unselectHelper.remove()
        delete this.unselectListener
        delete this.unselectHelper
    }

    onEnter() {
        this.mouseInside = true
    }

    onLeave() {
        delete this.mouseInside
    }

    onSelect() {
        this.innerElement.classList.add('selected')
    }

    onUnselect() {
        this.innerElement.classList.remove('selected')
    }
}

最簡單的方法: mouseleave(function())

更多信息: https ://www.w3schools.com/jquery/jquery_events.asp

您可以將 tabindex 設置為DOM元素。 當用戶在 DOM 元素外部單擊時,這將觸發模糊事件。

演示

<div tabindex="1">
    Focus me
</div>

document.querySelector("div").onblur = function(){
   console.log('clicked outside')
}
document.querySelector("div").onfocus = function(){
   console.log('clicked inside')
}
 <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();
}

這是我想出的最簡單的解決方案。

試試這個代碼:

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:

<label>包圍菜單並獲取焦點狀態更改。

http://jsfiddle.net/bK3gL/

另外:您可以通過Tab展開菜單。

使用非():

$("#id").not().click(function() {
    alert('Clicked other that #id');
});
$("body > div:not(#dvid)").click(function (e) {
    //your code
}); 
$("html").click(function(){
    if($('#info').css("opacity")>0.9) {
        $('#info').fadeOut('fast');
    }
});

這是一個經典案例,其中對 HTML 進行調整會是更好的解決方案。 為什么不對不包含菜單項的元素設置點擊? 然后你不需要添加傳播。

$('.header, .footer, .main-content').click(function() {
//Hide the menus if visible
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM