Reputation: 221
Tough title to explain the situation but I will share the context. I have a dropdown that is opened, and upon opening I set a new handler to check if the user clicked outside of the boundaries of the dropdown. Once that interaction happens, we can close the dropdown. I have enclosed a very simple test case, be aware that business logic for actually checking for dropdown boundaries is missing for demonstration purposes.
The observation is that the eventlistener
for click, added after executing the earlier click event for opening the dropdown, is executed immediately as well. I would have expected that, considering the mousedown
event has already been executed this event listener would only be executed on new click events.
This can be further validated; if I add a timeout on adding the eventhandler, it works just fine. Any thoughts or smart ideas on this approach?
I added a complete test case that shows the challenge. Both approaches available in the Open method.
<html>
<head><title></title></head>
<body>
<a class="trigger" activates="A">Open Dropdown</a>
<div id="A">Dropdown contents</div>
<script>
function Dropdown(domEl){
this.trigger = domEl;
this.Init();
}
Dropdown.prototype = {
Init : function(){
console.log('initialized dropdown');
this.trigger.addEventListener('click',function(){this.Open()}.bind(this));
},
Open : function(){
console.log('opened dropdown');
// this does not work, as the click event is triggered immediately upon triggering the Open even set under Init
window.addEventListener('click',function(){this.OnOpenEventHandler()}.bind(this));
// this works, as the event is not triggered immediately
window.setTimeout(function(){
window.addEventListener('click',function(){this.OnOpenEventHandler()}.bind(this));
}.bind(this),50);
},
OnOpenEventHandler : function(){
console.log('dropdown can be closed');
this.Close();
},
Close : function(){
console.log('closed dropdown');
}
}
var triggers = document.querySelectorAll('.trigger');
triggers.forEach((el,i) => {
new Dropdown(el);
});
</script>
</body>
</html>
Upvotes: 0
Views: 51
Reputation: 2679
If you do not want to use stopPropagation()
, as per your comment, an alternative solution might be checking that the click event has not been fired by the trigger
element:
<html>
<head>
<title></title>
</head>
<body>
<a class="trigger" activates="A">Open Dropdown</a>
<div id="A">Dropdown contents</div>
<script>
function Dropdown(domEl) {
this.trigger = domEl;
this.Init();
}
Dropdown.prototype = {
Init: function() {
console.log('initialized dropdown');
this.trigger.addEventListener('click', function() {
this.Open()
}.bind(this));
},
Open: function() {
console.log('opened dropdown');
window.addEventListener('click', function(event) {
/*
* execute the code only if the click event
* has not been fired by the 'a.trigger' element
*/
if (event.srcElement != this.trigger) {
this.OnOpenEventHandler()
}
}.bind(this));
},
OnOpenEventHandler: function() {
console.log('dropdown can be closed');
this.Close();
},
Close: function() {
console.log('closed dropdown');
}
}
var triggers = document.querySelectorAll('.trigger');
triggers.forEach((el, i) => {
new Dropdown(el);
});
</script>
</body>
</html>
Upvotes: 1
Reputation: 2679
If I understood correctly what you want to achieve, I think you simply need to prevent the click event from propagating:
<html>
<head>
<title></title>
</head>
<body>
<a class="trigger" activates="A">Open Dropdown</a>
<div id="A">Dropdown contents</div>
<script>
function Dropdown(domEl) {
this.trigger = domEl;
this.Init();
}
Dropdown.prototype = {
Init: function() {
console.log('initialized dropdown');
this.trigger.addEventListener('click', function(e) {
e.stopPropagation(); // will prevent the click event from propagating
this.Open()
}.bind(this));
},
Open: function() {
console.log('opened dropdown');
// this does not work, as the click event is triggered immediately upon triggering the Open even set under Init
window.addEventListener('click', function() {
this.OnOpenEventHandler()
}.bind(this));
// this works, as the event is not triggered immediately
/*window.setTimeout(function(){
window.addEventListener('click',function(){this.OnOpenEventHandler()}.bind(this));
}.bind(this),50);*/
},
OnOpenEventHandler: function() {
console.log('dropdown can be closed');
this.Close();
},
Close: function() {
console.log('closed dropdown');
}
}
var triggers = document.querySelectorAll('.trigger');
triggers.forEach((el, i) => {
new Dropdown(el);
});
</script>
</body>
</html>
Upvotes: 1