요소 외부의 클릭을 어떻게 감지합니까?


2486

HTML 메뉴가 있는데 사용자가이 메뉴의 헤드를 클릭하면 완전히 표시됩니다. 사용자가 메뉴 영역 바깥을 클릭하면 이러한 요소를 숨기고 싶습니다.

jQuery로 이와 같은 것이 가능합니까?

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

47
다음은이 전략의 예입니다. jsfiddle.net/tedp/aL7Xe/1
Ted

18
Tom이 언급 했듯이이 방법을 사용하기 전에 css-tricks.com/dangers-stopping-event-propagation 을 읽으 십시오 . 그 jsfiddle 도구는 꽤 멋지다.
Jon Coombs 2016 년

3
요소에 대한 참조를 얻은 다음 event.target을 가져 와서 마지막으로! = 또는 == 둘 다 코드를 실행하십시오.
Rohit Kumar


2
바닐라 JS 용액 으로 event.target하고 없이 event.stopPropagation .
lowtechsun

답변:


1812

참고 : 사용 stopEventPropagation()은 DOM에서 정상적인 이벤트 흐름을 방해하므로 피해야합니다. 자세한 내용은 이 기사 를 참조하십시오. 사용을 고려 이 방법을 대신

클릭 이벤트를 문서 본문에 첨부하여 창을 닫습니다. 별도의 클릭 이벤트를 컨테이너에 첨부하여 문서 본문으로의 전파를 중지하십시오.

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

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

708
이것은 #menucontainer에 포함 된 버튼과 링크를 포함한 많은 것들의 표준 동작을 깨뜨립니다. 이 답변이 인기가 있다는 것이 놀랍습니다.
Art

75
#menucontainer 내부의 모든 동작이 중단되지 않습니다. 이는 내부의 어떤 것도 전파 체인의 맨 아래에 있기 때문입니다.
Eran Galperin 2016 년

94
그것의 beautyfull하지만 당신은 $('html').click()몸을 사용 하지 않아야합니다 . 몸은 항상 내용의 높이를 가지고 있습니다. 내용이 많지 않거나 화면이 매우 높으며 본문이 채워진 부분에서만 작동합니다.
meo

103
또한이 솔루션이 너무 많은 표를 얻은 것에 놀랐습니다. stopPropagation jsfiddle.net/Flandre/vaNFw/3
Andre

140
필립 월튼 (Philip Walton)은 왜이 답변이 최상의 솔루션이 아닌지를 잘 설명합니다. css-tricks.com/dangers-stopping-event-propagation
Tom

1386

클릭 이벤트를 수신 document한 다음 #menucontainer을 사용하여 클릭 한 요소의 조상 또는 대상이 아닌지 확인할 수 있습니다 .closest().

그렇지 않은 경우 클릭 한 요소가 외부에 #menucontainer있으며 안전하게 숨길 수 있습니다.

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

편집 – 2017-06-23

메뉴를 닫고 이벤트 수신을 중지하려는 경우 이벤트 리스너 후에 정리할 수도 있습니다. 이 함수는의 다른 클릭 리스너를 유지하면서 새로 생성 된 리스너 만 정리합니다 document. ES2015 구문으로 :

export function hideOnClickOutside(selector) {
  const outsideClickListener = (event) => {
    $target = $(event.target);
    if (!$target.closest(selector).length && $(selector).is(':visible')) {
        $(selector).hide();
        removeClickListener();
    }
  }

  const removeClickListener = () => {
    document.removeEventListener('click', outsideClickListener)
  }

  document.addEventListener('click', outsideClickListener)
}

편집 – 2018-03-11

jQuery를 사용하지 않으려는 사람들을 위해. 다음은 일반 vanillaJS (ECMAScript6)의 위 코드입니다.

function hideOnClickOutside(element) {
    const outsideClickListener = event => {
        if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
          element.style.display = 'none'
          removeClickListener()
        }
    }

    const removeClickListener = () => {
        document.removeEventListener('click', outsideClickListener)
    }

    document.addEventListener('click', outsideClickListener)
}

const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ) // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js 

참고 : 이것은 !element.contains(event.target)jQuery 부분 대신 사용하기 위해 Alex 주석을 기반으로 합니다.

