Debugging

Every now and then you may find a Waypoint triggering at times you do not expect or not triggering at all. Most of these issues are attributable to a few common mistakes. If you find yourself in this situation, the lib directory contains a debugging script. Include that script at the end of your scripts.

<script src="/path/to/waypoints.js"></script>
<script src="/path/to/waypoints.debug.js"></script>

The debug script will monitor for instances of these common mistakes and provide a console error with a summary of the issue and a link to the detailed explanation on this page. You should be careful to remove this debug script before deploying to production, as it runs a number of unnecessary checks during key internal methods. Let's look at the errors and some hypothetical situations where they arise.

Display None Elements

Let's say you have an element down the page that you want to start faded out, and fade in when it fully enters the window. For fun, when you scroll up it will fade back out.

$('.something')
  .fadeOut(0) // immediately hide element
  .waypoint(function(direction) {
    if (direction === 'down') {
      $(this.element).fadeIn()
    }
    else {
      $(this.element).fadeOut()
    }
  }, {
    offset: 'bottom-in-view'
  })

This looks fine at first glance, except jQuery's fadeOut method ends by setting display:none. Elements with display of none do not have a defined position on the page. When Waypoints is calculating the trigger point, it doesn't have a way to find the would-be-if-not-display-none position. The resulting calculation ends up incorrect, often with sporadic values.

You should never use a display:none element as a waypoint. This includes elements that gain display:none at any time in the lifetime of your page, as trigger points are recalculated whenever a refresh occurs, such as a window resize event.

In our example above, let's rewrite this fade-out/in functionality to avoid using display:none.

$('.something')
  .css('opacity', 0) // immediately hide element
  .waypoint(function(direction) {
    if (direction === 'down') {
      $(this.element).animate({ opacity: 1 })
    }
    else {
      $(this.element).animate({ opacity: 0 })
    }
  }, {
    offset: 'bottom-in-view'
  })

To help demonstrate the issue, below we have a waypoint that will notify us when it is triggered. We also have buttons that will toggle display:none, report the waypoint's triggerPoint, and call refreshAll to force a trigger point recalculation.

var waypoint = new Waypoint({
  element: document.getElementById('display-none-example'),
  handler: function(direction) {
    notify('display:none example triggered')
  }
})

Fixed Position Elements

Let's say you haven't seen the sticky shortcut or wanted to implement your own. It seems simple enough, when your sticky element hits the top of the viewport, you give it fixed positioning.

var waypoint = new Waypoint({
  element: document.getElementById('something'),
  handler: function(direction) {
    if (direction === 'down') {
      this.element.style.position = 'fixed'
      this.element.style.top = 0
    }
    else {
      this.element.style.position = 'static'
    }
  }
})

This will work fine…until you resize the browser. Resizing the browser (which calls refreshAll) will force a trigger point recalculation. As part of this calculation, Waypoints looks for the element's position on the page. This doesn't return the position as if the element were still staticly positioned, but instead returns the element's current position, which for a fixed position element changes as you scroll. This is, after all, the point of fixed positioning.

Because of this, you should never use a fixed position element as a waypoint, including elements that gain fixed positioning in the future.