When styling your page and working out the perfect UI/UX for your users, you often find yourself in the situation that you want to apply specific styling to an element under certain specified conditions.
A common example would be an input field that is clicked and ready to be filled with input, we apply styling to the focused state like so:
input {
width: 100px;
height: 20px;
border: 1px solid grey;
}
input::focus {
border: 1px solid blue;
}
It's that simple, we have a grey border on default, but when the input is focused the input gets a blue border.
Well, let's do the same for a sticky element, shall we?
.element__with-input {
width: 100px;
height: 20px;
position: sticky;
}
.element__with-input::stuck {
background-color: gooeygreen;
}
Hate to break it to you, but this is not going to work, there is no pseudo-element called '::stuck'... There is no native way to see when your element is stuck or not.
So, our situation right now is that we have a working sticky element but no way to apply styling to it when it's in a stuck state.
We need to build something ourselves, so, what do we need?
- A way to observe whether the element that has position:sticky has passed some sort of boundary.
- Apply or toggle a second class to the element that indicates when that boundary has been crossed ( got stuck ).
- We also need to figure out when the element is out of its sticky state and is 'unstuck' to un-toggle the specific styling.
Let's get to work,
we need a way to observe... since we are using Typescript here at YTEC, I made a sticky-element-handler.ts and within that file, I made use of an Intersection Observer
This will give me the ability to asynchronously observe whether my element has been intersected.
I added -2 pixels to the bottom of the sticky element and when the Y-offset of the viewport is lower than 1 intersected pixel, the code will trigger the --stuck class
giving the sticky element the following classes: element__with-input element__with-input--stuck.
Awesome, now we can add styling in the CSS or in my case the SCSS file by targeting the element__with-input--stuck class which will overwrite the element__with-input class.
All in all, we made an observer to see a certain threshold has been set in order to toggle a stuck specific class to the existing element for us to style!
In a perfect world we would have a ::stuck pseudo-element like hover, focus, before, after, etc.
By viewing the code you will get a better understanding of the observer at work:
export class StickyChecker {
constructor() {
const stickyElement = document.querySelector(".event-detail__actions");
const observer = new IntersectionObserver(
([e]) =>
e.target.classList.toggle(
"event-detail__actions--stuck",
e.intersectionRatio < 1
),
{
threshold: [1],
}
);
observer.observe(stickyElement);
}
}