Learn how to implement scroll animations with HTML, CSS, and JavaScript. Use the JS IntersectionObserver and CSS transitions to implement a native "animate on scroll" effect that works in any browser.
Two things: You can add the following one-liner to toggle a class when the entry is intersecting. entry.target.classList.toggle('show', entry.isIntersecting); Instead of turning off the animation with prefers-reduced-motion, turn it on. @media (prefers-reduced-motion: no-preference) { .hidden { transition: all 1s; } }
Disabling for prefers-reduces-motion is easier to do. Firstly, all the code describing the animation details is with the rest of the styling. Secondly, it's easy to remove all transition on the page with a single selector: *, *::before, *::after { transition: none !important; }
@@pepkin88 nah, it goes with the mobile-first principles or in this case animation-less-first principle, where you assume everyone does not want animation unless stated otherwise. It’s a better way to handle prefers-reduced-motion
@@pepkin88 General reminder: Reduced motion is *not* no motion! Browsers could trivially turn off *all* motion in that way. The point of prefers reduced motion is to tone down or replace motions that would be problematic for users with certain disorders.
@@invinciblemode “unless stated otherwise” - but here you’re selecting based on “no preference”. It’s identical to disabling on prefers-reduced-motion in both form and function.
There is an alternative way to do the staggering part if you don´t want to write a :nth-child() for every item. One could define an inline CSS variable in the html like style="--order: 1;" (2, 3, 4 and so on for every html element). Then in the CSS set a transition-delay of 'calc(100ms * var(--order))'. This works because the inline variable will be cascaded into the .logo class. This simplifies the staggering part in the CSS, but note that your target browser must support CSS variables.
Thanks for this tutorial, found a way so I could put it into my own project, working on a website for my very first time and all tutorials and how things work have been incredibly insightful!
I’m still learning my way around CSS, HTML, and JS, which contributes to my lack of knowledge, however, I feel that if Fireship were to include the source code for videos like these, it would significantly help new learners like me.
I've done something similar to this but with interpolating a scroll position with an onscroll event and a position: sticky. The downside to the approach is that there's more JS, but the upside is that you can leave animations half finished if the user stops scrolling There's a Medium article about it (specifically, how to replicate the effect like it is on Apple's product pages) out there that shows exactly how it works.
Thank you for this amazing video, I have wasted my whole day for this scroll animation on internet but couldn't find any good thing. This video randomly popup in my youtube homepage and it made my work easier.
instead of *transition: all .5s* you can use *transition: .5s;* *transition-property: transform, filter;* and you can use a custom property instead of *.5s*
Found out about intersection observer 2 years ago and I was very happy that I didn't actually have to use any kind of library for this simple task in every project, especially since learning something like Greensock isn't particularly easy. Thank you for this tutorial!
One nitpick, you should avoid the 'all' for the transition. This will bite you in the behind in more complex stylesheets can lead to transitions for properties you don't want transitioned. And those transitions can be extremely bad on the render performance (like margins, dimensions and positions that don't use transform). I made it a habit to always explicitly define the transitioned properties in the transform rule.
Ahh see Im new to JS, but not to programming. I was wondering this as well as I was making a lot of transitions. What do you prefer to do instead? Today I made a navigation bar that fills the whole top of the page, and as the user scrolls down, the nav bar "pops" out and shrinks to be more of a floating element. But I also noticed when the page first loads, the nav bar shifts into place due to the transition all.
@@Anselwithmac most of the time you can do what you need with good performing properties like transform which offers rotation, positioning and scaling. Only if this does not cover what you need you should start to transition other stuff. For example if the navbar "pops out" you could fade the content out (really fast like 0.15s) and then shrink it using transform: scale. It sure requires extra work to avoid transition: all but gives you that extra bit of resilience.
Another small improvement besides the reduced motion preference would be to account for no-JS users and have some ".no-js" class on the body that reveals all the hidden elements. Otherwise atm everything would remain hidden.
@@wayneswildworld You're thinking of the most average user, but also think about parsers visiting the site and only grabbing the visual elements, some kiosk-mode machines with limitations or library computers or whatnot. This is a simple best practise that will allow you to universally reach a wider audience. Also some of my friends have JS off by default and only allow it for a limited number of trusted sites, those people also exist and that's okay 🤷♀
@@wayneswildworld I most likely would keep it visible. Depends on the specific scenario, you can sometimes just move toggling over to CSS or not have interaction on the non-js version
It's also nice to use inline css variables, fe. style="--delay:300ms", that way we don't need to worry about writing separate css for each element. Obviously classes (fe. .delay200) would work the same
especially if the number of elements are dynamically generated- so you either run out of classes ready or you need to generate them (with the nth option) the inline variable is the best option as it can be generated on the fly and a simple css line can take care of the delays based on the variable :)
Great and succinct. It would be nice if you could combine the `n` in nth-child and calc() to do something like `.logo:nth-child(n) { transition-delay: calc(200ms * n); }` . Maybe someday.
I actually have used this in my portfolio site but with scroll behaviour smooth upon when the element is about to appear but the one you showed here is better, I will try to use this one from now
Thought it might be worth noting that you shouldn't hide elements by default in the real world. Might be a good idea to use a custom attribute for the observer then hide each element, add the hidden class, on load with the JavaScript, as we're already using it. This way you won't exclude those that have JS disabled by default (a small number I know, but some crawlers struggle with JS) Of course there's many other ways to do this as well, like noscript.
I don't think for the crawlers it makes much of a difference if it's hidden or not since they tend to not take a lot of css into consideration. For users, if they have JS disabled, you can just have a noscript tag saying you need JS, like in a blank React app.
@@darkpain4208 hey, you wouldn't be able to have animations applied on scroll without JS, so if someone has JS disabled on their browser and they visit your site, using the code in the video, the user would never see any content that has the "hidden" class set by default as JS would not run and remove the hidden class on scroll. If every item is animated your page would essentially be blank. So I would use an attribute like bluej-scroll-anim="true (or name of animation class)" on any element needing animate on scroll in, then target that attr in JavaScript, finding all ("[bluej-scroll-anim]") and setting class hidden (if not in view) on load in JS, before activating the "scroll" observer on the set of elements.
@@matheusgoulart1618 true, a lot of sites are JS based nowadays so it is quite a small consideration. Would just be shame to have some users not see important elements on the page just because I added animation to it. Yeh, I suppose if a crawler considers CSS these days it's likely to take into account js too..
For staggering this can be useful as well if you need more control over the delays: .d1{transition-delay:100ms} .d2{transition-delay:200ms} .d3{transition-delay:300ms} .d4{transition-delay:400ms} .d5{transition-delay:500ms} .d6{transition-delay:600ms} .d7{transition-delay:700ms} .d8{transition-delay:800ms} .d9{transition-delay:900ms}
The issue with this is that it doesn't work when JavaScript isn't available. A progressive enhancement approach like this would be better: Have all the elements shown by default. When the JavaScript runs, the intersection observer can add a class to hide all of the non-visible elements, without affecting the already visible content. While this does mean that you lose the ability to have animations on any content over the fold (there would be a slight flicker, which isn't acceptable), I think the benefit of the content being accessible to a larger audience is worth it.
@@mszoezo6368 that seems like it would be more complicated than using an extra class. If it's using multiple classes you don't like, a custom attribute would work well - I was originally going to suggest that in my comment but I decided to use classes for the sake of simplicity
For none JS then the hidden class shouldn’t work with zero animations. Most people have JS on and we found most people using screen readers have JS turned on too.
What if js is disabled 🤔. Same as you teach I done yesterday. but I didn't add hidden class by myself. I add .reveal-anim select all the .reveal-anim when it will not intersecting it will add hidden class to the element and if intersecting it will remove. and if js is disabled all elements where visible in my case.
I'll just say something. Going too far with customizing basic stuff like scrolling behavior is sometimes overdone, and becomes gaudy and invasive, like custom cursors or scrollbars of the past.
Great video love the more in depth content. TL;DR: I know this is a demo site so this comment is only meant for larger projects. Put the script tag in the element with a defer attribute Regarding your placement of your script tag at the end of the body element, doing this may cause a document reflow due to the HTML/CSS already being parsed before the JS file is read; as the browser isn't aware if loaded JS is going to modify a DOM element. A more modern alternative is to place your script tag in the head of the docuemnt but you will need to add a "defer" tag to it as shown in this video to ensure it isn't render blocking as shown in this video ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-cXwnJKflxas.html