0

This is my code so far:

// Click Function

$('body').on('click', 'a', function() {
  $('a.active').removeClass('active');
  $(this).addClass('active');
});


// Scroll Function

const mediaInViewport = document.querySelectorAll('.media');

observer = new IntersectionObserver((entries, observer) => {
  console.log('Next Media in Viewport');
  entries.forEach((entry) => {
    if (entry.target && entry.isIntersecting) {
      entry.target.classList.add('active');

      const closestParent = entry.target.closest('section');
      if (closestParent) {
        const selector = closestParent.id;
        const links = document.querySelectorAll('.link');
        for (const link of links) {
          const split = link.href.split('#');
          if (split.length >= 2) {
            const local = split[1];
            link.classList.remove('active');

            if (local === selector) {
              link.classList.add('active');
            }
          }
        }
      }
    }
  });
}, {
  threshold: 0.7
});

window.addEventListener('DOMContentLoaded', () => {
  setTimeout( // Wait for images to fully load
    () => {
      mediaInViewport.forEach((item) => {
        observer.observe(item);
      });
    }, 1000);
});
* {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  font-size: 30px;
  text-decoration: none;
  color: inherit;
}

body {
  display: flex;
  cursor: default;
}

#left,
#right {
  width: 50%;
  height: 100vh;
  overflow-y: scroll;
  scroll-behavior: smooth;
}

#left {
  background-color: rgb(220, 220, 220);
}

#right {
  background-color: rgb(200, 200, 200);
}

.media {
  padding: 10px;
  padding-bottom: 0;
}

.media:nth-last-child(1) {
  margin-bottom: 10px;
}

img {
  display: block;
  width: 100%;
}

.link {
  cursor: pointer;
}

.active {
  background-color: black;
  color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="left">
  <a class="link active" href="#landscape">Landscapes</a>
  <a class="link" href="#cats">Cats</a>
  <a class="link" href="#food">Food</a>
</div>

<div id="right">

  <section id="landscape">
    <div class="media">
      <img src="https://upload.wikimedia.org/wikipedia/commons/8/8d/Freudenberg_sg_Switzerland.jpg">
    </div>
  </section>

  <section id="cats">
    <article class="media">
      <img src="https://upload.wikimedia.org/wikipedia/commons/b/b8/Cute_cat_%281698598876%29.jpg">
    </article>
  </section>

  <section id="food">
    <article class="media food">
      <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Good_Food_Display_-_NCI_Visuals_Online.jpg/1200px-Good_Food_Display_-_NCI_Visuals_Online.jpg">
    </article>
  </section>

In some cases, more than one .link gets the class .active. This can happen if a screen is in portrait orientation. I would like to avoid that. Only the last observed object should trigger an active state for a link.

Maybe it would be possible just to write a rule that says: "There can be only one .active class."?

But I don't know how to do that, unfortunately. Could somebody help me please? I would be sooooo thankful! <3

Anna_B
  • 820
  • 1
  • 4
  • 23
  • if you remove active and set active together it would be working, but I see you set active many times and in cycle. `In some cases, more than one .link gets the class .active.` on which resolution? I can't reproduce this bug – Daniil Loban Feb 11 '21 at 01:00
  • For example, if the cats and the foods image are both completely in the viewport (because of the window size), then the links "Cats" and "Food" have both a black background. But there always should be only one active link (with a black background). – Anna_B Feb 11 '21 at 01:15
  • I added answer with code refactoring, please check how it works, – Daniil Loban Feb 11 '21 at 01:41
  • if cats and the foods image are both completely in the viewport which link should be active first or second? – Daniil Loban Feb 11 '21 at 02:05
  • @Daniil Loban Thank you so much! Always the last one that come into the viewport should cause the active class. Is it now like that? – Anna_B Feb 11 '21 at 02:33
  • Yes is it, but I forgot about black frames in right. – Daniil Loban Feb 11 '21 at 03:06
  • Ohhh, it works perfectly!!! Thank you so much! <3<3<3 – Anna_B Feb 11 '21 at 03:07

1 Answers1

1

    const mediaInViewport = document.querySelectorAll('.media');
    const links = Array.from(document.querySelectorAll('.link'));
    let actLink = links[0];

    document.body.addEventListener('click',  (event)=> {
      if (event.target.tagName === 'a'){
        actLink.classList.remove('active');
        actLink = links.find(link => event.target.href === link.href)
        actLink.classList.add('active');
      }
    }, false)
    
    observer = new IntersectionObserver((entries, observer) => {
      entries.forEach((entry) => {
        if (entry.target && entry.isIntersecting) {
          const closestParent = entry.target.closest('section');
          if (closestParent){
            actLink.classList.remove('active');
            actLink = links.find(link => 
              link.href.slice(link.href.lastIndexOf('#')) === `#${closestParent.id}`
            )
            actLink.classList.add('active');
          }  
        }
      });
    }, {
      threshold: 0.7
    });

    window.addEventListener('DOMContentLoaded', () => {
      setTimeout( // Wait for images to fully load
        () => {
          mediaInViewport.forEach((item) => {
            observer.observe(item);
          });
        }, 1000);
    });    
    * {
      margin: 0;
      padding: 0;
      font-family: sans-serif;
      font-size: 30px;
      text-decoration: none;
      color: inherit;
    }

    body {
      display: flex;
      cursor: default;
    }

    #left,
    #right {
      width: 50%;
      height: 100vh;
      overflow-y: scroll;
      scroll-behavior: smooth;
    }

    #left {
      background-color: rgb(220, 220, 220);
    }

    #right {
      background-color: rgb(200, 200, 200);
    }

    .media {
      padding: 10px;
      padding-bottom: 0;
    }

    .media:nth-last-child(1) {
      margin-bottom: 10px;
    }

    img {
      display: block;
      width: 100%;
    }

    .link {
      cursor: pointer;
    }

    .active {
      background-color: black;
      color: white;
    }    
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <div id="left">
    <a class="link active" href="#landscape">Landscapes</a>
    <a class="link" href="#cats">Cats</a>
    <a class="link" href="#food">Food</a>
  </div>
  <div id="right">
    <section id="landscape">
      <div class="media">
        <img src="https://upload.wikimedia.org/wikipedia/commons/8/8d/Freudenberg_sg_Switzerland.jpg">
      </div>
      <div class="media">
        <img src="https://upload.wikimedia.org/wikipedia/commons/8/8d/Freudenberg_sg_Switzerland.jpg">
      </div>
    </section>
    <section id="cats">
      <article class="media">
        <img src="https://upload.wikimedia.org/wikipedia/commons/b/b8/Cute_cat_%281698598876%29.jpg">
      </article>
      <article class="media">
        <img src="https://upload.wikimedia.org/wikipedia/commons/b/b8/Cute_cat_%281698598876%29.jpg">
      </article>
    </section>
    <section id="food">
      <article class="media food">
        <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Good_Food_Display_-_NCI_Visuals_Online.jpg/1200px-Good_Food_Display_-_NCI_Visuals_Online.jpg">
      </article>
      <article class="media food">
        <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Good_Food_Display_-_NCI_Visuals_Online.jpg/1200px-Good_Food_Display_-_NCI_Visuals_Online.jpg">
      </article>
    </section>
  </div>
Daniil Loban
  • 4,165
  • 1
  • 14
  • 20
  • Hey, I have just found a further issue: https://stackoverflow.com/questions/66149062/intersection-observer-fine-tuning-for-different-window-sizes – It would be very cool if you could help me again! :) – Anna_B Feb 11 '21 at 05:11