그러나 element.closest()모든 주요 브라우저에서도 사용할 수 있습니다 (W3C 버전은 jQuery 버전과 약간 다릅니다). 폴리 필은 여기에서 찾을 수 있습니다. Element.closest ()

편집 – 2020-05-21

사용자가 요소 내부를 클릭하고 드래그 할 수있게하려면 요소를 닫지 않고 요소 외부에서 마우스를 놓습니다.

      ...
      let lastMouseDownX = 0;
      let lastMouseDownY = 0;
      let lastMouseDownWasOutside = false;

      const mouseDownListener = (event: MouseEvent) => {
        lastMouseDownX = event.offsetX
        lastMouseDownY = event.offsetY
        lastMouseDownWasOutside = !$(event.target).closest(element).length
      }
      document.addEventListener('mousedown', mouseDownListener);

그리고 outsideClickListener:

const outsideClickListener = event => {
        const deltaX = event.offsetX - lastMouseDownX
        const deltaY = event.offsetY - lastMouseDownY
        const distSq = (deltaX * deltaX) + (deltaY * deltaY)
        const isDrag = distSq > 3
        const isDragException = isDrag && !lastMouseDownWasOutside

        if (!element.contains(event.target) && isVisible(element) && !isDragException) { // or use: event.target.closest(selector) === null
          element.style.display = 'none'
          removeClickListener()
          document.removeEventListener('mousedown', mouseDownListener); // Or add this line to removeClickListener()
        }
    }

28
나는 다른 많은 답변을 시도했지만 이것 하나만 효과가있었습니다. 감사. 내가 사용한 코드는 다음과 같습니다. $ (document) .click (function (event) {if ($ (event.target) .closest ( '. window'). length == 0) {$ ( '. window' ) .fadeOut ( 'fast');}});
Pistos

39
실제로이 솔루션을 사용하면 동일한 페이지에서 여러 메뉴를 더 잘 지원하므로 첫 번째 메뉴가 열려있는 동안 두 번째 메뉴를 클릭하면 stopPropagation 솔루션에서 첫 번째 메뉴가 열린 상태로 유지됩니다.
umassthrower

13
훌륭한 답변입니다. 닫으려는 여러 항목이있을 때가는 방법입니다.
John

5
다른 솔루션에 event.stopPropagation ()의 결함으로 인해 허용되는 답변이어야합니다.
토마토

20
jQuery를하지 않고 - !element.contains(event.target)사용 Node.contains를 ()
알렉스 로스

303

요소 외부의 클릭을 감지하는 방법은 무엇입니까?

이 질문이 인기가 많고 답변이 많은 이유는 매우 복잡하기 때문입니다. 거의 8 년 동안 수십 번의 답변을받은 후, 접근성에 대한 관리가 거의 이루어지지 않은 것을보고 정말 놀랐습니다.

사용자가 메뉴 영역 바깥을 클릭하면 이러한 요소를 숨기고 싶습니다.

이것은 고귀한 원인이며 실제 문제입니다. 대부분의 답변이 해결하려고 시도하는 것으로 보이는 질문 제목에는 불행한 붉은 청어가 들어 있습니다.

힌트 : "클릭" 이라는 단어입니다 !

실제로 클릭 핸들러를 바인딩하고 싶지는 않습니다.

클릭 핸들러를 바인딩하여 대화 상자를 닫는 경우 이미 실패한 것입니다. 실패한 이유는 모든 사람이 click이벤트를 트리거하지 않기 때문 입니다. 마우스를 사용하지 않는 사용자는을 눌러 대화 상자를 벗어날 수 있으며 팝업 메뉴는 대화 상자의 일 종일 Tab수 있습니다. 그러면 click이벤트를 트리거하지 않으면 대화 상자 뒤의 내용을 읽을 수 없습니다 .

그럼 질문을 바꿔 봅시다.

사용자가 대화 상자를 마치면 어떻게 대화 상자를 닫습니까?

이것이 목표입니다. 불행히도 이제 userisfinishedwiththedialog이벤트 를 바인딩해야하며 바인딩은 그렇게 간단하지 않습니다.

그렇다면 사용자가 대화 상자 사용을 마쳤 음을 어떻게 알 수 있습니까?

focusout 행사

초점이 대화 상자를 떠 났는지 확인하는 것이 좋습니다.

힌트 : blur이벤트에 주의를 기울이고 blur이벤트가 버블 링 단계에 바인딩 된 경우 전파되지 않습니다!

