I’m working on a project – AroundTheseWords.com – that has user submitted comments. When a user submits a comment I want to scroll the app to the bottom automatically. Sounds pretty easy but it gets slightly tricky.

The setup
This is a Flex 4 app where and the Application is skinned to add scrollBars. ( See post: Put a border around your scrollable Flex 4 app ). It is simple to tell the AppSkin to scroll to the bottom of the page. In the skin class just say:

scroller.verticalScrollBar.value = scroller.verticalScrollBar.maximum;

That will scroll to the bottom. But the question is when to do it. If I simply tell the skin to scroll to the bottom directly then it will do so however there might be content added to the Application after it scrolls and then I’m not at the bottom anymore.

My first thought was to set a flag – _flag_scrollToBottom – in the skin telling it to scroll to the bottom on the next updateComplete of the  <s:Scroller> component. But there is no telling how many times that event might fire. In my case it went off 4 times.  But after I scroll to the bottom I want to turn the flag off so that it doesn’t keep scrolling to the bottom on every single updateComplete.

A little hackery
This seems a little hackery but it works fine. When I set the _flag_scrollToBottom to true I also set the _flag_time to the current Date – new Date(). Now in the updateComplete event I keep scrolling to the bottom as long as _flag_scrollToBottom is true. And at the same time I check to see if more than 1000 milliseconds have expired since the flag was set. My thinking is that any updates are going to take place in much less than 1 second. Now the next time the user does something to change the height of the app the updateComplete event will fire again and it will find that more than 1000 milliseconds have passed and so now it will set the _flag_scrollToBottom back to false and then return without scrolling to the bottom. That all seems to work just fine.

Below is the pertinent code from the AppSkin.mxml file:

private function setVertScrollToMax():void {
	_flag_scrollToBottom = true;
	_flag_time = new Date();
	scroller.verticalScrollBar.value = scroller.verticalScrollBar.maximum;
}

private function onUpdateComplete():void {
	/**
	 * 	Hack: the problem here is that the 'updateComple' event fires
	 *		many times when the display is updated. For instance when
	 * 	submitting a comment it fires when the comment is rendered by
	 * 	the DateRenderer and then again when the submit comment field
	 * 	is repositioned at the bottom. So when is it actually DONE
	 * 	updating? I'm going assume that it takes less than one second
	 * 	to do all that and it's unlikely that the user will enter
	 * 	anything during that short time. So I don't unset the
	 *		_flag_scrollToBottom until at least one second has passed.
	 * 	What should happen is that the next time the updateComplete
	 * 	fires due to a screen update is that it does reset the _flag_
	 *		and then returns without scrolling to the bottom.
	 */
	if( _flag_scrollToBottom ) {
		var now:Date = new Date();
		if( now.time - _flag_time.time > 1000 ) {
			_flag_scrollToBottom = false;
			return;
		}
		scroller.verticalScrollBar.value = scroller.verticalScrollBar.maximum;
	}
}

Note: I’m using my MCP event dispatcher class to notify AppSkin.mxml that I want it to scroll to the bottom. I’m sure you’ll come up with your own technique of communicating with your AppSkin.mxml.

What do you think?