Struggling with smoothly transitioning an image into view using CSS and JavaScript

I'm currently experimenting with creating a cool effect on my website where an image fades in and moves up as the user scrolls it into view. I've included the code I have so far below, but unfortunately, I keep getting a 404 error when trying to run it. Any help would be greatly appreciated! I'm still new to JavaScript and have been working hard to understand this.

Check out my CSS:

.section3 {
        opacity: 0;
        transform: translateY(20vh);
        visibility: hidden;
        transition: opacity 0.6s ease-out, transform 1.2s ease-out;
        will-change: opacity, visibility;
    }
.fade {
        opacity: 1;
        transform: none;
        visibility: visible;
    }

Here is how the HTML and JS are structured:

<section id="section3" class="section3">
        <img style="width: 100%;" src="lovethyneighbor.jpg">
</section>

<script>
        var section3 = document.getElementById("section3");
        var location = section3.getBoundingClientRect();

        if (location.top >= 0) {
            document.getElementById("section3").classList.add("fade");
        } else {
            document.getElementById("section3").classList.add("section3");
        }
</script>

Answer №1

Let me introduce you to the Intersection Observer API! This handy tool is built into JavaScript and allows you to trigger an event or function when a specific element enters the viewport.

This API is a game-changer and I highly recommend using it over relying on getBoundingClientRect(). One of the main advantages is evident in the code snippet below:

if (location.top >= 0) {
     document.getElementById("section3").classList.add("fade");
} 
else {        
     document.getElementById("section3").classList.add("section3");
}

If you use Intersection Observer, your function will only run when needed, instead of being triggered by every mousewheel event, which can be inefficient and impact performance. With this API, your page is constantly monitored, and actions are only taken when elements come into view. Take a look at the annotated code below for further details.

Efficiency with Multiple Elements Requiring Different Animations

// Define the sections or containers
const sections = document.querySelectorAll("section.section");

// Set up options for the intersection function
const options = {
  root: null,
  threshold: 0.5, // Determine how much of the element should be visible before triggering the function (0 - 1)
  rootMargin: "0px 0px 0px 0px" // Default root margin value
};

// Initialize the observer - Allows multiple elements with varying animations to be tracked using forEach loop
let observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {

    // Identify the element to animate
    const block = entry.target.querySelector("img.fader");

    // Retrieve all elements that need animation within the same section
    const animationBlocks = entry.target.querySelectorAll("[data-animation]");

    // Trigger animations when the element is visible
    if (entry.isIntersecting) {
      // Loop through multiple animations for the same element
      animationBlocks.forEach((animation) => {
        animationClass = animation.dataset.animation;

        // Apply data-animation class to initiate the animation
        animation.classList.add(animationClass);
      });
    }
  });
}, options);

observer.observe(document.querySelector("section.section"));

// Start running the animations
document.addEventListener("DOMContentLoaded", function() {
  Array.from(sections).forEach(function(element) {
    observer.observe(element);
  });
});
body {
  height: 300vh;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  background-color: teal;
  gap: 400px;
}

/* Initial values */
[data-animation="fadeInUp"] {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
}

/* Activate animation when class is added */
.fadeInUp {
  animation-name: fadeInUp;
  animation-duration: 0.6s;
  animation-fill-mode: both;
}

/* Animation definition */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translate3d(0, 20px, 0);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}
<section id="section2" class="section section2">
  <img data-animation="fadeInUp" class="fader" style="width: 100%;" src="https://picsum.photos/200/300">
</section>

<section id="section3" class="section section3">
  <img data-animation="fadeInUp" class="fader" style="width: 100%;" src="https://picsum.photos/200/300">
</section>

Simplified Approach for Single Element and Single Animation

const sections = document.querySelectorAll("section.section");

const options = {
  root: null,
  threshold: 0.5
};

let observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {

    const block = entry.target.querySelector("img.fader");

    if (entry.isIntersecting) {
      block.classList.add('fadeInUp');
    }
  });
}, options);

observer.observe(document.querySelector("section.section"));

// Begin running the animations
document.addEventListener("DOMContentLoaded", function() {
  Array.from(sections).forEach(function(element) {
    observer.observe(element);
  });
});
body {
  height: 300vh;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  background-color: teal;
  gap: 400px;
}

img.fader {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
}

.fadeInUp {
  animation-name: fadeInUp;
  animation-duration: 0.6s;
  animation-fill-mode: both;
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translate3d(0, 20px, 0);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}
<section id="section2" class="section section2">
  <img data-animation="fadeInUp" class="fader" style="width: 100%;" src="https://picsum.photos/200/300">
</section>

<section id="section3" class="section section3">
  <img class="fader" style="width: 100%;" src="https://picsum.photos/200/300">
</section>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

A stylish and versatile Bootstrap Modal is the perfect

Just dipping my toes into Bootstrap 4.5, but struggling with the Modal component. I've gone through the documentation multiple times and simply copied and pasted from the website. The code is so basic, yet I can't seem to figure out what's ...

What is the correct way to reset the styling of a web browser element, such as a <button>?