jQuery focusout는 잘 작동합니다. jQuery를 사용할 수없는 경우 blur캡처 단계에서 사용할 수 있습니다 .

element.addEventListener('blur', ..., true);
//                       use capture: ^^^^

또한 많은 대화 상자에서 컨테이너가 포커스를 얻도록 허용해야합니다. tabindex="-1"탭하여 흐름을 방해하지 않으면 서 대화 상자가 동적으로 초점을받을 수 있도록 추가 합니다.

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on('focusout', function () {
  $(this).removeClass('active');
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>


그 데모를 1 분 이상 플레이하면 문제를 빨리보기 시작해야합니다.

첫 번째는 대화 상자의 링크를 클릭 할 수 없다는 것입니다. 해당 탭이나 탭을 클릭하면 상호 작용이 시작되기 전에 대화 상자가 닫힙니다. 내부 요소에 초점을 맞추면 이벤트가 다시 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({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});


Esc

포커스 상태를 처리하여 완료했다고 생각되면 사용자 경험을 단순화하기 위해 더 많은 것을 할 수 있습니다.

이것은 종종 "좋은"기능이지만, 모달이나 팝업이있을 때 Esc키가 닫히는 것이 일반적입니다.

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


대화 상자에 포커스 가능한 요소가 있다는 것을 알고 있으면 대화 상자에 직접 초점을 맞출 필요가 없습니다. 메뉴를 작성하는 경우 첫 번째 메뉴 항목에 초점을 맞출 수 있습니다.

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


WAI-ARIA 역할 및 기타 접근성 지원

이 답변은 희망이 기능에 대한 접근 키보드와 마우스 지원의 기초를 다루고 있지만, 이미 꽤 상당한 규모의 나는이 모든 논의를 피하기 위해거야 WAI-ARIA 역할과 특성을 그러나 나는 매우 구현 세부 사항에 대한 사양을 참조하는 것이 좋습니다 그들이 어떤 역할을 사용해야하는지 그리고 다른 적절한 속성에 대해


29
설명과 접근성을 염두에두고 가장 완벽한 답변입니다. 다른 답변의 대부분은 클릭 만 처리하고 설명없이 코드 스 니펫이 삭제되었으므로 이것이 허용되는 답변이어야한다고 생각합니다.
Cyrille

4
훌륭하고 잘 설명되어 있습니다. 방금 React Component에이 접근법을 사용했고 완벽하게 감사했습니다.
David Lavieri

2
@ zzzzBov에 대한 깊은 답변에 감사드립니다. 바닐라 JS에서 그것을 달성하려고 노력하고 있으며 모든 jquery 항목으로 약간 잃어 버렸습니다. 바닐라 js에 비슷한 것이 있습니까?
HendrikEng

2
@ zzzzBov 아니오 나는 jQuery가없는 버전의 코스를 작성하려고하지 않습니다. 나는 그것을 시도 할 것이고 최선을 다하면 여기서 정말로 새로운 질문을하는 것이 좋습니다. 다시 한번 감사드립니다.
HendrikEng

7
이 방법은 사용자 지정 드롭 다운 또는 기타 입력에서 클릭을 감지하는 훌륭한 방법이지만 두 가지 이유로 모달이나 팝업에 대해 선호되는 방법이되어서는 안됩니다. 모달은 사용자가 다른 탭이나 창으로 전환하거나 닫히는 상황에 맞는 메뉴를 열 때 닫힙니다. 또한 'click'이벤트는 마우스를 올리면 발생하고 'focusout'이벤트는 마우스를 누르는 순간을 트리거합니다. 일반적으로 버튼은 마우스 버튼을 눌렀다 놓아도 동작을 수행합니다. 모달에 대해 적절하고 액세스 가능한 방법은 탭 가능한 닫기 버튼을 추가하는 것입니다.
Kevin

144

여기에있는 다른 솔루션은 저에게 효과적이지 않으므로 사용해야했습니다.

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

나는 자신의 팝업 상자에 html 핸들러를 삽입 할 때 다른 Jquery UI 위젯을 클릭하지 않고 다른 Jquery UI 위젯을 트리거하지 않도록 event.target을 사용하는 방법에 대한 실제적인 예를 게시 했습니다. 원래 대상을 얻는 가장 좋은 방법
Joey T

43
이것은 자식 요소가 클릭 될 때 메뉴를 닫지 않도록 명령문 && !$(event.target).parents("#foo").is("#foo")안에 추가 한 것을 제외하고는 나를 위해 일했습니다 IF.
honyovk

1
더 이상 찾을 수 없습니다 : // 우리는 $ (event.target) .parents ( '# foo'). length == 0
밖에

3
딥 네 스팅을 처리하기위한 간결한 개선은 사용하는 .is('#foo, #foo *')것이지만 이 문제를 해결하기 위해 클릭 핸들러를 바인딩하지 않는 것이 좋습니다 .
zzzzBov

1
!$(event.target).closest("#foo").length@honyovk의 추가가 더 좋을 것입니다.
tvanc

127

메뉴를 열 때 클릭 이벤트를 본문에 첨부한다는 점을 제외하고는 Eran의 예제와 비슷하게 작동하는 응용 프로그램이 있습니다 ...

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

  event.stopPropagation();
});

