Skip to content
Advertisement

why does intersection observer keeps on running?

I want to change the class of the header using intersection observer. The idea here is that I have a header with full height and width and when we scroll down to another div the header shrinks to a small bar.

This is my javascript code.

if('IntersectionObserver' in window){
  const options = {
    root: null,
    rootMargin: '0px',
    threshold: 0.0
  }

  callback = (entries) => {
    const header = document.querySelector("header");
    const IS_INTERSECTING = entries[0].isIntersecting;
    if(!IS_INTERSECTING){
      header.classList.replace("header_full","header");
      return false;
    }else if(IS_INTERSECTING){
      header.classList.replace("header","header_full");
      return false;
    }else{
      return false;
    }
  }

  let observer = new IntersectionObserver(callback, options);

  let target = document.querySelector('header');
  observer.observe(target);
}

This is my markup

<div class="application">
        <header class="header_full">
            <div>
                <img src="logo_2.png" alt="logo">
                <h2>Intersection Observer</h2>
            </div>
            <div class="bars"></div>
        </header>
        <div class="full">full_1</div>
        <div class="full">full_2</div>
        <div class="full">full_3</div>
        <div class="full">full_4</div>
        <div class="full">full_5</div>
    </div>

This is my scss file

%full{
    height: 100vh;

}
body{
    margin: unset;
    background:whitesmoke;
    font-family: Comfortaa;
}

.full{
    @extend %full;
}

.header_full{
    @extend %full;
    background: goldenrod;
    display: grid;
    place-items:center;
    position: relative;

    & img{
        height: 250px;
        width:250px;
        object-fit: contain;
    }

    & h2{
        text-align: center;
        color: rgb(60, 60, 60);
        letter-spacing: 1.4px;
    }

    & .bars{
        &::after{
            content: "☰";
            font-size:1.3rem;
        }
        height: 40px;
        width: 40px;
        display: grid;
        place-items:center;
        position:absolute;
        top:10px;
        right: 10px;
        color: rgb(60,60,60);
    }
}

header{
    transition: all 500ms linear;
}

.header{
    height: 100px;
    max-height: 100px;;
    background: goldenrod;
    position: fixed;
    top:0;
    width: 100%;
    padding: 10px;
    box-sizing: border-box;
    animation: bring_down 500ms linear;

    & img{
        height: 80px;
        width:80px;
        object-fit: contain;
    }

    & h2{
        display: none;
    }

    & .bars{
        position:static;
    }
}

The problem I am facing is when I scroll down, the intersection observer keeps toggling the classes ie. header_full and header. making it flicker all the time. I have tried “observer.unobserve(header)” but the problem that I get is stops observing and thus makes the header change one time only.

I have also refered the following stack overflow questions but no luck.

Change style header/nav with Intersection Observer (IO)

Intersection Observer change class based on clientHeight

Advertisement

Answer

IntersectionObserver is based on visibility within the viewport (or another specified element root), so if your target is constantly entering and exiting the viewport you will end up with an infinite loop.

This is what your code is doing. The header exits the viewport, triggering the !IS_INTERSECTING control flow, which immediately puts it back inside the viewport. Re-entering the viewport triggers the IS_INTERSECTING control flow, which immediately pushes it back out – that’s an infinite loop.

You need your IntersectionObserver to target a static element which will not change its DOM position as a result of the callback. I would suggest taking your header out of the document flow entirely and putting a 100vh placeholder behind it. This has the added benefit of removing the heavy layout shift when your header goes from 100vh to essentially 0px as far as the remaining content is concerned.

<div class="application">
  <div class="header_placeholder"></div>
  <header class="header_full">
      <div>
          <img src="logo_2.png" alt="logo">
          <h2>Intersection Observer</h2>
      </div>
      <div class="bars"></div>
  </header>
  <div class="full">full_1</div>
  <div class="full">full_2</div>
  <div class="full">full_3</div>
  <div class="full">full_4</div>
  <div class="full">full_5</div>
</div>
%full{
    height: 100vh;

}
body{
    margin: unset;
    background:whitesmoke;
    font-family: Comfortaa;
}

.full{
    @extend %full;
}

.header_placeholder {
  height: 100vh;
}

.header_full{
    @extend %full;
    background: goldenrod;
    display: grid;
    place-items:center;

    /* move this out of the document flow */
    position: absolute;
    top: 0;
    width: 100%;

    & img{
        height: 250px;
        width:250px;
        object-fit: contain;
    }

    & h2{
        text-align: center;
        color: rgb(60, 60, 60);
        letter-spacing: 1.4px;
    }

    & .bars{
        &::after{
            content: "☰";
            font-size:1.3rem;
        }
        height: 40px;
        width: 40px;
        display: grid;
        place-items:center;
        position:absolute;
        top:10px;
        right: 10px;
        color: rgb(60,60,60);
    }
}

header{
    transition: all 500ms linear;
}

.header{
    height: 100px;
    max-height: 100px;;
    background: goldenrod;
    position: fixed;
    top:0;
    width: 100%;
    padding: 10px;
    box-sizing: border-box;
    animation: bring_down 500ms linear;

    & img{
        height: 80px;
        width:80px;
        object-fit: contain;
    }

    & h2{
        display: none;
    }

    & .bars{
        position:static;
    }
}
if('IntersectionObserver' in window){
  const options = {
    root: null,
    rootMargin: '0px',
    threshold: 0.0
  }

  callback = (entries) => {
    const header = document.querySelector("header");
    const IS_INTERSECTING = entries[0].isIntersecting;
    if(!IS_INTERSECTING){
      header.classList.replace("header_full","header");
      return false;
    }else if(IS_INTERSECTING){
      header.classList.replace("header","header_full");
      return false;
    }else{
      return false;
    }
  }

  let observer = new IntersectionObserver(callback, options);

  // target the placeholder element here
  let target = document.querySelector('.header_placeholder');
  observer.observe(target);
}
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement