Reputation: 633
I have three tooltip buttons on a page. I can close any open tooltip by clicking anywhere outside buttons. And this is what I came across:
In the code below, when I click on any place on the page, the handler of this part of code is activated $(document).on('click', (event) => this.closeOnOutsideClick(event));
I can see in inspector, that function closeOnOutsideClick
is fired three times - it makes three checks for each tooltip button present on the page. I cannot figure out what mechanism is responsible for that and why the check if (!$(event.target).closest(this.$elem))
is not performed only once? My code can be found here and also below: https://jsfiddle.net/bakrall/786cz40L/
This is a simplified version of a more complex code just to give example of my issue:
const selectors = {
tooltip: '.tooltip-container',
tooltipButton: '.tooltip-button',
tooltipMessage: '.tooltip-message'
}
class Tooltip {
constructor(tooltip) {
this.$elem = $(tooltip);
this.$tooltipButton = this.$elem.find(selectors.tooltipButton);
this.$tooltipMessage = this.$elem.find(selectors.tooltipMessage);
this.$tooltipMessageText = this.$tooltipButton.attr('data-tooltip-content');
this.bindUiEvents();
}
bindUiEvents() {
$(document).on('click', (event) => this.closeOnOutsideClick(event));
this.$tooltipButton.on('click', () => this.showTooltipMessage());
this.$tooltipButton.on('blur', () => this.hideTooltip());
}
showTooltipMessage() {
this.$tooltipMessage
.text(this.$tooltipMessageText)
.addClass('shown-message');
}
hideTooltip() {
this.$tooltipMessage
.text('')
.removeClass('shown-message');
}
closeOnOutsideClick(event) {
if (!$(event.target).closest(this.$elem)) {
this.hideTooltip();
}
}
}
//class in another file
const tooltip = $('.tooltip-container');
tooltip.each(function(index, item) {
new Tooltip(item);
})
.input-wrapper {
margin-bottom: 2em;
}
.tooltip-container {
position: relative;
display: inline-block;
}
.tooltip-message {
display: none;
position: absolute;
left: 100%;
top: 0;
width: 10em;
padding: 0.5rem;
background: #000;
color: #fff;
}
.tooltip-message.shown-message {
display: inline-block;
}
button {
width: 1.2em;
height: 1.2em;
border-radius: 50%;
border: 0;
background: #000;
font-family: serif;
font-weight: bold;
color: #fff;
}
button:focus {
outline: none;
box-shadow: 0 0 0 0.25rem skyBlue;
}
input {
display: block;
}
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title> </title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="tooltip.css">
</head>
<body>
<div class="input-wrapper">
<label for="name">
What's your name?
<span class="tooltip-container">
<button class="tooltip-button" type="button" aria-label="more info"
data-tooltip-content="This clarifies whatever needs clarifying">i</button>
<span class="tooltip-message" role="status"></span>
</span>
</label>
<input id="name" type="text"/>
</div>
<div class="input-wrapper">
<label for="age">
What's your age?
<span class="tooltip-container">
<button class="tooltip-button" type="button" aria-label="more info"
data-tooltip-content="This is to know how old you are">i</button>
<span class="tooltip-message" role="status"></span>
</span>
</label>
<input id="age" type="text"/>
</div>
<div class="input-wrapper">
<label for="nationality">
What's your nationality
<span class="tooltip-container">
<button class="tooltip-button" type="button" aria-label="more info"
data-tooltip-content="What country are you from?">i</button>
<span class="tooltip-message" role="status"></span>
</span>
</label>
<input id="nationality" type="text"/>
</div>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous">
</script>
<script src="tooltip.js" async defer></script>
</body>
</html>
Upvotes: 0
Views: 28
Reputation: 3070
tooltip.each(function(index, item) {
new Tooltip(item);
})
Since you instantiate 3 Tooltips, you bind a separate event listener to the document each time. Each listener is getting triggered with each click. However, each of those listeners has a different this
which is what allows each listener to tell if its Tooltip was clicked and if not, hide it.
If you want a single listener you could store a list of all your Tooltips and have the event listener iterate through the list of Tooltips, closing all Tooltips that were not clicked.
Upvotes: 1
Reputation: 10549
Your click event is firing on mutliple elements, because you specified just (document). Maybe you can be more specific:
$(document).on('click', '.input-wrapper', (event) => this.closeOnOutsideClick(event));
Upvotes: 0