Element ‘in view’ Event Plugin

I’ve been preparing a few articles for jQuery for Designers and for .net magazine and in doing so I’ve had to write a plugin that could prove to be useful to share.

I’ve created an event that will trigger when the element is scrolled in to the viewport.

Preamble

First of all, this isn’t really a plugin. It’s a utility of sorts. It’s not really a plugin, because you don’t call it. It binds on to the scroll event and does the work for you.

Also, I’m aware that there is the lazyload plugin. I’ve not had real time to play with it, but I suspect there’s some similarities, though my inview plugin is extremely stripped down (because I wrote it for a particular purpose). Also note that my code only works for vertical scroll, and not horizontal.

I should also add that this utility/plugin was inspired by Dustin Diaz’s detect when an element scrolls in to view code.

Demo

The example is mostly lorem text, but in the middle of the page is an element whose text reads: “You can’t see me”. When the element is scrolled in to view it will change to “You found me”.

To confirm this, open firebug while the element is out of view, and watch the element in question as you scroll it in to view.

http://jsbin.com/ugupa/1 (to edit: http://jsbin.com/ugupa/1/edit)

Download

Download jQuery inview event plugin

Usage

The script makes use of the new $.support properties – so it will only work with jQuery 1.3 upwards. If you need to use it with older versions of jQuery, drop a comment, and I’ll post an alternative.

The event will only fire when the element comes in to view of the viewport, and out of view. It won’t keep firing if the user scrolls and the element remains in view.

Bear in mind if think the element may already be in view, you may need to kick the scroll event (using $(window).scroll()). If you include this plugin last (i.e. after you’ve hooked in to the event), then the script will automatically trigger the scroll event – therefore sending the event to your bound element.

The variable after the event argument indicates the visible state in the viewport.

$('div').bind('inview', function (event, visible) {
  if (visible == true) {
    // element is now visible in the viewport
  } else {
    // element has gone out of viewport
  }
});

To stop listening for the event – simply unbind:

$('div').unbind('inview');

Remember you can also bind once:

$('div').one('inview' fn);

How it works

When the window is scrolled, the event checks the position of the elements against the viewport height and the scrollTop position.

However, I wanted to create a utility that would only check the elements that were registered under the ‘inview’ event, i.e. I didn’t want to keep checking the element list if we unbind from the event.

This is achieved by dipping in to the $.cache store within jQuery, and looping through, looking for the elements tied to the ‘inview’ event.

This way the user can treat it like a native event on the page.

