Seph Reed
Seph Reed

Reputation: 11028

How can I use Intersection Observers to tell when something is taking up the entire viewport?

It's pretty easy to tell with an intersection observer when the entirety of an element in within the viewport. The code is roughly like so:

// get an element
const thingIWantEntirelyInView = $("#Thing")[0];  

const checkInView = new IntersectionObserver((event) => {
  console.log("It's in view");
}, {
  root: null, // viewport
  threshold: 1.0,
});
checkInView.observe(thingIWantEntirelyInView);

What I can't figure out, though, is how to flip it around, so it's less like contain and more like cover. I want to know when my element (which is larger than the viewport) is totally covering the screen.

I tried switching the places of null and thingIWantEntirelyInView above, but that didn't work.

I also tried adding a position fixed height: 100vh; width: 100vw element to the page and checking the intersection with that, but it appears intersection observers don't work with two elements that don't have a parent child relationship? Maybe I did the code wrong, but here's an example:

const thingIWantFillingView = document.getElementById("BigScrolly");
const viewPort = document.getElementById("ViewPort");  

// what I tried
const checkInView = new IntersectionObserver((event) => {
  console.log("This never seems to happen never happen");
}, {
  root: thingIWantFillingView,
  threshold: 0.5,
});
// when the viewport is at least half inside BigScrolly
checkInView.observe(viewPort);



// example of the thing I don't want
const checkTouchView = new IntersectionObserver((event) => {
  console.log("It has touched the viewport");
}, {
  root: null,
  threshold: 0,
});
// when BigScrolly has touched the viewport
checkTouchView.observe(thingIWantFillingView);
#ViewPort {
  position: fixed;
  height: 100vh;
  width: 100vw;
  top: 0px;
  left: 0px;
  pointer-events: none;
  border: 2px solid red;
  box-sizing: border-box;
}

#SomePreviousThing {
  height: 100vh;
}

#BigScrolly {
  height: 300vh;
  background: linear-gradient(#FFEEEA, #222);
}
<div id="ViewPort"></div>
<div id="SomePreviousThing">
  Ignore this section, scroll down.
</div>
<div id="BigScrolly"></div>

How can I tell when one of my elements is totally covering the screen using Intersection Observers?

Upvotes: 3

Views: 3572

Answers (2)

Abhi Beckert
Abhi Beckert

Reputation: 33369

You can achieve this by simply checking if intersectionRect.top is zero (and also check if there's any intersection at all):

new IntersectionObserver((entries) => {
  entries.forEach( entry => {
    if (entry.intersectionRatio == 0) {
      console.log('not visible at all')
      return
    }
    if (entry.intersectionRect.top == 0) {
      console.log('at the top of the viewport')
      return
    }
    console.log('partially visible — not at the top of the viewport')
  })
})

Here's a full example - where I use this to invert a floating page header while a dark section of the page is under the header: https://jsfiddle.net/okvtm7gc/

Upvotes: 1

Seph Reed
Seph Reed

Reputation: 11028

Came up with a solution: I have a sticky element inside BigScrolly that's 100vh tall (make sure to wrap in an absolute positioned element to negate it's place in flow). As long as I'm fully scrolled into that section, it'll be taking up the entire view.

const coverChecker = document.querySelector(".CoverChecker");


// what I tried
const checkInView = new IntersectionObserver((event) => {
  const coverage = event[0].intersectionRatio;
  if (coverage >= 1) {
    console.log("BigScrolly covers the viewport");
  } else {
    console.log("Coverage", event[0].intersectionRatio);
  }
}, {
  root: null,
  threshold: [0, 0.25, 0.5, 0.75, 1.0],
});
// when the viewport is at least half inside BigScrolly
checkInView.observe(coverChecker);
.Positioner {
  position: absolute;
  top: 0px;
  left: 0px;
  right: 0px;
  bottom: 0px;
  pointer-events: none;
}

.CoverChecker {
  position: sticky;
  height: 95vh;
  width: 95vw;
  border: 2px solid red;
  box-sizing: border-box;
  top: 2px; 
}


.OtherSection {
  height: 100vh;
}

#BigScrolly {
  position: relative;
  height: 300vh;
  padding: 20px;
  background: linear-gradient(#FFEEEA, #222);
}
<div class="OtherSection">
  Ignore this section, scroll down.
</div>
<div id="BigScrolly">
  <div class="Positioner">
    <div class="CoverChecker"></div>
  </div>
  This is some text
</div>
<div class="OtherSection">
  Ignore this section, scroll tup.
</div>

Upvotes: 3

Related Questions