Popovers are contain extra information. They pop out when a user clicks on a button that triggers it.
Most popovers appear in four directions:
- Top
- Right
- Bottom
- Left
It helps to learn to create popovers that appear in all four directions. For this lesson, you’ll learn to build a popover that appears at the top.
Making a trigger
A popover requires a trigger. The trigger should be a <button> element since we want users to interact with it.
<button class="popover-trigger">...</button>For this component, we’ll use arrows as triggers. We’ll create these arrows with SVG.
<button class="trigger"> <svg viewBox="0 0 40 20"> <use xlink:href="#arrow"></use> </svg></button>Building the popover
Popovers should appear above all other elements. This means popovers must be a direct descendant of the <body> element. If popovers are placed inside another element, they may get cut off when an ancestor element uses transform or overflow.
<body> <!-- other elements --> <div class="popover"> <p>The quick brown fox jumps over the lazy dog</p> </div></body>The popover needs to know the direction they should appear. We can use a custom attribute for this. Let’s call this custom attribute data-position.
<div class="popover" data-position="top"> <p>The quick brown fox jumps over the lazy dog</p></div>The top popover should appear above the top trigger as shown in the picture below.
We can only achieve such a precise positioning by setting position to absolute. Since position is absolute, we need to provide the correct top and left values.
.popover { position: absolute; /* Example values */ top: 300px; left: 500px;}This means we need to use JavaScript to calculate the top and left values.
Calculating popover’s left position
First, we need to get the trigger and popover.
const popoverTrigger = document.querySelector('.popover-trigger')const popover = document.querySelector(`.popover`)To position the popover above the trigger, we also need to know the center position of the trigger.
We can get these values with getBoundingClientRect.
const popoverTriggerRect = popoverTrigger.getBoundingClientRect()const triggerCenter = (popoverTriggerRect.left + popoverTriggerRect.right) / 2The center of the trigger is also the center of the popover. We can calculate the popover’s left position with this information.
We’ll need the popover’s width value to calculate the left position. We can find the popover’s width value with getBoundingClientRect.
const popoverRect = popover.getBoundingClientRect()const leftPosition = triggerCenter - popoverRect.width / 2We can set the left position with the style property.
popover.style.left = `${leftPosition}px`
Next, we want to find the top position.
Calculating popover’s top position
To calculate the popover’s top position, we first need to find the trigger’s top position.
We can get this value from getBoundingClientRect.
const triggerTop = popoverTriggerRect.topThe trigger’s top value is equal to the sum of the popover’s top value, the popover’s height, and some breathing space.
Let’s say the space is 20px.
const space = 20We can get the popover’s height from popoverRect.
const topPosition = triggerTop - popoverRect.height - spaceWe will then set the top position with the style property.
popover.style.top = `${topPosition}px`
Hiding the popover
The popover should be hidden at first. We can hide the popover by adding the hidden attribute. Make sure you add this hidden attribute after positioning the popover.
popover.style.left = `${leftPosition}px`popover.style.top = `${topPosition}px`
// Hiding the popoverpopover.setAttribute('hidden', true)Showing and hiding the popover
We need to show the popover when the trigger gets clicked. To do this, we remove the hidden attribute.
popoverTrigger.addEventListener('click', _ => { popover.removeAttribute('hidden')})If the user clicks the trigger again, we want to hide the popover. We can use an if statement to accomplish this.
popoverTrigger.addEventListener('click', _ => { if (popover.hasAttribute('hidden')) { popover.removeAttribute('hidden') } else { popover.setAttribute('hidden', true) }})
If the user clicks outside the popover and the trigger, we want to hide the popover as well. We can do this by listening to the document.
document.addEventListener('click', event => { // ...})If event.target does not originate from within .popover or .popover-trigger, we know the user has clicked outside both the popover trigger and the popover.
We’ll hide the popover by setting hidden to true.
document.addEventListener('click', event => { if ( event.target.closest('.popover') || event.target.closest('.popover-trigger') ) return popover.setAttribute('hidden', true)})
That’s it!
Welcome! Unfortunately, you don’t have access to this lesson. To get access, please purchase the course or enroll in Magical Dev School.
Unlock this lesson