I remember about a couple of months ago I came across a strange scenario in which the click handler of one of the DOM elements was not getting called. While analysis I didn't find any stoppropagation or preventdefault that could actually stop the handler from being called. This made my situation little problematic because I have to spend some more time finding the actual reason behind this issue.
Some more closer analysis revealed that neither this was any javascript coding mistake error nor any browser bug which resulted in this, rather the bug was reported because one of the transparent div masked that specific DOM element. Since the element was masked the events did not get triggered on it.
To better understand this scenario we can take the below HTML example:
Some more closer analysis revealed that neither this was any javascript coding mistake error nor any browser bug which resulted in this, rather the bug was reported because one of the transparent div masked that specific DOM element. Since the element was masked the events did not get triggered on it.
To better understand this scenario we can take the below HTML example:
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<style>
#container
{
height : 200px;
width: 200px;
position : absolute;
top : 100px;
left : 100px;
border : 2px solid red;
z-index : 10;
}
#mask
{
height : 400px;
width: 400px;
position : absolute;
top : 10px;
left : 10px;
border : 2px solid green;
z-index : 100;
}
</style>
</head>
<body>
<div id="container">The click event handler will not get called when do click on this div.</div>
<div id = "mask">The click handler of this div will get called.</div>
<script>
$('#container').on('click', function(){
console.log("container click event handler");
});
$('#mask').on('click', function(){
console.log("mask click event handler");
});
</script>
</body>
</html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<style>
#container
{
height : 200px;
width: 200px;
position : absolute;
top : 100px;
left : 100px;
border : 2px solid red;
z-index : 10;
}
#mask
{
height : 400px;
width: 400px;
position : absolute;
top : 10px;
left : 10px;
border : 2px solid green;
z-index : 100;
}
</style>
</head>
<body>
<div id="container">The click event handler will not get called when do click on this div.</div>
<div id = "mask">The click handler of this div will get called.</div>
<script>
$('#container').on('click', function(){
console.log("container click event handler");
});
$('#mask').on('click', function(){
console.log("mask click event handler");
});
</script>
</body>
</html>
Points to note :
1.)The HTML body contains two div elements which have position absolute and the div with id as "mask" lies on top of the div with id "container".
2.) Both of the divs have click handler configured on them
Now let us talk about the issue - when you launch this Demo and click on the "container" div portion, then only one handler gets called, which is evident from the fact that on the console you get to see - "mask click event handler" .
i.e even though you tried clicking on the "container" div the handler of the "container" div for click was not called because the "mask" div ahve overshadowed the "container" div and thus prevented the click handler of the "container" div from calling.
Now that we know the reason what could be the solution?
While searching for the solution I came to know there is one CSS property named "pointer-events". If you have set the CSS of an element to pointer-events: none, it won’t catch any click on it at all, but instead just let the event fall through to the element below it.
i.e if I launch the demo after modifying the style for "mask" div as:
1.)The HTML body contains two div elements which have position absolute and the div with id as "mask" lies on top of the div with id "container".
2.) Both of the divs have click handler configured on them
Now let us talk about the issue - when you launch this Demo and click on the "container" div portion, then only one handler gets called, which is evident from the fact that on the console you get to see - "mask click event handler" .
i.e even though you tried clicking on the "container" div the handler of the "container" div for click was not called because the "mask" div ahve overshadowed the "container" div and thus prevented the click handler of the "container" div from calling.
Now that we know the reason what could be the solution?
While searching for the solution I came to know there is one CSS property named "pointer-events". If you have set the CSS of an element to pointer-events: none, it won’t catch any click on it at all, but instead just let the event fall through to the element below it.
i.e if I launch the demo after modifying the style for "mask" div as:
#mask
{
height : 400px;
width: 400px;
position : absolute;
top : 10px;
left : 10px;
border : 2px solid green;
z-index : 100;
pointer-events: none;
}
{
height : 400px;
width: 400px;
position : absolute;
top : 10px;
left : 10px;
border : 2px solid green;
z-index : 100;
pointer-events: none;
}
This will now allow the click handler of the "container" div to be called. But this solution has two drawbacks:
1.) now the click handler of "mask" div will not get called.
2.) This solution is not cross browser compatible, I mean If you have to support IE8 as well then you may not use this solution at all because this CSS property is not supported by IE8.
Again I started looking for a fix which may work in IE8 as well. Finally I got a javascript solution which helped me in delegating the events to the underneath element.
This fix works by first hiding the masking element then getting the underneath element from the coordinates then again showing back the masking element and finally triggering the event on the underneath elelment. So Then new modified Script works like below:
1.) now the click handler of "mask" div will not get called.
2.) This solution is not cross browser compatible, I mean If you have to support IE8 as well then you may not use this solution at all because this CSS property is not supported by IE8.
Again I started looking for a fix which may work in IE8 as well. Finally I got a javascript solution which helped me in delegating the events to the underneath element.
This fix works by first hiding the masking element then getting the underneath element from the coordinates then again showing back the masking element and finally triggering the event on the underneath elelment. So Then new modified Script works like below:
<script>
$('#container').on('click', function(){
console.log("container click event handler");
});
;$('#mask').on('click', function(evt){
console.log("mask click event handler");
triggerUnderneathEvent(evt);
});
function triggerUnderneathEvent(evt)
{
var maskingElement = $(evt.target);
var displayMaskStyle = maskingElement.css('display');
maskingElement.css('display', 'none'); // hide the masking element.
var underneathElem = $(document.elementFromPoint(evt.clientX, evt.clientY)); // get the underneath element
//reset the masking element visibility to the original display
if(displayMaskStyle)
{ maskingElement.css('display', displayMaskStyle);
}
else
{
maskingElement.css('display' , '');
}
evt.target = underneathElem;
underneathElem.trigger(evt);
} </script>
$('#container').on('click', function(){
console.log("container click event handler");
});
;$('#mask').on('click', function(evt){
console.log("mask click event handler");
triggerUnderneathEvent(evt);
});
function triggerUnderneathEvent(evt)
{
var maskingElement = $(evt.target);
var displayMaskStyle = maskingElement.css('display');
maskingElement.css('display', 'none'); // hide the masking element.
var underneathElem = $(document.elementFromPoint(evt.clientX, evt.clientY)); // get the underneath element
//reset the masking element visibility to the original display
if(displayMaskStyle)
{ maskingElement.css('display', displayMaskStyle);
}
else
{
maskingElement.css('display' , '');
}
evt.target = underneathElem;
underneathElem.trigger(evt);
} </script>
Now when you launch the demo and click on the "container" div both of the handlers will get called successfully.