Eu encontrei o mesmo problema, mas com uma restrição adicional: eu não tinha controle sobre o código que anexava novos elementos ao contêiner de rolagem. Nenhum dos exemplos que encontrei aqui me permitiu fazer exatamente isso. Aqui está a solução que eu acabei com.
Ele usa Mutation Observers
( https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver ) que o torna utilizável apenas em navegadores modernos (embora existam polyfills)
Então, basicamente, o código faz exatamente isso:
var scrollContainer = document.getElementById("myId");
// Define the Mutation Observer
var observer = new MutationObserver(function(mutations) {
// Compute sum of the heights of added Nodes
var newNodesHeight = mutations.reduce(function(sum, mutation) {
return sum + [].slice.call(mutation.addedNodes)
.map(function (node) { return node.scrollHeight || 0; })
.reduce(function(sum, height) {return sum + height});
}, 0);
// Scroll to bottom if it was already scrolled to bottom
if (scrollContainer.clientHeight + scrollContainer.scrollTop + newNodesHeight + 10 >= scrollContainer.scrollHeight) {
scrollContainer.scrollTop = scrollContainer.scrollHeight;
}
});
// Observe the DOM Element
observer.observe(scrollContainer, {childList: true});
Fiz um violino para demonstrar o conceito:
https://jsfiddle.net/j17r4bnk/