I have come across many sources advising me to reset HTML by manually resetting numerous individual properties, as demonstrated here: https://css-tricks.com/overriding-default-button-styles/ Another approach I discovered in CSS is simply using: button: {a ...

Tips on stopping slideToggle from opening and closing when clicked for the first time

When using the slideToggle function, I'm encountering an issue where the content div briefly shows on the first click but then immediately slides closed. Subsequent clicks work as expected and slide open correctly. This is the Jquery script I have be ...

JavaScript filename

This question may appear simple, but I believe the answer is not as straightforward. Here it goes: Should I keep the filename of jQuery as "jquery-1.3.2.min.js" for compatibility reasons, or should I rename it to jquery.js? In my opinion, it's best ...

How can I efficiently add multiple items to an array and store them in async storage using React Native?

I am trying to store multiple elements in local storage using React Native. I found some helpful documentation on how to do this here. Could someone guide me on the correct way to achieve this? Here's a snippet of my code: My current approach const ...

Storing data with Laravel 5.3 using Storage::put and XMLHttpRequest

Attempting to send a file using DRAG & DROP with XMLHttpRequest. $images = $_FILES['images']; When I use foreach: foreach($images["name"] as $file => $name) and move_uploaded_file($images["tmp_name"][$file], $images_dir . $name it works ...

Changing the color of tabs using inline styles in material ui does not seem to work

I am brand new to using material ui and have been attempting to alter the colors of the selected tab. Currently, the color is a dark blue shade and I am aiming to change it to red. I tried applying inline styles, but unfortunately, there was no change. C ...

It seems that JavaScript is unable to detect newly added elements following an AJAX request

One issue I'm facing is that when an element loads data based on a clicked block, all javascript functionalities break. Currently, using Ajax, I load an entire PHP file into my index after sending a variable to it. For example: If Block 1 is clicked ...

Troubleshooting IE compatibility issues with jQuery .hide() method

I am encountering a curious issue with hiding span elements using CSS (display: none;). Upon page load, I expect the first span element to be displayed, which it does in all browsers except IE7. This anomaly has left me perplexed as there is no unusual cod ...

Looking for a method to substitute "test" with a different value

Showing a section of the menu using <li id="userInfo" role="presentation" data-toggle="tab" class="dropdown"> <a href="#" name="usernameMenu" class="dropdown-toggle" data-toggle="dropdown" role="button"> <span class="glyphicon glyph ...

Button placement that feels out of place

I'm looking to style an input-group with a form input and submit button using Bootstrap 5. Here's my current code: .card { margin: auto; width: 50%; padding: 10px; opacity: 0.9!important; top: 250px; } <div class="card-header"> ...

Can anyone provide guidance on locating the parent of a pseudo element with Selenium WebDriver?

Is there a way to retrieve the parent web element of a pseudo element (if we can call it the parent) using JavaScript? I know that Selenium's methods for searching for web elements are not suitable for pseudo elements, but JavaScript does allow manipu ...

Adjust the active carousel item to 0 within onsen-ui (displaying a list of carousel items in a sliding menu)

In my sliding menu, each menu item contains a carousel with two items. I am trying to make the first carousel item show after closing and reopening the menu, or by clicking a button outside of the list on the menu page. This is my current setup: <ons- ...

Showing the initials of a user at the top of an SVG using ReactJS

I require the user's initials to be displayed on the avatars as a grey circle with those initials. I have the function ready, but I am unsure of how to implement it in the JSX code for the Dropdown menu (using SemanticUI library). Any suggestions? co ...

Guide on initiating document-wide events using Jasmine tests in Angular 2/4

As stated in the Angular Testing guidelines, triggering events from tests requires using the triggerEventHandler() method on the debug element. This method accepts the event name and the object. It is effective when adding events with HostListener, such as ...

The appearance of HTML in JSP and CSS output in a Spring application is different from the local environment

For my web application's landing page, I created the design using HTML and CSS. Typically, I first design it on scratchpad.io before implementing it into my STS IDE. However, when I run the application, the output of the page appears different from th ...

"The server responded with a 405 error message indicating that the requested method

I have been working on a registration form project using HTML, JS, NodeJS, and SQLite. However, I am encountering an issue with the fetch function when trying to post the inputted information into the database. I keep receiving a POST 405 (Method Not Allo ...

Having trouble with Javascript not detecting when it's empty?

Recently, I have been attempting to modify the color of a submit button when a form is empty. As a beginner in this area, I am somewhat puzzled as to what mistake I might be making. I will share the current code with you in hopes of receiving some guidance ...

Combining data from multiple API calls in a for loop with AngularJS

I'm working with an API that contains pages from 1 to 10 and my goal is to cycle through these page numbers to make the necessary API calls. app.factory('companies', ['$http', function($http) { var i; for (i = 1; i < 11 ...

The alert message fails to appear during an Ajax request

I'm having trouble understanding why the Ajax function in this code snippet is not triggering the success or error functions: function get_cust_key(custid) { $.ajax({ type: "POST", url: 'http://localhost/Test/index.php/getCust ...