Why does the if and else both get executed on scroll?

68 views Asked by At

I got this really strange behaviour for my scroll event, I just can't seem to figure it why this happens or how to fix it. Basically what I have is an event listener that listens to scrolling, but both the if and else is getting executed on scroll..

I got this directive:

core.directive('offsetColor', [function() {

  return {
    restrict: 'A',
    link: function(scope, element, attrs) {

      $window.on('scroll', function() {
        var windowY = $(window).scrollTop();
        var elementY = element.offset().top - 60; // -60 to add site header height

        if (elementY <= windowY) {
          console.log('element is at top');
        }
        else {
          console.log('no element is at top');
        }
      });
    }
  }
}]);

Whos task is to change the site header's color based on the y position of certain elements.

Currently I have set this directive on two elements.

Now my simple guess to why this is happening is because I've set the directive on two elements and not one, therefore the scroll event "loops" through both of the elements and checking their y position. So one of the elements is at the top but the other one is not, causing both the if and else to be executed.

I feel like I've tried everything to stop it from executing both but I come up with no solution.

What am I doing wrong here?

1

There are 1 answers

4
Agop On

As Roamer-1888 mentioned in the comments, in order to resolve a problem like this, you need to consolidate your event handlers (the scroll listeners, in this case).

Here's one idea: create a directive and apply it to a common ancestor of all of the elements that change the header's color. Add the scroll listener on the window inside of this directive.

Now, either by using jQuery, or by using child directives that talk to the parent directive (like the AngularJS tabs example), loop through all of the elements that could change the header's color. Grab their offsets from the window position, and then find the one with the smallest offset.

The element with the smallest offset is the one that should affect the header color.

All in all, your directive would look something like this (going the jQuery route; the nested directive route is left as an exercise to the read) (untested):

core.directive('offsetColorContainer', [function() {

  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var colorElems = $('.some-selector-that-identifies-your-color-elems', element);

      $window.on('scroll', function() {
        var windowY = $(window).scrollTop(),
            closestElem = null,
            closestDist = null;

        colorElems.each(function() {
          var elem = $(this),
              elemDist = Math.abs(elem.offset().top - windowY);

          if (closestElem === null || elemDist < closestDist)
          {
            closestElem = elem;
            closestDist = elemDist;
          }
        });

        if (closestElem !== null)
        {
          // change the header color based on closestElem
        }
      });
    }
  }
}]);

Note that you may need to tweak the element distance logic depending on what your elements actually represent, but that should get you started in the right direction.