33 Responses to “Element ‘in view’ Event Plugin”

  1. Cool! Great idea Remy!

    I did something similar (although, probably not as useful) a while ago by creating a new selector for jQuery (or rather, Sizzle):

    $.extend($.expr[':'],{
        inView: function(a) {
            var st = (document.documentElement.scrollTop || document.body.scrollTop),
                ot = $(a).offset().top,
                wh = (window.innerHeight && window.innerHeight < $(window).height()) ? window.innerHeight : $(window).height();
            return ot > st && ($(a).height() + ot) < (st + wh);
        }
    });
    
    // Example:
    if ( $('div#whatever').is(':inView') ) {...}
    
  2. Nice work Remy…I dig it.

  3. Brilliant plugin. You already saw Lazy Load. Did you also notice the Viewport selectors?

    http://www.appelsiini.net/projects/viewport

  4. I know it’s not related, But how about some updated to the visualjquery site?

  5. Hello

    Thank you very much. This was just what I needed :)

  6. Is it possible to make this code work for a div with scroll bars, so that you can find out which elements within are showing within that divs viewport?

  7. @Bobby – I’m currently working on exactly that. I’ve had quite a bit of trouble getting it working, but that could just being totally jetlagged and coding – I’ll post up here if I crack it (though it’s for a jQuery for Designers article).

  8. Hi Remy, im using your inview code to load new sets of thumbnails through ajax when someone scrolls to the bottom of a page. It works beautifully on FF and Safari, but not in IE. What happens is that the new thumbs push the inview element out of the view. This isnt detected, so I force a $(window).scroll(). (else the event keeps triggering). Now FF and Safari are happy. But in IE, this also scrolls the window to the top. I’ve tried everything I can think of to prevent this from happening, but no luck. Maybe you have some insight?

  9. Can this be made to work as a jQuery Live event using .live and .die in addition to .bind and .unbind?

  10. Hi Remy,
    Played around with this and its great, one small thought – should this not react to the window.resize event too? Like so:

    $(window).scroll(handleViewportChange);
    $(window).bind('resize', handleViewportChange);
    
    function handleViewportChange() {
      var vpH = getViewportHeight(),
          scrolltop = (document.documentElement.scrollTop ?
              document.documentElement.scrollTop :
              document.body.scrollTop),
          elems = [];
    
      // naughty, but this is how it knows which elements to check for
      $.each($.cache, function () {
        if (this.events && this.events.inview) {
          elems.push(this.handle.elem);
        }
      });
    
      if (elems.length) {
        $(elems).each(function () {
          var $el = $(this),
              top = $el.offset().top,
              height = $el.height(),
              inview = $el.data('inview') || false;
    
          if (scrolltop > (top + height) || scrolltop + vpH < top) {
            if (inview) {
              $el.data('inview', false);
              $el.trigger('inview', [ false ]);
            }
          } else if (scrolltop < (top + height)) {
            if (!inview) {
              $el.data('inview', true);
              $el.trigger('inview', [ true ]);
            }
          }
        });
      }
    }
  11. Good job Remy! Thanks for this code, I would like to know if it is possible to detect if an element is fully visisible, partially visible or not visible at all ? Thanks in advanced for your response.

  12. Thanks for this plugin! I imported the code into github at http://github.com/zuk/jquery.inview

    … planning on making some additions to it via extra config options.

  13. Has anyone got this working on iphone safari? It’s kinda working for me, i’ve got a div to display on scroll but then never disappears?

  14. 2 Matt Zukowski: I’ve forked & updated some to your Git version:
    http://github.com/nugged/jquery.inview

    - added onResize & onClick detection,
    - added third parameter to detect whether element top and bottom or both seens by user,
    - added more complex example with timeout

    inview is good, thanks remy!

    now we need to test it in all major browsers.

  15. Thumbs up… I didn’t use this in a real website with many listeners, but I just wanted to confirm that this plugin works on IE8 in all modes including Quirks mode.. BRAVO!

  16. Any news about the possibility of using this on an overflowing container not the window?? I can help in that if you’d like.. just point me to the direction you are thinking of, because there are many ways and probably most of them won’t work.

  17. Hi Remy,

    I spend some time to enhance and extend the code.
    Check the repository: https://github.com/protonet/jquery.inview and the blog post: http://protonet.info/post/2516624585/jquery-inview-plugin

    It now supports:
    * live/delegate events
    * horizontal scrolling
    * detecting whether an element is in the viewport without checking onscroll
    * …

    Cheers,
    Christopher

  18. Hi Remy,

    Thanks for the great code! I’m trying to tweak it so that rather than change the text, it changes the background color of some text in the nav bar. Got it separated between the nav and the content no problem, but having a heck of a time getting it to change the background color! Any tips for a js noob?

    thanks!
    Brook

  19. This unfortunately doesn’t work anymore with the 1.5 version of jQuery, as they changed the $.cache object and how events are stored.

    However this is fixed in my fork. Check https://github.com/protonet/jquery.inview

  20. thanks Christopher, this is great!

  21. Thanks Christopher. I’ve noticed that your code doesn’t accurately understand when a div is coming into view when approached by scrolling up. In most situations this would not be noticeable since most sites will simply start from the top and move downwards. On my site I am binding the event after a particular scrollTo command has been executed. I am doing this to prevent all of the images above from automatically loading. I do this to prevent loading everything in between where I am scrolling to in code.

    So there appears to be some inaccuracy in determining when an element is in view above the current position. I’ve determined this inaccuracy to be somewhere in the range of about 20-30px. If this could be tightened up then it would be better. I’ve read your code and I can’t quite get my head into the positioning logic. I’m hoping that this would be a simple fix for you to do. Otherwise I’ll be diving in.

    Thanks.

  22. Hi,

    Just noticed a similar plugin

    http://plugins.jquery.com/project/inView

    Cheers

  23. I’m having an issue with this working in Firefox (but not Chrome or Safari). The issue is that the images that aren’t in view when the page is loaded don’t load when horizontal scroll takes place.

    Any thoughts on what might be causing this?

    Thanks!

  24. Here’s an example of it not working in Firefox: http://galenvinter.com/test-inview/

    Would really appreciate any thoughts!

  25. To add to Christopher Blum’s comment it doesn’t work with 1.5.* through 1.6.* but will work with versions below 1.5, and 1.7 and above.

  26. @James – Thanks for that selector extension, you just saved my day!

  27. Add the following (line54) code to fix problem when object is already in viewport on page load.

    $(window).load(function(){
    $(window).scroll();
    })

  28. hi -
    this looks great.
    I am looking for something similar but inside a div with scrolling, like:
    http://jsbin.com/ugupa/1208/

  29. How would one go about ‘kicking the scroll event’? I’m having trouble with this.

    Would .trigger() work?

    $(window).scroll(function () {
      $('.our-story').trigger('inview');
      $('.residential .right-wrap').trigger('inview');
      $('.commercial .left-wrap').trigger('inview');
      $('.faq ul').trigger('inview');
    });
  30. Great plugin! I’ve been looking for a solution for quite some time and luckily found this page. This was the best option I found. Thanks for your contribution!

  31. Has anyone managed to do this with a horizontally scrolling div that has overflow:auto, know this is for the vertical inview but searching everywhere and failed to edit any code to do it

  32. Hi! Thanks for this. It didn’t however work for me, as $.cache returns undefined. I’m using jQuery v2, so it might have been removed.

    I rewrote it as a plugin that checks which element in a given jQ collection is visible. I removed the scroll event from the plugin, but most of the other stuff is still there.

    The rewritten code is here: http://pastebin.com/6vtdJZLw

    I use it by putting

    
    var $inView = $("selector").inview();
    

    in a scroll event listener, then doing what I need to with the element it returns.

    Be aware that I’ve decreased the detected height of the elements by removing vpH / 1.5 from the heights of the elements. This is because I only want it to fire when the element is a good bit inside the viewport.

    Currently the “lazy detection” only works when scrolling up, though. I’ll have to refine this some more! But the idea is there.

    Any idea how to fix your original event code?

  33. Is there a way to make this not target window? A parent container element instead of window then the inview event could be dispatched for the elements inside the parent container.

Leave a Reply
Not required

CODE: Please escape code and wrap in <pre><code>, doing so will automatically syntax highlight