I have a meteor.js
app that functions as a news feed, with threads being posted and people menting on the threads in realtime. This means that while you are looking at a post, there will be new ments 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?
I have a meteor.js
app that functions as a news feed, with threads being posted and people menting on the threads in realtime. This means that while you are looking at a post, there will be new ments 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?
You could try this approach:
scrollTop
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./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.
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 pensation).
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>
You can use package animated-each meteor add mizzao:animated-each
, to solve the problem.
It would be certainly better than implementing custom jquery. And if package does not solve exact problem, then you can use it as an example on how to hook into rendering and create solution the meteor way. Probably by utilising meteor-ui-hooks