jQuery의 기능 에 대한 추가 정보one()


9
그러나 메뉴 자체를 클릭하고 외부를 클릭하면 작동하지 않습니다 :)
vsync

3
클릭 리스너를 본문에 바인딩하기 전에 event.stopProgagantion ()을 넣는 데 도움이됩니다.
재스퍼 케니스

4
이것의 문제점은 "하나"가 배열에 이벤트를 여러 번 추가하는 jQuery 메소드에 적용된다는 것입니다. 따라서 메뉴를 클릭하여 두 번 이상 열면 이벤트가 본문에 다시 바인딩되고 메뉴를 여러 번 숨기려고 시도합니다. 이 문제를 해결하려면 안전 장치를 적용해야합니다.
marksyzm 2016 년

핸들러 .one내부에서를 사용하여 바인딩 한 후 $('html')a를 작성하십시오 $('html').off('click').
Cody

4
@Cody 나는 그것이 도움이되지 않는다고 생각합니다. one핸들러가 자동으로 호출합니다 off(그것이 jQuery를 워드 프로세서에 표시된 것 같이).
Mariano Desanze

44

연구 후 3 가지 작동 솔루션을 찾았습니다 (참조를 위해 페이지 링크를 잊었습니다)

첫 번째 해결책

<script>
    //The good thing about this solution is it doesn't stop event propagation.

    var clickFlag = 0;
    $('body').on('click', function () {
        if(clickFlag == 0) {
            console.log('hide element here');
            /* Hide element here */
        }
        else {
            clickFlag=0;
        }
    });
    $('body').on('click','#testDiv', function (event) {
        clickFlag = 1;
        console.log('showed the element');
        /* Show the element */
    });
</script>

두 번째 해결책

<script>
    $('body').on('click', function(e) {
        if($(e.target).closest('#testDiv').length == 0) {
           /* Hide dropdown here */
        }
    });
</script>

세번째 해결책

<script>
    var specifiedElement = document.getElementById('testDiv');
    document.addEventListener('click', function(event) {
        var isClickInside = specifiedElement.contains(event.target);
        if (isClickInside) {
          console.log('You clicked inside')
        }
        else {
          console.log('You clicked outside')
        }
    });
</script>

8
세 번째 솔루션은 가장 우아한 검사 방법입니다. 또한 jQuery의 오버 헤드를 포함하지 않습니다. 아주 좋아요 많은 도움이되었습니다. 감사.
dbarth

document.getElementsByClassName누군가가 실마리를 가지고 있다면 여러 요소로 세 번째 솔루션을 얻으려고합니다 .
lowtechsun

@lowtechsun 당신은 각각을 확인하기 위해 반복해야합니다.
Donnie D' Amato

세 번째 솔루션을 정말 좋아했지만 div가 클릭하기 전에 클릭이 발생하여 다시 숨기는 이유가 무엇인지 알 수 있습니까?
indiehjaerta

세 번째는 console.log를 허용하지만, display를 none으로 설정하여 실제로 닫을 수는 없습니다. 메뉴가 표시되기 전에이를 수행하기 때문입니다. 이에 대한 해결책이 있습니까?
RolandiXor

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

나를 위해 잘 작동합니다.


