skip to Main Content

I have a div with two spans inside it. The position of spans is that span1 uses a sticky position from the center of the div while span2 is at the bottom of the div. The idea is that, as the user scrolls, span1, which is initially at opacity 0 becomes visible and after further scrolling, span2 becomes visible. I tried achieving this by manually tweaking the points they become visible. Is there an optimal way to achieve this? I am attaching my code as reference

window.addEventListener('scroll', function() {
  const message = document.querySelector('.message');
  const deathSpan = document.querySelector('.message > span');
  const vhDiv = document.querySelector('.vh');

  const rect = message.getBoundingClientRect();
  const windowHeight = window.innerHeight;


  const fadeStart1 = windowHeight * 0.3;
  const fadeEnd1 = windowHeight * 0.6;


  const fadeStart2 = windowHeight * 0.5;
  const fadeEnd2 = windowHeight * 0.9;


  if (rect.top <= fadeEnd1 && rect.bottom >= fadeStart1) {
    let opacity1 = (fadeEnd1 - rect.top) / (fadeEnd1 - fadeStart1);
    opacity1 = Math.min(Math.max(opacity1, 0), 1);
    deathSpan.style.opacity = opacity1;
  } else {
    deathSpan.style.opacity = 0;
  }


  if (rect.top <= fadeEnd2 && rect.bottom >= fadeStart2) {
    let opacity2 = (fadeEnd2 - rect.top) / (fadeEnd2 - fadeStart2);
    opacity2 = Math.min(Math.max(opacity2, 0), 1);
    vhDiv.style.opacity = opacity2;
  } else {
    vhDiv.style.opacity = 0;
  }
});
.above-divs a{
font-size:10rem;
background-color: black;
color: white;

}


.message {
  width: 100%;
  height: 150vh;
  display: flex;
  position: relative;
  background-color: rgb(0, 0, 0);
  align-items: center;
}

.message span {
  font-size: 10vw;
  color: rgb(255, 255, 255);
}

.message>span {
  position: sticky;
  top: 50%;
  opacity: 0;
  transition: opacity 0.5s ease;
}

.vh {
  position: absolute;
  bottom: 0;
  right: 10%;
  opacity: 0;
  transition: opacity 0.5s ease;
}
<div class="above-divs">
  <a href="">hello</a>
</div>



<div class="message">
  <span>Text1 (span1)</span>
  <div class="vh">
    <span>Text2 (span2)</span>
  </div>
</div>

2

Answers


  1. This might be useful: Intersection Observer API MDN

    This API provides an efficient way to track visibility changes of elements relative to the viewport.

    You can try doing something like:

    // Target elements
      const span1 = document.querySelector('.message > span');
      const span2 = document.querySelector('.vh > span');
    
      // Fade-in observer for span1 (centered/sticky)
      const observer1 = new IntersectionObserver(function (entries) {
        entries.forEach(entry => {
          if (entry.intersectionRatio > 0.5) { // If more than half of the span is visible
            span1.style.opacity = 1;
          } else {
            span1.style.opacity = 0;
          }
        });
      }, observerOptions);
    
      // Fade-in observer for span2 (at the bottom)
      const observer2 = new IntersectionObserver(function (entries) {
        entries.forEach(entry => {
          if (entry.intersectionRatio > 0.5) { // Same logic for span2
            span2.style.opacity = 1;
          } else {
            span2.style.opacity = 0;
          }
        });
      }, observerOptions);
    
      // Observe the target elements
      observer1.observe(span1);
      observer2.observe(span2);
    });
    
    Login or Signup to reply.
  2. Indeed IntersectionObserver is the way, but you don’t need to specify one for each element

    Your HTML/CSS structure is a bit weird but I more or less followed it for a working example

    const options = {
      // Root to defaults if not specified
      //root: document.querySelector("#myParentArea"),
      rootMargin: "0px",
      threshold: 1.0,
    };
    const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          if (entry.intersectionRatio > 0.5) {
            entry.target.style.opacity = 1;
          } else {
            entry.target.style.opacity = 0;
          }
        });
      }, options);
      
      observer.observe(span1);
      observer.observe(span2);
    .above-divs a{
      font-size:10rem;
    }
    
    .message {
      margin-top:30rem
    }
    
    .message span {
      font-size: 10vw;
      opacity: 0.1;
      transition: opacity 2s ease;
    }
    
    .vh{
      padding-top:10rem
     }
    <div class="above-divs">
      <a href="">hello</a>
    </div>
    
    
    
    <div class="message">
      <span id='span1'>Text1 (span1)</span>
      <div class="vh">
        <span id='span2'>Text2 (span2)</span>
      </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search