5

Let's say I have a situation like this:

  • The page is 4000 pixels long.
  • The user has scrolled down the page, so 1000 pixels of content are hidden above the viewport.

Then, the user clicks a button, and content of arbitrary length is loaded via AJAX at the top of the page, pushing the button (and the content the user was looking at) below the viewport.

I've tried writing a Javascript callback to scroll down to the content the user was looking at before they clicked the button, but the experience is not seamless (a scroll "up" when new content is inserted, followed by a scroll back "down").

Is there any way to keep the viewport fixed on the content the user was looking at?

This is a simplified example, but should get the point across.

<div style="height: 1000px; width:1000px;" id="top-div">some content above the fold</div>
<button id="button">Click Me</button>
<img src="img.jpg" alt="Some image the user was looking at when they clicked the button." />

<script>
$("button").click(function() {
    $.get('/new/content', function(response) {
        $("#top-div").before(response);
    });
});
</script>
Jasper
  • 75,717
  • 14
  • 151
  • 146
Kevin Burke
  • 61,194
  • 76
  • 188
  • 305
  • how about a working fiddle to begin with, so we don't have to setup the whole thingy on our own? :) – Robin Drexler Jan 09 '13 at 21:14
  • Add the new content below what is being viewed. – kennebec Jan 09 '13 at 21:17
  • Not an option in this case... – Kevin Burke Jan 09 '13 at 21:25
  • robin, added a simplified example. – Kevin Burke Jan 09 '13 at 21:28
  • `requestAnimationFrame` would be great for this. When your AJAX request returns, you can setup a `requestAnimationFrame` loop so the next time it fires you can add the new content and scroll to its position before any frames are drawn (theoretically, large amounts of HTML added to the DOM may make this difficult). Here is a great polyfill for `requestAnimationFrame`: http://paulirish.com/2011/requestanimationframe-for-smart-animating/ – Jasper Jan 09 '13 at 21:31

2 Answers2

3

Delay displaying the new content

The closest to an elegant solution that comes to mind is to delay displaying the new content until it's within or below the viewport. In other words, don't change the height of any major layout elements that are above the viewport; change them when they are within or below the viewport, when it won't do any harm. Display them when it's safe to do so.

For example, the user scrolls to the bottom third of a very tall page. While they're down there, some Ajax content of a new or different size is loaded near the top of the page, but it's not displayed yet. The user scrolls back up through the page, and once all of the affected layout area scrolls into view, the new content is displayed, as if it was loaded just then.

Basically, when Ajax content is loaded, retrieve the scroll position of the layout element, and either display the content or add it to a queue, based on the current scroll position of the page. Anytime the user scrolls the page or clicks on an anchor tag (or any action that changes the scroll position of the page), check the queue to see if there's any content that still needs to be displayed, and determine if it can now be safely displayed.

Make the content collapsible

Another option is to have the Ajax content appear in a collapsible format. It could be displayed initially in a small size that doesn't affect the page layout (if the layout element is above the viewport). The user can then click on the content to toggle between the collapsed format and the full version or, in a variation of the previous idea, it could automatically expand when the layout element is scrolled into view.

Matt Coughlin
  • 18,666
  • 3
  • 46
  • 59
2

Check out this fiddle: http://jsfiddle.net/FYEYB/

Here's the important code:

$("button").click(function() {
  //cache the org height
  var orgHeight = $("#content").height();
  //run your ajax request.
  mockAjax(function() {
    //in the callback ajust the height
    $(window).scrollTop($(window).scrollTop() + $("#content").height() - orgHeight);
  });
});

Basically in the callback of your ajax request, add the difference in the height of the container to what it was before. You will probably need to add a check to make sure the new content was indeed added above the viewport, but I'll leave you to figure that out.

InvisibleBacon
  • 3,137
  • 26
  • 27
  • interesting, I'll try it out! – Kevin Burke Jan 09 '13 at 21:29
  • (I know I didn't ask this above, but the actual problem I'm having involves multiple HTTP requests. I thought it would be OK if I simplified it to one. Sadly this approach causes the page to scroll too much.) – Kevin Burke Jan 09 '13 at 22:19
  • Please keep in mind that this answer works for content loaded above the current scroll position but not below. If the dynamic content is below the viewport then the scroll position shouldn't change. This answer will still add the difference to the current scroll position. – Allan Bogh Apr 11 '14 at 22:01
  • Here's a codepen example of how you can maintain the scroll position for added content either above or below the current scroll position: http://codepen.io/anon/pen/GaDkb/?editors=101 – Allan Bogh Apr 13 '14 at 09:14