3
이것이 내가 사용하는 것입니다. 완벽하지는 않지만 취미 프로그래머로서 명확하게 이해할 수있을 정도로 단순합니다.
kevtrout

흐림 효과는 #menucontainer클릭에 대한 의문의 여지 가없는 움직임입니다.
borrel

4
@borrel blur는 컨테이너 외부로의 이동 이 아닙니다 . 흐림은 초점의 반대입니다. 마우스 아웃을 생각하고 있습니다. 이 솔루션은 클릭시 일반 텍스트와 입력 필드 사이를 전환하는 "클릭하여 편집"텍스트를 만들 때 특히 효과적이었습니다.
parker.sikand

나는 추가했다 tabindex="-1"받는 #menuscontainer그것이 작동되도록 할 수 있습니다. 컨테이너 안에 입력 태그를 넣고 클릭하면 컨테이너가 숨겨지는 것 같습니다.
tyrion

MouseLeave 이벤트는 메뉴 및 용기 적합하다 (참조 : w3schools.com/jquery/... )
Evgenia 마놀로

38

이제 외부 플러그인 ( 블로그 게시물 )을 위한 플러그인이 있습니다.

클릭 아웃 핸들러 (WLOG)가 요소에 바인딩 된 경우 다음이 발생합니다 .

  • 요소는 clickoutside 핸들러로 모든 요소를 ​​보유하는 배열에 추가됩니다.
  • ( 네임 스페이스가 지정된 ) 클릭 핸들러가 문서에 바인딩되어 있습니다 (아직없는 경우).
  • 임의의 클릭 문서의 clickoutside의 이벤트가 동일하지 않은 것을 배열의 해당 요소 또는 부모의 트리거 클릭 - 이벤트 대상
  • 또한 clickoutside 이벤트 의 event.target 은 사용자가 클릭 한 요소로 설정되므로 사용자가 외부를 클릭 한 것이 아니라 사용자가 클릭 한 것을 알 수 있습니다.

따라서 이벤트가 전파에서 중지되지 않으며 추가 클릭 핸들러가 외부 핸들러와 함께 요소 위에 "위"로 사용될 수 있습니다.


좋은 플러그인. "외부 이벤트"에 대한 링크는 사용하지 않는 반면 블로그 게시물 링크는 살아 있고 "clickoutside"종류의 이벤트에 매우 유용한 플러그인을 제공합니다. 또한 라이센스가 MIT입니다.
TechNyquist

훌륭한 플러그인. 완벽하게 작동했습니다. 사용법은 다음과 같습니다.$( '#element' ).on( 'clickoutside', function( e ) { .. } );
Gavin

33

이것은 나를 위해 완벽하게 일했다!!

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

이 솔루션은 내가하고있는 일에 잘 작동했습니다. 여기서 클릭 이벤트가 계속 발생해야했습니다. 감사합니다.
Mike

26

실제로 필요한 것은 사용자가 외부를 클릭 할 때 메뉴를 닫는 것이라고 생각하지 않습니다. 필요한 것은 사용자가 페이지의 아무 곳이나 클릭하면 메뉴가 닫히는 것입니다. 메뉴를 클릭하거나 메뉴를 해제하면 메뉴가 제대로 닫힙니 까?

위의 만족스러운 답변을 찾지 못하면 다른 날 이 블로그 게시물 을 작성하라는 메시지가 표시 됩니다. 더 pedantic을 위해, 주목해야 할 많은 것들이 있습니다 :

  1. 클릭시 클릭 이벤트 핸들러를 본문 요소에 첨부하는 경우 메뉴를 닫고 이벤트 바인딩을 해제하기 전에 두 번째 클릭을 기다려야합니다. 그렇지 않으면 메뉴를 연 클릭 이벤트가 메뉴를 닫아야하는 청취자에게 버블 링됩니다.
  2. click 이벤트에서 event.stopPropogation ()을 사용하면 페이지의 다른 어떤 요소도 클릭 위치 닫기 기능을 가질 수 없습니다.
  3. 클릭 이벤트 핸들러를 본문 요소에 무기한으로 첨부하는 것은 수행 솔루션이 아닙니다.
  4. 이벤트의 대상과 부모를 처리기의 작성자와 비교하면 원하는 것은 메뉴를 클릭하면 메뉴를 닫고, 원하는 것은 페이지의 아무 곳이나 클릭하면 메뉴를 닫는 것으로 가정합니다.
  5. body 요소에서 이벤트를 수신하면 코드가 더 부서지기 쉽습니다. 이처럼 무고한 스타일링 :body { margin-left:auto; margin-right: auto; width:960px;}

