Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lock scroll as new content is added above and below

I have a meteor.js app that functions as a news feed, with threads being posted and people commenting on the threads in realtime. This means that while you are looking at a post, there will be new comments being added to posts above and below and new threads will be added above. This will push the post you are focusing on down and out of the viewport, which is never expected (unless you're already scrolled to the top).

What is the best way to update the scrolling to maintain the same visual center as new content is added?

like image 791
JacobPariseau Avatar asked Dec 21 '15 22:12

JacobPariseau


People also ask

What prevents the body from scrolling when overlay is on?

Approach: To prevent body scrolling but allow overlay scrolling we have to switch the overflow to be hidden when the overlay is being shown. And while the whole page's overflow is hidden or the scroll is disabled the overflow in the y-axis of the overlay is enabled and the overflow can be scrolled.

How do I make a scroll overlay?

For touch devices, try adding a 1px wide, 101vh min-height transparent div in the wrapper of the overlay. Then add -webkit-overflow-scrolling:touch; overflow-y: auto; to the wrapper. This tricks mobile safari into thinking the overlay is scrollable, thus intercepting the touch event from the body.

How do you freeze a scroll in CSS?

To hide the scrollbar and disable scrolling, we can use the CSS overflow property. This property determines what to do with content that extends beyond the boundaries of its container. To prevent scrolling with this property, just apply the rule overflow: hidden to the body (for the entire page) or a container element.


2 Answers

You could try this approach:

  1. save the scrollTop value;
  2. prepend the content (i.e., add a new post above the one that's focused);
  3. add the height of the new content to the value saved in step 1;
  4. scroll to the new value.

Here's an example:

function randomString() {
  return Math.random().toString(36).substring(7);
}

$('#addAbove').on('click', function(e){
  var newDiv = $('<li class="post">' + randomString() + '</li>');
  var savedScrollTop = $('#content').scrollTop();
  $('#content').prepend(newDiv);
  $('#content').scrollTop(savedScrollTop + newDiv.height());
});

$('#addBelow').on('click', function(e){
  var newDiv = $('<li class="post">' + randomString() + '</li>');
  $('#content').append(newDiv);
});
#content {
  height: 100px;
  overflow-y: scroll;
}
.post {
  height: 40px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="content">
  <li class="post">u9tv190be29</li>
  <li class="post">dgdr8gp66rp</li>
  <li class="post">93vfntdquxr</li>
</ul>
<button id="addAbove">Add Above</button>
<button id="addBelow">Add Below</button>

Appending content below the current viewport shouldn't require adjustment.

Here's a blog post describing a similar approach, and a similar SO question.

like image 109
rphv Avatar answered Oct 14 '22 06:10

rphv


I suggest you do like so, when a new item is to be added you check if you are at the top, if so, then just add the new item.

If not, you get the existing top/height of your item container, add the new item, get the item containers height a second time and then update the scroll top.

Here is a simple sample of how one can do, adding at both top and bottom (btw, at bottom don't need scroll compensation).

function addItem (totop) {
  var msgdiv = document.getElementById('items');
  var attop = scrollAtTop(msgdiv);
  var prevtop = parseInt(msgdiv.scrollHeight - msgdiv.scrollTop);

  if (totop) {
    msgdiv.innerHTML = 'Long long content ' + (tempCounter++) + '!<br/>' + msgdiv.innerHTML;

    if (!attop) {
      updateScroll(msgdiv, parseInt(msgdiv.scrollHeight) - prevtop);
    }
  } else {
    msgdiv.innerHTML += 'Long long content ' + (tempCounter++) + '!<br/>';
  }
}

var tempCounter = 10;
function updateScroll(el, top){
  el.scrollTop = top;
}
function scrollAtTop(el){
  return (el.scrollTop == 0);
}
html, body { height:100%; margin:0; padding:0; }

.items{
  display: inline-block;
  width: 300px;
  height: 220px;
  border: 1px solid black;
  overflow: auto;
}


button { width: 15%; height: 44px; margin: 20px; vertical-align: top; }
<div class="items" id="items">
  Long long content 9!<br/>
  Long long content 8!<br/>
  Long long content 7!<br/>
  Long long content 6!<br/>
  Long long content 5!<br/>
  Long long content 4!<br/>
  Long long content 3!<br/>
  Long long content 2!<br/>
  Long long content 1!<br/>
</div>
<button onclick="addItem(true);">Add 2 top</button><button onclick="addItem(false);">Add 2 bottom</button>
like image 44
Asons Avatar answered Oct 14 '22 06:10

Asons