Adjusting the visibility of a div as you scroll

I'm trying to achieve a fade-in and fade-out effect on div elements when scrolling over them by adjusting their opacity. However, I'm facing difficulties in getting it to work properly.

The issue lies in the fact that my div elements are positioned in the middle of the page rather than at the top, so using scrollTop doesn't seem to be the right approach, right?

var fade = $('.b_wrapper');
var range = 400;

$(window).on('scroll', function() {
  var st = $(this).scrollTop();
  fade.each(function() {
    var offset = $(this).offset().top;
    var height = $(this).outerHeight();
    offset = offset + height / 1;
    
    $(this).css({
      'opacity': 1 - (st + offset - range) / range
    });
  });
});
.content {
  height: 100px;
}

.b_wrapper {
  background: lightgreen;
  margin-top: 40px;
  padding: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<div class="content"></div>
<div class="b_wrapper">
  <p>this is a div</p>
</div>
<div class="b_wrapper">
  <p>this is a div</p>
</div>
<div class="b_wrapper">
  <p>this is a div</p>
</div>
<div class="b_wrapper">
  <p>this is a div</p>
</div>
<div class="b_wrapper">
  <p>this is a div</p>
</div>
<div class="b_wrapper">
  <p>this is a div</p>
</div>
<div class="b_wrapper">
  <p>this is a div</p>
</div>

(Check out the JsFiddle here)

Answer №1

If you want to monitor changes in the intersection between a target element and its ancestor or the viewport, you should consider using the Intersection Observer API. This API keeps an eye on these intersections and triggers a callback when certain thresholds are met.

To start, initialize a new observer with specific options:

let options = {
  root: null,
  threshold: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
}
let observer = new IntersectionObserver(callback, options);
  • root can refer to either the containing ancestor element (e.g., selected using document.querySelector()) or the viewport by setting it to null.

  • threshold is an array of values that represent different levels of intersection ratios. When an element's intersection ratio matches one of these values, the callback function is invoked, allowing you to adjust the element's opacity directly based on this ratio.

After setting up the options, make sure to add each target element to the observer:

for (const target of document.querySelectorAll('.b_wrapper')) {
    observer.observe(target);
}

The callback function simply sets the opacity of watched elements based on their intersection ratio with the viewport:

let callback = (entries, observer) => {
  entries.forEach((entry) => {
    entry.target.style.opacity = entry.intersectionRatio
  });
};

This should give you a good starting point. For more information and examples, consult the API documentation.

Answer №2

Perhaps not the most optimal approach, but here is my attempt:

  • When scrolling, I determine which element is the most centered on the screen

  • Upon window resize, I update the center of the screen value

let centerOfScreen = window.innerHeight / 2;
let pageScroll;
let closestCenteredDivPosition = 999999;
let currentIndex;

let wrappers = document.getElementsByClassName("wrapper");
let positions = [];
for (let e of wrappers) {
  positions.push(e.offsetTop + e.offsetHeight / 2);
}

highlightMostCenteredDiv = () => {
  pageScroll = document.documentElement.scrollTop || document.body.scrollTop;
  let currentValue;
  for (let i = 0; i < positions.length; i++) {
    currentValue = Math.abs(positions[i] - centerOfScreen - pageScroll);
    if (closestCenteredDivPosition > currentValue) {
      closestCenteredDivPosition = currentValue;
      currentIndex = i;
    }
  }
  if (!document.querySelector(".current")) {
    wrappers[currentIndex].classList.add("current");
  } else {
    if (wrappers[currentIndex] != document.querySelector(".current")) {
      document.querySelector(".current").classList.remove("current");
      wrappers[currentIndex].classList.add("current");
    }
  }
  closestCenteredDivPosition = 999999;
}

highlightMostCenteredDiv();

document.addEventListener("scroll", highlightMostCenteredDiv)

window.addEventListener("resize", () => {
  centerOfScreen = window.innerHeight / 2;
})
body {
  margin: 0;
}

.wrapper {
  background-color: orange;
  height: 100px;
  width: 100%;
  margin: 50px 0;
  opacity: 0.3;
  transition: opacity 1s ease;
}

.current {
  opacity: 1;
}
<div class="wrapper"></div>
<div class="wrapper"></div>
<div class="wrapper"></div>
<div class="wrapper"></div>
<div class="wrapper"></div>
<div class="wrapper"></div>
<div class="wrapper"></div>
<div class="wrapper"></div>

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

Capturing a variety of input values from a form and storing them in an array

I have a form with several input fields like this: <input type="text" name="name[]" value=""> <input type="text" name="name[]" value=""> <input type="text" name="name[]" value=""> I am attempting to submit the form and store those value ...

Tips for updating property values when calling a TypeScript function

Hello everyone, I am looking to convert a snippet of JavaScript code into TypeScript. JavaScript function newState(name){ var state ={ name : name, age : 0 } return state } function initStates() { this.JamesStat ...

Positioning html images to overlap each other without losing the ability to click

Currently, I am facing a challenge with positioning two images over each other. The back image should be at the bottom, while the center image needs to be on top of it. Additionally, there is a requirement for placing a random link (in this case google.com ...

Storing input data in a JavaScript array instead of sending it through an AJAX request

I've been grappling with this issue for quite some time now, and I just can't seem to wrap my head around it. While there are a few similar solutions out there, none of them address my exact problem. Here's the scenario: I have a form where ...

Ensure that two elements containing content of varying width are equal in width

I am facing a challenge with a container that contains two child elements (previous and next links). These links have labels of different lengths, but I need them both to have the same width on the page. Ideally, each link should be as wide as the widest e ...

Use of number type input on mobile devices in HTML

My goal is to display the up/down arrows alongside an input element with type="number" in Chrome on Android. I've adjusted the CSS to set opacity=1, but unfortunately, they are not appearing. On desktop, however, they show up without any iss ...

Instructions on utilizing sockets for transmitting data from javascript to python

How can I establish communication between my Node.js code and Python using sockets? In a nutshell, here is what I am looking for: Node.js: sendInformation(information) Python: receiveInformation() sendNewInformation() Node.js: receiveNewInformation( ...

Approximately 20% of spoken words are not accurately conveyed by Speech Synthesis Google Voices, failing to adhere to the intended voice selection

When using speechSynthesis.speak(utterance) in Chrome with the "Google UK English Female" voice, there is an issue where a male voice is randomly spoken instead. Any thoughts on how to resolve this? Latest Update: July 26th, 2022 This appears to be a bug ...

Can someone provide instructions on how to convert base64 data to an image file

I'm utilizing the vue-signature Library but I am unsure how to download the base64 data that is generated as an image. Here is the link to the library: https://www.npmjs.com/package/vue-signature. I have gone through the documentation and noticed that ...

Running two servers at the same time using nodemon might seem complex, but it

Is it possible to run two servers (www.js and apiServer.js) simultaneously using nodemon? I have set the value for the "start" key in package.json as https://i.stack.imgur.com/r8M7p.jpg After running nodemon in the command prompt with the current working ...

Transitioning with an ellipsis

Currently, I am working on a project where I am trying to utilize the CSS property text-overflow: ellipsis to achieve a specific animation. However, despite applying this property along with white-space: nowrap and overflow: hidden to .card-dish__info, I a ...

Discover the method for retrieving information through AJAX requests and dynamically displaying or hiding content based on the received

Currently, I'm in the process of developing a PHP script that outputs a numerical value indicating the number of unread messages. The snippet below showcases my code that triggers the PHP function every 30 seconds: setInterval(function (){ ...

Dealing with Laravel and AJAX - Issue with Loading DIV

I find myself in a perplexing situation. Despite not encountering any errors in my apache logs or browser (chrome), I am faced with an issue that has left me baffled. On a specific page (localhost/admin/networks), I can click on an item from a database-ge ...

The created function in VueJS may sometimes result in an undefined outcome

I recently started a project with VueJs where I encountered an issue while making a GET request using the Axios library. The data was returned as expected, but I faced difficulties calling the loadUsers function inside mounted. Here is my code snippet: ex ...

Navigating to a Specific Section with a Fixed Header on Another Page

Trying to create a link to an anchor on Page 1 from Page 2, both of which have fixed headers. I'm utilizing the following script on both pages: $(document).ready(function(){ $('.testlink').bind('click', function(e) { e ...

The result from the AngularJs promise is coming back as undefined

I am facing an issue while trying to implement the login function of my AuthService factory in conjunction with my AuthLoginController controller. The problem arises when the User.login function is triggered with incorrect email and password details, causi ...

I am interested in displaying all the items on the list instead of just one

<html> <head> <link rel="stylesheet" type="text/css" href="mango.css"> <script> function showItems(){ var items = document.querySelectorAll("#main li"); items.forEach(i ...

What is the best way to conceal all input elements in a form?

I have been attempting to conceal input elements within my form by using the code snippet below. Unfortunately, it doesn't seem to be functioning as expected... <html lang="en"> <head> <title>example</title> < ...

A perfectly organized and justified menu with evenly spaced horizontal list items

I couldn't find a solution to evenly spacing out a series of list items for a menu styled list. After realizing CSS alone wasn't enough, I decided to incorporate some javascript (jQuery). My goal was to have equal padding between each LI without ...

Retrieve information and auto-fill text boxes based on the selected dropdown menu option

UPDATED QUESTION STATUS I'm currently working with Laravel 5.7 and VueJs 2.5.*. However, I am facing a challenge where I need to automatically populate form textboxes with data from the database when a dropdown option is selected. Despite my efforts ...