63

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.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
Thomas
  • 33,544
  • 126
  • 357
  • 626

12 Answers12

140

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.

Andrea Gherardi
  • 826
  • 1
  • 9
  • 18
KhalilRavanna
  • 5,768
  • 3
  • 28
  • 24
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>
DarkNeuron
  • 7,981
  • 2
  • 43
  • 48
amosrivera
  • 26,114
  • 9
  • 67
  • 76
  • 9
    This will not handle cases where the user clicks on a child of the div; it will report `"outside div"`. – Phrogz Jan 11 '11 at 17:48
  • Nice. Your use of `parents()` has inspired me to update my answer to use more jQuery and less old-school traversal. – Phrogz Jan 11 '11 at 18:07
  • 1
    This will not work if there are elements outside the div that use stopPropagation, see example here: http://jsfiddle.net/Flandre/YdrTA/ – Andre May 02 '12 at 19:34
  • 1
    Andre, can't do much about that can't we? – AlexG Jan 15 '13 at 16:24
  • 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! – buycanna.io Dec 07 '17 at 01:55
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" );
  });
});
Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • 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. – Thomas Jan 11 '11 at 17:44
  • if i click on #foo that also triggers the body handlers because foo is inside the body – amosrivera Jan 11 '11 at 17:45
  • 1
    @amosrivera No, it doesn't, because the call to `stopPropagation` prevents it from bubbling. You can test this for yourself here: http://jsfiddle.net/Qh2MN/ – Phrogz Jan 11 '11 at 17:51
  • @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. – Phrogz Jan 11 '11 at 17:52
  • Working with click as expected, but interestingly does not work with "touchend": when clicking inside and then dragging outside and releasing, the `clickedOn.is('#foo')` resolves to true. – Mister Smith Jul 15 '13 at 07:34
  • 2
    Hello from 2021, where `.andSelf()` is deprecated. Instead, simply replace `andSelf` with `addBack`, and it should work the same. https://api.jquery.com/andSelf/ – qJake Feb 12 '21 at 19:05
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");
        }
    });
})
RayOnAir
  • 2,038
  • 2
  • 22
  • 33
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.)

brooksrelyt
  • 3,925
  • 5
  • 31
  • 54
buildsucceeded
  • 4,203
  • 4
  • 34
  • 72
2

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].pageX : event.pageX,
                y: event.targetTouches ? event.targetTouches[0].pageY : event.pageY
            };
            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')))
        });
Alex Nikulin
  • 8,194
  • 4
  • 35
  • 37
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/

  • this is the only one which work for me with BS 4.5, and can click on an anchor inside the popover content. thx – subdesign Jul 28 '22 at 12:59
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>
Santo Boldizar
  • 1,255
  • 14
  • 17
0

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

document.querySelectorAll("label")[6].parentElement.onclick = () => {console.log('label clicked');}
rbansal
  • 1,184
  • 14
  • 21
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
    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](/help/how-to-answer). – Community Oct 12 '22 at 16:38
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.

Simbiat
  • 339
  • 2
  • 12
-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 */
}
Andre
  • 1,852
  • 1
  • 16
  • 21