2
"메뉴를 클릭하거나 메뉴를 해제하면 메뉴가 제대로 닫힙니 까?" 항상 그런 것은 아닙니다. 요소를 끌어서 클릭을 취소하면 여전히 문서 레벨 클릭이 트리거되지만 메뉴를 계속 닫으려는 의도는 없습니다. 내부 클릭을 허용하는 "클릭 아웃"동작을 사용할 수있는 다른 유형의 대화 상자도 많이 있습니다.
zzzzBov

25

다른 포스터가 말했듯이, 특히 표시하는 요소 (이 경우 메뉴)에 대화 형 요소가있는 경우 많은 문제가 있습니다. 다음 방법이 상당히 강력하다는 것을 알았습니다.

$('#menuscontainer').click(function(event) {
    //your code that shows the menus fully

    //now set up an event listener so that clicking anywhere outside will close the menu
    $('html').click(function(event) {
        //check up the tree of the click target to check whether user has clicked outside of menu
        if ($(event.target).parents('#menuscontainer').length==0) {
            // your code to hide menu

            //this event listener has done its job so we can unbind it.
            $(this).unbind(event);
        }

    })
});

25

상황에 대한 간단한 해결책은 다음과 같습니다.

$(document).mouseup(function (e)
{
    var container = $("YOUR SELECTOR"); // Give you class or ID

    if (!container.is(e.target) &&            // If the target of the click is not the desired div or section
        container.has(e.target).length === 0) // ... nor a descendant-child of the container
    {
        container.hide();
    }
});

위의 스크립트는 click 이벤트 div외부 div가 트리거 된 경우 if 를 숨 깁니다 .

자세한 내용은 다음 블로그를 참조하십시오. http://www.codecanal.com/detect-click-outside-div-using-javascript/


22

해결책 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
    }
});

이 솔루션을 부울 플래그와 함께 사용했으며 관절 식 DOm에서도 유용하며 #menucontainer 내부에 다른 많은 요소가있는 경우
Migio B

솔루션 1은 이벤트가 문서에 전파 될 때 클릭 대상이 DOM에서 제거되는 경우를 처리하므로 더 효과적입니다.
앨리스

21

창 클릭 이벤트 대상을 확인하고 (다른 곳에서 캡처되지 않는 한 창으로 전파되어야 함) 메뉴 요소가 아닌지 확인하십시오. 그렇지 않은 경우 메뉴 외부에있는 것입니다.

또는 클릭 위치를 확인하고 메뉴 영역 내에 포함되어 있는지 확인하십시오.


18

나는 이런 식으로 성공했습니다 :

var $menuscontainer = ...;

