[AJAX] [PROGRAMMING] Allow user to scroll and maintain position with "Scroll To Bottom of the Div" example
Allow user to scroll and maintain position with "Scroll To Bottom of the Div" example
Well I am getting tired of being emailed the same question about my entry Scroll To Bottom of a Div. So I sat down in a few minutes I came up with this. My first attempt used onscroll, but it Opera appears to not supporet onscroll on a div. So I had to twidle my thumbs and realized I just had to use the last know position as a reference. Duh...
So how do I keep the scroll position of a div if the user scrolls it and also allow for it to stick to the bottom?
Put this code in your head:
var chatscroll = new Object();
chatscroll.Pane = function(scrollContainerId){
this.bottomThreshold = 20;
this.scrollContainerId = scrollContainerId;
this._lastScrollPosition = 100000000;
}
chatscroll.Pane.prototype.activeScroll = function(){
var _ref = this;
var scrollDiv = document.getElementById(this.scrollContainerId);
var currentHeight = 0;
var _getElementHeight = function(){
var intHt = 0;
if(scrollDiv.style.pixelHeight)intHt = scrollDiv.style.pixelHeight;
else intHt = scrollDiv.offsetHeight;
return parseInt(intHt);
}
var _hasUserScrolled = function(){
if(_ref._lastScrollPosition == scrollDiv.scrollTop || _ref._lastScrollPosition == null){
return false;
}
return true;
}
var _scrollIfInZone = function(){
if( !_hasUserScrolled ||
(currentHeight - scrollDiv.scrollTop - _getElementHeight() <= _ref.bottomThreshold)){
scrollDiv.scrollTop = currentHeight;
_ref._isUserActive = false;
}
}
if (scrollDiv.scrollHeight > 0)currentHeight = scrollDiv.scrollHeight;
else if(scrollDiv.offsetHeight > 0)currentHeight = scrollDiv.offsetHeight;
_scrollIfInZone();
_ref = null;
scrollDiv = null;
}
Create a new instance with the name of the div;
var divScroll = new chatscroll.Pane('divExample');
When ever you add something to the div call the method activeScroll
divScroll.activeScroll();
And the magic will occur. Below is a working example (may have to refresh, example runs for 1.5 seconds):
I tested this on Win XP with IE6, Firefox 1.5, Netscape 8.04, Mozilla 1.7.12, and Opera 8.5.1 with no issues.
My MAC testers came through: Safari 2.0.4, Camino 1.0.2int, Firefox 1.5.0.5, and Opera 9.0.1 are good. Minor issue with Opera 8.52 and touchpad. I don't think that is a show stopper.
Eric Pascarello
Coauthor of Ajax In Action
Moderator of HTML/JavaScript at www.JavaRanch.com
Author of: JavaScript: Your Visual Blueprint for building Dynamic Web Pages
Eric
function getMessageSentComplete(result)
{
if (result == "")
return;
if (chatScroller != null)
var autoScroll = chatScroller.isScrollable();
$("chatMessages").innerHTML = $("chatMessages").innerHTML + result.substring(1);
if (chatScroller == null)
{
chatScroller = new ChatScroll.Pane('chatMessages');
chatScroller.resize();
window.onresize = resizeEvent;
}
else
{
if (autoScroll == true)
chatScroller.scrollToEnd();
if (result.charAt(0) == '!')
notifySound.Play();
}
}
function resizeEvent()
{
chatScroller.resize();
}
The way I solved it was to check to see if a scroll to the end is required before I add the returned message content. This was done in the
var autoScroll = chatScroller.isScrollable();The resize is handled by setting the
window.onresize = resizeEvent;to the chatScrollers object resize. This helps keep the content scrolled to the end on a resize. The following is my updated version of the chatScroller to take into account these activities.
var ChatScroll = new Object();
ChatScroll.Pane =
function(scrollContainerId)
{
this.bottomThreshold = 50;
this.scrollContainerId = scrollContainerId;
}
ChatScroll.Pane.prototype.resize =
function()
{
var scrollDiv = document.getElementById(this.scrollContainerId);
scrollDiv.style.height = (document.documentElement.clientHeight - 225) + 'px';
scrollDiv = null;
this.scrollToEnd();
}
ChatScroll.Pane.prototype.scrollToEnd =
function()
{
var scrollDiv = document.getElementById(this.scrollContainerId);
scrollDiv.scrollTop = scrollDiv.scrollHeight;
scrollDiv = null;
}
ChatScroll.Pane.prototype.isScrollable =
function()
{
var scrollDiv = document.getElementById(this.scrollContainerId);
var currentHeight = 0;
if (scrollDiv.scrollHeight > 0)
currentHeight = scrollDiv.scrollHeight;
else
if (objDiv.offsetHeight > 0)
currentHeight = scrollDiv.offsetHeight;
var Result = (currentHeight - scrollDiv.scrollTop - ((scrollDiv.style.pixelHeight) ? scrollDiv.style.pixelHeight : scrollDiv.offsetHeight) < this.bottomThreshold);
scrollDiv = null;
return Result;
}
This version has a scrollToEnd() method which might resolve Chemin's issue.
Peter
function ScrollingLogger(id) {
var obj = $(id);
this.write = function(message) {
obj.innerHTML += message + "<br/>";
if (obj.scrollHeight > obj.offsetHeight) {
obj.scrollTop = obj.scrollHeight - obj.offsetHeight;
}
};
};
var logger = new ScrollingLogger("log");
Could you tell me why style.pixelHeight is required in your example? Is it for browser compatability?
<script type=text/javascript>
function ScrollingLogger(id, message)
{
var obj = document.getElementById(id);
obj.innerHTML += message+"<br/>";
if (obj.scrollTop==0) obj.scrollTop=4;
if (obj.scrollTop+12==obj.scrollHeight - obj.offsetHeight)
{
if (obj.scrollHeight
> obj.offsetHeight)
{
obj.scrollTop = obj.scrollHeight - obj.offsetHeight;
}
}
};
function add()
{
var logger = new ScrollingLogger("chat_text","message");
}
</script>
function receivedMsg(msg, obj)
{
if(msg.length > 0)
{
obj.innerHTML += "<br/>"+msg;
obj.scrollTop += obj.scrollHeight;
}
}
var chatscroll = new Object(); chatscroll.Pane = function(scrollContainerId) { this.bottomThreshold = 25; this.scrollContainerId = scrollContainerId; } chatscroll.Pane.prototype.activeScroll = function() { var scrollDiv = document.getElementById(this.scrollContainerId); var currentHeight = 0; if (scrollDiv.scrollHeight > 0) currentHeight = scrollDiv.scrollHeight; else if (objDiv.offsetHeight > 0) currentHeight = scrollDiv.offsetHeight; if (currentHeight - scrollDiv.scrollTop - ((scrollDiv.style.pixelHeight) ? scrollDiv.style.pixelHeight : scrollDiv.offsetHeight) < this.bottomThreshold) scrollDiv.scrollTop = currentHeight; scrollDiv = null; }