Martin James
Martin James

Reputation: 930

Changing theme of nav bar depending on the data attribute of an element scrolled into view using Intersection Observer

I have a fixed side navigation bar that, by default, is a dark colour to match the dark colour of the topmost element on my page, the header.

enter image description here

The next section below this dark header has a white background and when that scrolls into view with an offset of 50%, I would like to change the fixed, left side navigation bar to the same colour. When scrolled back up, the side navigation must, again, change accordingly.

I used to achieve this effect by using a plugin called Waypoints by Imakewebthings:

var waypoint1 = new Waypoint({
    element: document.getElementById('services-summary'),
        handler: function (direction) {
            if (direction === 'down') {
                // change navigation theme
            } else {
                // change navigation theme
            }
    },
    offset: main_nav_height
});

However, I'm trying to build this new project without using any dependencies at all. I've therefore decided to use Javascript's native Intersection Observer to try and achieve the same effect, as I'm okay with the browser support for this and some other visual effects.

I'm having trouble getting a working version together that respects scrolling directions up and down. I also have an issue where, on page load, I'm getting both section theme names outputted to JS console which is not ideal, at all.

This where I'm up to with it:

Javascript:

var intersection_observer_options = {
    root: null,
    rootMargin: '0px',
    threshold: 0
};

var observer = new IntersectionObserver(change_themes_on_view, intersection_observer_options);

function change_themes_on_view(entries, observer) {
    for (var i = 0; i < entries.length; i++) {
        var theme = entries[i].target.getAttribute('data-theme');
        console.log(theme);
    }
}

var themed_sections = document.querySelectorAll('*[data-theme]');
for (var i = 0; i < themed_sections.length; i++) {
    observer.observe(themed_sections[i]);
}

HTML:

I'm not including the CSS side of things as I can work that out myself. I'm also not including the navigation bar here, as I just want to demonstrate how I'm identifying each section's theme. I have, however, shown a little more in the JSFIDDLE example.

<header data-theme="dark">
    ...
</header>

<main>
    <section data-theme="light">
        ...
    </section>

    <section data-theme="dark">
        ...
    </section>

    <section data-theme="light">
        ...
    </section>

    <section data-theme="dark">
        ...
    </section>
</main>

When I run my page, I get the following console output without scrolling anywhere and can't see why:

> dark
> light
> dark
> light
> dark

To help you help me, I have put together a FIDDLE to help get this working.

Could somebody with knowledge of this new Intersection Observer API please help me cross the threshold? Pardon the pun.

UPDATE 1

I think I have got it working here. Still tinkering.

UPDATE 2

Yep, it's working.

I'll add my own answer because I'm sure somebody else will find this useful instead of deleting the question.

Upvotes: 1

Views: 491

Answers (1)

Martin James
Martin James

Reputation: 930

I fixed it myself and surprisingly wasn't far off.

To fix both of my concerns - why it was outputting all themes in the console and not considering the scroll direction - I simply added this conditional statement to my change_themes_on_view function:

if (entries[i].intersectionRatio > 0) {
    // do stuff now entry is in viewport
}

Which tests if the target element/entry is inside the viewport.

This obviously fixed the script outputting all target theme values on page load to my console. It also respects direction now too, as when the target element leaves the viewport the theme is reversed.

This is now the full function:

function change_themes_on_view(entries, observer) {
  for (var i = 0; i < entries.length; i++) {
    if (entries[i].intersectionRatio > 0) {
      var theme = entries[i].target.getAttribute('data-theme');
      nav_bar.setAttribute('data-theme', theme);
    }
  }
}

This is the working demo

Upvotes: 0

Related Questions