$('#trigger').click(function() {
  $menuscontainer.show();

  $('body').click(function(event) {
    var $target = $(event.target);

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

논리는 다음과 같습니다. #menuscontainer이 표시되면 클릭 #menuscontainer의 대상이 대상의 하위 항목이 아닌 경우에만 숨길 수 있는 클릭 핸들러를 본문에 바인딩 합니다.


17

변형으로 :

var $menu = $('#menucontainer');
$(document).on('click', function (e) {

    // If element is opened and click target is outside it, hide it
    if ($menu.is(':visible') && !$menu.is(e.target) && !$menu.has(e.target).length) {
        $menu.hide();
    }
});

이벤트 전파 중지에 아무런 문제가 없으며 첫 번째 메뉴가 열려있는 동안 두 번째 메뉴를 클릭하면 첫 번째가 stopPropagation 솔루션에 열려있는 동일한 페이지에서 여러 메뉴를 더 잘 지원합니다.


16

이벤트에는 "트리 순서로 모든 조상의 정적 순서 목록"인 요소의 event.path 속성이 있습니다. 이벤트가 특정 DOM 요소 또는 해당 하위 요소 중 하나에서 발생했는지 확인하려면 해당 특정 DOM 요소의 경로를 확인하십시오. 또한 함수 OR에서 요소 검사를 논리적으로 수행 하여 여러 요소를 검사하는 데 사용할 수 있습니다 some.

$("body").click(function() {
  target = document.getElementById("main");
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  })
  if (flag) {
    console.log("Inside")
  } else {
    console.log("Outside")
  }
});
#main {
  display: inline-block;
  background:yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
  <ul>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
  </ul>
</div>
<div id="main2">
  Outside Main
</div>

따라서 귀하의 경우에는

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

1
Edge에서는 작동하지 않습니다.
Legends

event.path일이 아닙니다.
앤드류

14

일부 jQuery 캘린더 플러그인 에서이 방법을 찾았습니다.

function ClickOutsideCheck(e)
{
  var el = e.target;
  var popup = $('.popup:visible')[0];
  if (popup==undefined)
    return true;

  while (true){
    if (el == popup ) {
      return true;
    } else if (el == document) {
      $(".popup").hide();
      return false;
    } else {
      el = $(el).parent()[0];
    }
  }
};

$(document).bind('mousedown.popup', ClickOutsideCheck);

13

미래 시청자를위한 바닐라 JavaScript 솔루션은 다음과 같습니다.

문서 내의 요소를 클릭 할 때 클릭 한 요소의 ID가 토글되거나 숨겨진 요소가 숨겨지지 않고 숨겨진 요소에 클릭 된 요소가 포함되어 있지 않으면 요소를 토글하십시오.

(function () {
    "use strict";
    var hidden = document.getElementById('hidden');
    document.addEventListener('click', function (e) {
        if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
    }, false);
})();

같은 페이지에 여러 개의 토글이있을 경우 다음과 같이 사용할 수 있습니다.

  1. hidden접을 수있는 항목에 클래스 이름 을 추가하십시오 .
  2. 문서를 클릭하면 클릭 한 요소가없고 숨겨지지 않은 모든 숨겨진 요소를 닫습니다.
  3. 클릭 한 요소가 토글 인 경우 지정된 요소를 토글합니다.

(function () {
    "use strict";
    var hiddenItems = document.getElementsByClassName('hidden'), hidden;
    document.addEventListener('click', function (e) {
        for (var i = 0; hidden = hiddenItems[i]; i++) {
            if (!hidden.contains(e.target) && hidden.style.display != 'none')
                hidden.style.display = 'none';
        }
        if (e.target.getAttribute('data-toggle')) {
            var toggle = document.querySelector(e.target.getAttribute('data-toggle'));
            toggle.style.display = toggle.style.display == 'none' ? 'block' : 'none';
        }
    }, false);
})();
<a href="javascript:void(0)" data-toggle="#hidden1">Toggle Hidden Div</a>
<div class="hidden" id="hidden1" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden2">Toggle Hidden Div</a>
<div class="hidden" id="hidden2" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden3">Toggle Hidden Div</a>
<div class="hidden" id="hidden3" style="display: none;" data-hidden="true">This content is normally hidden</div>


13

실제로 아무도 인정하지 않은 focusout이벤트에 놀랐습니다 .

var button = document.getElementById('button');
button.addEventListener('click', function(e){
  e.target.style.backgroundColor = 'green';
});
button.addEventListener('focusout', function(e){
  e.target.style.backgroundColor = '';
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <button id="button">Click</button>
</body>
</html>



이것은 바로 대답으로 받아 들여 져야합니다. 감사!!!
럭키 램

8

IE 및 FF 3. *를 스크립팅하고 특정 상자 영역 내에서 클릭이 발생했는지 알고 싶다면 다음과 같이 사용할 수도 있습니다.

this.outsideElementClick = function(objEvent, objElement){   
var objCurrentElement = objEvent.target || objEvent.srcElement;
var blnInsideX = false;
var blnInsideY = false;

if (objCurrentElement.getBoundingClientRect().left >= objElement.getBoundingClientRect().left && objCurrentElement.getBoundingClientRect().right <= objElement.getBoundingClientRect().right)
    blnInsideX = true;

if (objCurrentElement.getBoundingClientRect().top >= objElement.getBoundingClientRect().top && objCurrentElement.getBoundingClientRect().bottom <= objElement.getBoundingClientRect().bottom)
    blnInsideY = true;

if (blnInsideX && blnInsideY)
    return false;
else
    return true;}

8

흐름 중단, 흐림 / 초점 이벤트 또는 기타 까다로운 기술을 사용하는 대신 이벤트 흐름을 요소의 친밀감과 일치시키기 만하면됩니다.

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

외부 이벤트 리스너 클릭을 제거하려면 다음을 수행하십시오.

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

나에게 이것은 최고의 솔루션이었다. 애니메이션 후 콜백에서 사용했기 때문에 분리 이벤트가 필요했습니다. +1
qwertzman

여기에 중복 검사가 있습니다. 요소가 실제로 있다면 #menuscontainer여전히 부모입니다. 먼저이를 확인하고 해당 요소가 아닌 경우 DOM 트리로 이동하십시오.
vsync

권리 ! 조건을으로 변경할 수 있습니다 if(!($(event.target).is("#menuscontainer") || $(event.target).parents().is("#menuscontainer"))){. 그것은 작은 최적화이지만, click.menu-outside이벤트가 등록 되면 각 클릭에 대해 프로그램 수명 동안 몇 번만 발생합니다 . 더 길고 (+32 자) 메소드 체인을 사용하지 않습니다
mems

8

사용하다:

var go = false;
$(document).click(function(){
    if(go){
        $('#divID').hide();
        go = false;
    }
})

$("#divID").mouseover(function(){
    go = false;
});

$("#divID").mouseout(function (){
    go = true;
});

$("btnID").click( function(){
    if($("#divID:visible").length==1)
        $("#divID").hide(); // Toggle
    $("#divID").show();
});

7

여기에 궁금한 사람이 자바 스크립트 솔루션 (es6) 인 경우 :

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

다음과 같은 경우 es5

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

});


7

순수한 자바 스크립트에 의한 간단한 해결책은 다음과 같습니다. 그것은이다 최신 상태 ES6과 :

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

"ES6의 최신 버전"은 ES6의 최신 버전이 () => {}대신 하는 것만으로도 대담한 주장 입니다 function() {}. ES6의 트위스트를 통해 일반 JavaScript로 분류됩니다.
MortenMoulder

@ MortenMoulder : 예. 실제로 ES6 인 경우에도주의를 기울입니다. 그러나 해결책을보십시오. 나는 그것이 좋다고 생각한다.
Duannx

바닐라 JS이며 DOM에서 제거 된 이벤트 대상에 대해 작동합니다 (예 : 내부 팝업의 값을 선택하고 팝업을 즉시 닫음). 나에게서 +1!
앨리스

6

클릭 이벤트 리스너를 문서에 연결하십시오. 이벤트 리스너 내에서 이벤트 객체 , 특히 event.target 을보고 클릭 한 요소를 확인할 수 있습니다.

$(document).click(function(e){
    if ($(e.target).closest("#menuscontainer").length == 0) {
        // .closest can help you determine if the element 
        // or one of its ancestors is #menuscontainer
        console.log("hide");
    }
});

6

아래 스크립트를 사용하고 jQuery를 사용했습니다.

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

아래에서 HTML 코드를 찾으십시오

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

여기 에서 튜토리얼을 읽을 수 있습니다


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

문서를 클릭하면 동일한 요소를 클릭하지 않는 한 주어진 요소를 숨 깁니다.


5

가장 인기있는 답변을지지하지만 추가

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

따라서 스크롤 막대를 클릭해도 대상 요소가 [숨겨 지거나 무엇이든]되지 않습니다.


5

더 사용하기 쉽고 표현력이 좋은 코드를 위해 다음과 같은 jQuery 플러그인을 만들었습니다.

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

참고 : target 은 사용자가 실제로 클릭 한 요소입니다. 그러나 콜백은 여전히 ​​원래 요소의 컨텍스트에서 실행되므로 jQuery 콜백에서 예상대로 이를 활용할 수 있습니다 .

플러그인:

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

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

    });
    return context;
};

기본적으로 클릭 이벤트 리스너는 문서에 배치됩니다. 그러나 이벤트 리스너 범위를 제한하려는 경우 클릭을 청취 할 최상위 상위 요소가 될 상위 레벨 요소를 나타내는 jQuery 객체를 전달할 수 있습니다. 이것은 불필요한 문서 레벨 이벤트 리스너를 방지합니다. 제공된 상위 요소가 초기 요소의 상위 요소가 아니면 작동하지 않습니다.

다음과 같이 사용하십시오.

$('div.my-element').clickOut($('div.my-parent'), function(target) { 
    //do something here...
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.