Friday, July 12, 2013

Detect When a DOM Element is Scrolled into View using jQuery

The browser window can only show so much content before the user has to scroll. Knowing if or when a certain element has scrolled in or out of the visible area on the screen may be useful in certain situations. Not intending to reinvent the wheel or anything, I did some research to see what other libraries existed related to scrolling in the browser:



I'm sure others exist but I thought it was an interesting problem to explore so I set out to build a basic solution that would have the following functionality:

  • Add elements of interest to some global list with configuration options
  • Listen to the window scroll event and check each element to see if its in the viewport
  • When it first enters/leaves trigger an event/callback to allow further processing


Register Elements

First, we need to be able to register the elements we care about so their scroll position can be monitored. We'll use this global list in the scroll event handler later:



This just creates a tracking object which contains state, options, and the target element.

Monitor Scrolling

The next step is to listen for scroll events on the window. We can't listen to the individual items since they won't generate scroll events - the container that scrolls will emit these which means we need to listen to that container element and then check all the registered items to determine if they scrolled in or out of the visible part of the page. However, scrolling can generate a lot of events and probably will cause a performance hit if we are constantly trying to test for the position of all the registered elements. What we need is a way to only process one event per some interval of time. A possible solution is the Underscore throttle function which is a nice way to manage events that may rapidly fire to help boost performance in a situation where it might degrade if the underlying logic ran that frequently. Now, I didn't want a dependency on Underscore in this case so I just implemented a simple version specific to my needs:



Another benefit of this buffer is if the user scrolls quickly past an item, it won't be detected as being in the view.

Check and Fire Events

The checkInView() function referenced above iterates over all the registered elements we're interested in and checks the boundaries of the element against the visible part of the page. When the element was registered, an object was created with a invp key to track the state of the element. False means its not in the visible part of the screen and true means it is. This allows us to track when it changes and to fire the appropriate event:



Create a Plugin

At this point, we have the three parts required to enable the detection and trigger an event. Now, we just need to package it up so its easier to use with jQuery. For testing, I made a quick plugin:



This plugin is lacking the necessary cleanup handling and some other housekeeping but will work well enough for now. Next, we can create some markup:



And use the plugin to monitor the DIVs as they are scrolled:



I dropped this code into jsFiddle as a demo. You'll need to have your console open to see the output.

Next Steps

At this point, this is only a proof-of-concept to hash out the required functionality for a more robust version. Its missing some critical elements:
  • Only detects scrolling up and down not left and right
  • Only can detect elements scrolling in the window, not elements contained inside another element that scrolls
  • The jQuery plugin is lacking teardown logic required to stop monitoring an element and free up memory
In order to get there, some major refactoring is necessary to fill the gaps in functionality. As part of that reimplementation, I'd probably switch to using the jQuery UI widget factory which neatly encapsulates the details of initializing/destroying a jQuery plugin. Additionally, this plugin only detects the positioning and raises an event. It seems that including the ability to animate the scroll position instead of using another library would make sense as well.

I've built a more robust version of this plugin using the jQuery UI widget library as a basis. This enhanced version enables monitoring, querying, or changing the scroll position of an element relative to any scrolling container: