65

Suppose I have one div in my page. how to detect the user click on div content or outside of div content through JavaScript or JQuery. please help with small code snippet. thanks.

Edit: As commented in one of the answers below, I only want to attach an event handler to my body, and also want to know which element was clicked upon.

13 Answers 13

147

Here's a one liner that doesn't require jquery using Node.contains:

// Get arbitrary element with id "my-element"
var myElementToCheckIfClicksAreInsideOf = document.querySelector('#my-element');
// Listen for click events on body
document.body.addEventListener('click', function (event) {
    if (myElementToCheckIfClicksAreInsideOf.contains(event.target)) {
        console.log('clicked inside');
    } else {
        console.log('clicked outside');
    }
});

If you're wondering about the edge case of checking if the click is on the element itself, Node.contains returns true for the element itself (e.g. element.contains(element) === true) so this snippet should always work.

Browser support seems to cover pretty much everything according to that MDN page as well.

Sign up to request clarification or add additional context in comments.

Comments

98

Using jQuery:

$(function() {
  $("body").click(function(e) {
    if (e.target.id == "myDiv" || $(e.target).parents("#myDiv").length) {
      alert("Inside div");
    } else {
      alert("Outside div");
    }
  });
})
#myDiv {
  background: #ff0000;
  width: 25vw;
  height: 25vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myDiv"></div>

5 Comments

This will not handle cases where the user clicks on a child of the div; it will report "outside div".
Nice. Your use of parents() has inspired me to update my answer to use more jQuery and less old-school traversal.
This will not work if there are elements outside the div that use stopPropagation, see example here: jsfiddle.net/Flandre/YdrTA
Andre, can't do much about that can't we?
Love that $(e.target).parents("#myDiv").length, I can memorize that, the other codes I found were much more complex and did not even work correctly, thank you so much!
14

Using jQuery, and assuming that you have <div id="foo">:

jQuery(function($){
  $('#foo').click(function(e){
    console.log( 'clicked on div' );
    e.stopPropagation(); // Prevent bubbling
  });
  $('body').click(function(e){
    console.log( 'clicked outside of div' );
  });
});

Edit: For a single handler:

