How to animate elements within boundaries using jQuery?
January 14, 2011 10:48 AM   Subscribe

Using jQuery, how can I make an element move up or down the page as the user scrolls, until it reaches a certain distance from its parent's border or the viewport border?

The page I'm working with has a bunch of nested divs, each one with a rotated p attached to its left border.

As the viewer scrolls down, the top of the viewport will approach a p. When they get within say 30px of each other, I want the p to get sticky and start sliding down with the scroll. But when each p hits a certain distance from the bottom border of its parent div, I want it to stop sliding and stay put.

So the p tags attached to the largest, outermost divs will continue to stick pretty much until you hit the bottom of the page. Those attached to smaller, inner divs will hit their limits and scroll off the page. The same thing should happen on the way back up.

This gives me some simple slide-down functionality:

$(window).scroll(function() {
$(".side-label").animate({"top": "+=50px"}, "slow");
});

But it has nothing to stop the motion. I'm not sure where to start with that business.

Any ideas, or examples from the wild are much appreciated!
posted by Fred Mars to Computers & Internet (8 answers total) 2 users marked this as a favorite
 
So they start like this:
+--------------------------+    
|                          |
|    +---+----------+      |
|    | p |          |      |
|    +---+          |      |
|    |              |      |
|    |              |      |
|    |              |      |
|    +--------------+      |
|                          |
+--------------------------+
And end like this?
+--------------------------+    
|                          |
|    +--------------+      |
|    |              |      |
|    |              |      |
|    |              |      |
|    +---+          |      |
|    | p |          |      |
|    +---+          |      |
|    +--------------+      |
|                          |
+--------------------------+
What's the CSS that puts them in their initial position? If the viewport is so large that the p's won't need to scroll, what position are they in? What determines whether they should scroll? Are you trying to keep them near the top of the browser window or some other landmark?
posted by artlung at 11:39 AM on January 14, 2011


Oh, and you mention nesting, is that what you mean by getting closer to each other? So you want p's at different points in the hierarchy of divs to not collide or overlap?
posted by artlung at 11:40 AM on January 14, 2011


When you get close to your position, apply a CSS class that does "position: fixed". Then it will stick as the user scrolls. Remove this attribute when you don't want it to be sticky anymore.

You can do this check in a mouse movement callback, I think.
posted by jrockway at 12:02 PM on January 14, 2011


Best answer: You want to read window.scrollTop() to get how far the window has been scrolled, check the [p] elements' offset() to get their top, the [div] elements' offset() and height(), to determine within what bounds you want to be repositioning a p, then just do the math and set the [p]'s offset() directly if the window's scroll position is somewhere within the div.

Something along these lines (I have not tested this, it's almost certainly buggy, but you get the idea:)

$(window).scroll(function() {
  var scrolled = $(window).scrollTop();
  var slop = 30;
  $('.side-label').each(function() {
    var container = $(this).closest('div'); // modify this if necessary to find the container div you care about
    var topOfRange = container.offset().top + slop;
    var bottomofRange = container.offset().top + container.height() - $(this).height() - slop;
    if (scrolled > topOfRange && scrolled < topOfRange) {
      $(this).offset().top = scrolled + slop; // this actually positions the p
    }
  })
});

Off the top of my head I can't remember if the scroll event fires continuously during a scroll or only once at the end. If it's only once, you might want to use animate() to move the p gradually to its new position instead of just snapping it into place.
posted by ook at 12:04 PM on January 14, 2011 [1 favorite]


That would be if (scrolled > topOfRange && scrolled < bottomOfRange), of course.
posted by ook at 12:06 PM on January 14, 2011


I can't answer this directly, but you'll probably get some excellent answers at Stackoverflow.com.
posted by blue_beetle at 12:14 PM on January 14, 2011


Okay, I tested it. Turns out you can't set the offset top independently from the left; it has to be

$(this).offset({top: scrolled + slop, left: $(this).offset().left});

And bottomOfRange should subtract 2*slop, not slop.

Other than that it seems to work fine.
posted by ook at 12:23 PM on January 14, 2011


Response by poster: Sorry for the long delay there, everybody, and thanks for all the suggestions. Well, not just suggestions......ook, your code does exactly what I was envisioning! I'll post again soon with a sample of it in action.
posted by Fred Mars at 9:19 AM on January 18, 2011


« Older Condoms? Condoms!   |   Is vitamin B6 making me miserable? Newer »
This thread is closed to new comments.