Tuesday, March 20, 2012

Timeline scrolling technique for HTML Gantt

This is a little nice solution of a problem that seemed too big until I actually started to work on it. Turned out to be very simple, as so many things do. I need to implement a timeline at the top of an HTML-based Gantt chart which can be scrolled (and, eventually, even panned) both vertically and horizontally. The timeline has to scroll horizontally with the Gantt, but not vertically - to always stay on the page.


Illustration time (using the nice picture from my previous post on drawing Gantt dependencies in HTML/CSS):

Scrollable HTML Gantt timeline

Do you see what I mean?

Solved by a combination of CSS to govern the display, and JavaScript (using jQuery, since it is already used in the project) to ensure the scrolling behavior.

HTML

Here's the HTML snippet, #main div contains #timeline and #tasks:

<div id="main">
   <div id="timeline">...</div>
   <div id="tasks">...</div>
</div>

Stylesheets

Styles for #main are obvious - include the necessary width / height etc, since in my case the Gantt needs to occupy only a part of the page. It is important to set overflow to hidden, since that will hide the spilling out parts of the timeline:

#main {
   width: 90%; /* or whatever is needed */
   overflow: hidden;
}

The #timeline's CSS is just an empty shell - the actual width is set by the Gantt JavaScript code, determined by the needed output time range, so the width is not needed here (I suspect even the z-index is not required, but I'd like to keep it just in case):

#timeline {
    height: 40px;
}

Nothing special, eh? Then come the #tasks, set to as high and as wide as possible (but also then modified in JavaScript), with key parameters being overflow set to auto (this way the scroll bars are displayed only in case there is an actual overflow - nice for Gantt charts with little data), and position set to relative - this resets the rendering context, and all the contained elements are positioned against the #tasks now - perfect for absolute positioning, the kind we need. The style:

#tasks {
    width: 100%;
    height: 100%;
    position: relative; /* for absolutely positioned elements */
    overflow: auto; /* scroll bars only if needed */
}

There. What happens next is our data-processing code populates the #tasks div with actual tasks (and dependencies, connectors, milestones, labels etc), the div expands to include all this heavy planning, scrollbars appear, and...

JavaScript

Or "the part I feared the most", but it turned out to be trivial. All we do is add a scroll event handler to the #tasks element to move the #timeline with it. No tricks, no "kinetic movement", the timeline just moves with the scrolled content, no ifs no buts.

$(document).ready(function () {
   // move the timeline when tasks scroll
   $('#tasks').scroll(function (event) {
      $('#timeline').css('margin-left', -$('#tasks').scrollLeft());
   });
});

And it works!

If you have any suggestions / comments / improvements - let me know. See you next time, we'll solve another problem.


PS Syntax highlighting was done with the help of the very nice and smart http://tohtml.com/ tool.

No comments:

Post a Comment