jQuery(function($){
  $('body').click(function(e){
    var clickedOn = $(e.target);
    if (clickedOn.parents().andSelf().is('#foo')){
      console.log( "Clicked on", clickedOn[0], "inside the div" );
    }else{
      console.log( "Clicked outside the div" );
  });
});

6 Comments

thanks for your effort but i want it in different way. i want to capture the element id where click occurred and i want handle it at body level not want to attach a click event handler with div.
if i click on #foo that also triggers the body handlers because foo is inside the body
@amosrivera No, it doesn't, because the call to stopPropagation prevents it from bubbling. You can test this for yourself here: jsfiddle.net/Qh2MN
@Thomas I have updated my answer with a single handler on the body that tells you if the click was inside the div or outside.
Hello from 2021, where .andSelf() is deprecated. Instead, simply replace andSelf with addBack, and it should work the same. api.jquery.com/andSelf
|
13

Rather than using the jQuery .parents function (as suggested in the accepted answer), it's better to use .closest for this purpose. As explained in the jQuery api docs, .closest checks the element passed and all its parents, whereas .parents just checks the parents. Consequently, this works:

$(function() {
    $("body").click(function(e) {
        if ($(e.target).closest("#myDiv").length) {
            alert("Clicked inside #myDiv");
        } else { 
            alert("Clicked outside #myDiv");
        }
    });
})

Comments

8

What about this?

<style type="text/css">
div {border: 1px solid red; color: black; background-color: #9999DD;
width: 20em; height: 40em;}
</style>

<script type="text/javascript">
function sayLoc(e) {
  e = e || window.event;
  var tgt = e.target || e.srcElement;

  // Get top lef co-ords of div
  var divX = findPosX(tgt);
  var divY = findPosY(tgt);

  // Workout if page has been scrolled
  var pXo = getPXoffset();
  var pYo = getPYoffset();

  // Subtract div co-ords from event co-ords
  var clickX = e.clientX - divX + pXo;
  var clickY = e.clientY - divY + pYo;

  alert('Co-ords within div (x, y): ' + clickX + ', ' + clickY);
}

function findPosX(obj) {
  var curleft = 0;
  if (obj.offsetParent) {
    while (obj.offsetParent) {
      curleft += obj.offsetLeft;
      obj = obj.offsetParent;
    }
  } else if (obj.x) {
    curleft += obj.x;
  }
  return curleft;
}

function findPosY(obj) {
  var curtop = 0;
  if (obj.offsetParent) {
    while (obj.offsetParent) {
      curtop += obj.offsetTop;
      obj = obj.offsetParent;
    }
  } else if (obj.y) {
    curtop += obj.y;
  }
  return curtop;
}

function getPXoffset() {
  if (self.pageXOffset) {
    // all except Explorer
    return self.pageXOffset;
  } else if (
    document.documentElement &&
    document.documentElement.scrollTop
  ) {
    // Explorer 6 Strict
    return document.documentElement.scrollLeft;
  } else if (document.body) {
    // all other Explorers
    return document.body.scrollLeft;
  }
}

function getPYoffset() {
  if (self.pageYOffset) {
    // all except Explorer
    return self.pageYOffset;
  } else if (
    document.documentElement &&
    document.documentElement.scrollTop
  ) {
    // Explorer 6 Strict
    return document.documentElement.scrollTop;
  } else if (document.body) {
    // all other Explorers
    return document.body.scrollTop;
  }
}

</script>

<div onclick="sayLoc(event);"></div>

(from http://bytes.com/topic/javascript/answers/151689-detect-click-inside-div-mozilla, using the Google.)

3 Comments

That's an insane amount of code for something that can now be done in a single line: stackoverflow.com/a/28432139/288906
this is exactly what I need it, even the insane amount of code
@fregante This code accurately detects "Geometric inside", not "hierarchical inside".
3

This question can be answered with X and Y coordinates and without JQuery:

       var isPointerEventInsideElement = function (event, element) {
            var pos = {
                x: event.targetTouches ? event.targetTouches[0].clientX : event.clientX,
                y: event.targetTouches ? event.targetTouches[0].clientY : event.clientY
            };
            var rect = element.getBoundingClientRect();
            return  pos.x < rect.right && pos.x > rect.left && pos.y < rect.bottom && pos.y > rect.top;
        };

        document.querySelector('#my-element').addEventListener('click', function (event) {
           console.log(isPointerEventInsideElement(event, document.querySelector('#my-any-child-element')))
        });

1 Comment

I really like your approach, but since getBoundingClientRect() returns element's position relative to viewport, you have to compare it with event.clientX and event.clientY instead of event.pageX and event.pageY.
1

For bootstrap 4 this works for me.

$(document).on('click', function(e) {
    $('[data-toggle="popover"],[data-original-title]').each(function() {
        if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
            $(this).popover('hide')
        }
    });
});

working demo on jsfiddle link: https://jsfiddle.net/LabibMuhammadJamal/jys10nez/9/

1 Comment

this is the only one which work for me with BS 4.5, and can click on an anchor inside the popover content. thx
1

In vanilla javaScript - in ES6

(() => {
    document.querySelector('.parent').addEventListener('click', event => {
        alert(event.target.classList.contains('child') ? 'Child element.' : 'Parent element.');
    });
})();
.parent {
    display: inline-block;
    padding: 45px;
    background: lightgreen;
}
.child {
    width: 120px;
    height:60px;
    background: teal;
}
<div class="parent">
    <div class="child"></div>
</div>

Comments

1

most of the solutions fail the edge case i.e when any child of parentDiv is clicked and the child is removed from dom due to some necessary logic it should be treated as clickInside but parentDiv.contains(event.target) will be false and the solutions using this will treat it as click outside so using composePath() will give the expected behaviour:

const parentDiv = document.querySelector('#parentDiv')

document.addEventListener('click', (e) => {
  const isClickedInsideParent = e.composedPath().includes(parentDiv)

  if (isClickedInsideParent) {
    console.log('clicked inside parent')
  } else {
    console.log('clicked outside parent')
  } 
})

Comments

0

If you want to add a click listener in chrome console, use this

document.querySelectorAll("label")[6].parentElement.onclick = () => {console.log('label clicked');}

Comments

0
function closePopover(sel) {
    $('body').on('click', function(e) {
        if (!$(event.target).closest(sel+', .popover-body').length) {
            $(sel).popover('hide');
        }
    });
}

closePopover('#elem1'); closePopover('#elem2');

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
0

I found a slightly different approach to the most voted one, although concept is quite similar. I will use my sidebars code as example. In HTML I have stuff like this:

<dialog class="sidebar modal">
        <aside>
        </aside>
</dialog>

In CSS I have this:

/*Need this to reset padding, which is 1rem by default, so that dialog's content "covers" the whole element.*/
dialog {
    padding: 0;
}
/*Styling of child element, in my case I have `aside`*/
.sidebar aside {
    /*This can vary, but the point is to make sure that whatever child you have at covers completely matches the size of the dialog element.*/
    display: flex;
    flex-direction: column;
    justify-content: center;
    height: inherit;
    /*If you want to have some spacing around the main actual dialog content use padding, not margin, because if you click on the margin of the element, it will be considering clicking on the element "under" it, but padding will be treated as part of the element you are clicking on. In fact, depending on the above styling or other child elements, you may even need to use this.*/
    padding: 1rem;
}

Then in my JS code I have this:

//Handle dialog closure when clicking outside dialog content
const dialogs = document.querySelectorAll('dialog');
if (!empty(dialogs)) {
    dialogs.forEach((dialog) => {
        dialogInit(dialog);
    });
}

function dialogInit(dialog)
{
    //For my needs I check for class `modal`, but you can use any other class or do this for all dialog elements, if you know that all of them will be modal
    if (dialog.classList.contains('modal')) {
        dialog.addEventListener('click', (event) => {
            //If we setup styling correctly, clicking "outside" of the dialog means clicking on its margin. Since it is margin, event target is actually an element under the dialog element, which is its backdrop, which is a pseudo element, and thus it is still treated as clicking on dialog.
            if (event.target && event.target === dialog) {
                dialog.close();
            }
        });
    }
}

Again, the concept is practically same, but in reverse, and I think it may make it a bit more universal, because you are not limited to what type of child element you have in the dialog, as long as you set its size correctly.

Comments

-1

Instead of using the body you could create a curtain with z-index of 100 (to pick a number) and give the inside element a higher z-index while all other elements have a lower z-index than the curtain.

See working example here: http://jsfiddle.net/Flandre/6JvFk/

jQuery:

$('#curtain').on("click", function(e) {

    $(this).hide();
    alert("clicked ouside of elements that stand out");

});

CSS:

.aboveCurtain
{
    z-index: 200; /* has to have a higher index than the curtain */
    position: relative;
    background-color: pink;
}

#curtain
{
    position: fixed;
    top: 0px;
    left: 0px;
    height: 100%;
    background-color: black;
    width: 100%;
    z-index:100;   
    opacity:0.5 /* change opacity to 0 to make it a true glass effect */
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.