3
\$\begingroup\$

I want to remove/add classes when the user is at different distances from the top by using jQuery.

I have successfully done it, and it works fine, but I think I'm doing it wrong, and I would like your help to optimize the code.

The html is simple, basically the sections(including the header), have 100% width. and different colors. I want to make the header change color when its over the first section(for aesthetical purposes). And I also want it to have a shadow when the page has been scrolled more than 1 pixel.

I'm doing it by adding/removing classes.

When I use one big else if statement it doesn't work well because whenever any any condition is matched js stops checking for other matches, so it doesn't apply all the classes needed.

The next code works, however as I said, I think that it's not optimal/badly written. Here is the HTML markup:

<header class="dark no-shadow">
  Header
</header>
<section class="blue">
  Please Scroll Down to see the header changes...
</section>
<section>
  The header color Should change when you pass through me.
</section>

And here is the jQuery code:

var header = $('header'),
        blueSection = $('section.blue'),
    // Calculate when to change the color.
        offset = blueSection.offset().top + blueSection.height() - header.height();

$(window).scroll(function(){
  var scroll = $(window).scrollTop();

    // Remove Class "dark" after scrolling over the dark section
  if (scroll >= offset) {
    header.removeClass('dark');
  } else {
    header.addClass('dark');
  }

    // Remove Class "no-shadows" whenever not on the top of the page.
  if (scroll >= 1) {
    header.removeClass('no-shadow');
  } else {
    header.addClass('no-shadow');
  }

});

And for those of you who like to use JSFiddle(like me!): https://jsfiddle.net/shock/wztdt077/6/

\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

I know you tagged this jQuery, but I believe that loading in jQuery for this seems like a lot of processing for little benefit, so I decided to write it in a more universal approach, which I think is the main problem with your code.

What you want to accomplish is to have a little script that can be included and will work predictably in as many circumstances without being to specific, so first I went about actually creating a structure:

  • The element the class will be applied to will have an attribute named data-scroll-group - it will define the group name for these scrollable items.
  • The elements that your header will respond to will contain an attribute constructed of the above group name and prefixed by data. It's contents will be the classname(s) you want to apply.

This structure looks like this:

<header data-scroll-group="header-group"></header>
<section data-header-group="aClassName"></section>

This means you could have many scroll groups on the page, allowing you to reuse the code with different aspect and different headers, different responders, etc... It makes your code less connected to the DOM and more based in a structure.

The rest is pretty simple. Use getBoundingClientRect to get the elements position compared to the viewport. I am currently applying the classes using the position of the viewport, but you could simply get the same getBoundingClientRect on the header and add the values together to get a result for your header specifically.

Check out the snippet and tell me if you have any questions about it. You could replace my header.className with jQuery if you wanted - you could replace most of it with jQuery, but I think coupling your code to jQuery has no benefit here, as you want this to work anywhere, and pure vanilla Javascript is about as long.

// This is a handy wrapper function that will return an array of matching element instead of a nodeList
function querySelectorArray(query, root){
  return Array.prototype.slice.call((root || document).querySelectorAll(query));
}

// Get all headers that are designated 'scroll-group'
var headers = querySelectorArray('[data-scroll-group]');

// Loop through the headers
headers.forEach(function(header){
  // Get the name of the group from the headers [data-scroll-group] attribute
  var group = header.getAttribute('data-scroll-group');
  // Get all the sections with a matching data-[data-scroll-group] attribute
  var sections = querySelectorArray('[data-' + group + ']');
  // Create an Event Listener for scrolling
  window.addEventListener('scroll', function(){
    // Declare a lastSection variable that can store the last class that scrolled by
    var lastSection = false;
    sections.forEach(function(section){
      // Get the elements position compared to the viewport
      var offset = section.getBoundingClientRect();
      // If the position is smaller than 0 it has scrolled further than that section
      // The same is true for the scroll being smaller than the negative height - if so, it is out of view.
      if(offset.top < 0 && offset.top > -offset.height) lastSection = section.getAttribute('data-' + group + '');
    });
    // Apply the class to your header
    header.className = lastSection || ''; 
  })
});
body {
  padding: 0;
  margin: 0;
  padding-top: 20px;
  height: 405vh;
  font-family: Arial, serif;
}
header {
  position: fixed;
  width: 100%;
  top: 0;
  left: 0;
  background: #dd0300;
  -webkit-transition: all 1s;
  transition: all 1s;
  color: #fff;
  padding: 5px 20px;
  box-sizing: border-box;
}
header.blue {
  background: #4e88ff;
}
header.shadow {
  box-shadow: 0 0 20px #000;
}
section {
  box-sizing: border-box;
  width: 100%;
  height: 100vh;
  background: #ccc;
  padding: 20px;
}
<header data-scroll-group="header-group">
  Header
</header>
<section data-header-group="blue">
  Scroll Down (header will become blue)
</section>
<section data-header-group="shadow">
  Scroll Down (header will have a shadow, no longer blue)
</section>
<section data-header-group="blue shadow">
  Scroll Down (header will have a shadow and be blue)
</section>
<section>
  Scroll Down (header will reset)
</section>

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks, This is much better than what i expected. saved me big chunks of code all over the website :) \$\endgroup\$
    – Samuel E.
    Feb 20, 2016 at 22:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.