Vis JS Timeline - Freeze horizontal time axis when vertically scrolling

Viewed 504

I'm using the following options in Vis JS Timeline to produce a horizontal axis at the top of the timeline with time labels:

orientation: {
    axis: 'both'
},

The horizontal axis looks like this:

enter image description here

My timeline has many rows, so the user needs to vertically scroll down the page to see everything. The problem is that the horizontal axis at the top does not stay in view when scrolling down the page.

Question: How can I freeze the horizontal axis at the top so that the time labels stay in view when scrolling down?

The following code snippet, or jsfiddle.net/nj1647tb, is my timeline:

const seed = '11';
Math.seedrandom(seed);

const nGroups = 40;
const maxSubGroups = 2;
const maxItemsPerSubGroup = 1;
const metaEventCount = 2;
const itemLengthScale = 200;

let now = moment().minutes(0).seconds(0).milliseconds(0);
var groupCount = 12;
var itemCount = 70;
var tcCrashProbability = 0.2;

function randInt(min, max) {
  return Math.round(min + Math.random() * (max - min));
}

function getStartEnd(earliestStart) {
  if (earliestStart === undefined) {
    earliestStart = 0;
  }
  let startAdd = earliestStart + Math.random() * 200;
  let length = Math.random() * itemLengthScale;
  let endAdd = startAdd + length;
  return {
    startAdd: startAdd,
    endAdd: endAdd
  }
}

const stackTrace = `Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/requests/adapters.py", line 449, in send
    timeout=timeout
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 756, in urlopen
    method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
  File "/usr/local/lib/python3.6/site-packages/urllib3/util/retry.py", line 532, in increment
    raise six.reraise(type(error), error, _stacktrace)
  File "/usr/local/lib/python3.6/site-packages/urllib3/packages/six.py", line 769, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 706, in urlopen
    chunked=chunked,
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 445, in _make_request
    six.raise_from(e, None)
  File "<string>", line 3, in raise_from
  File "/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py", line 440, in _make_request
    httplib_response = conn.getresponse()
  File "/usr/lib64/python3.6/http/client.py", line 1346, in getresponse
    response.begin()
  File "/usr/lib64/python3.6/http/client.py", line 307, in begin
    version, status, reason = self._read_status()
  File "/usr/lib64/python3.6/http/client.py", line 268, in _read_status
    line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
  File "/usr/lib64/python3.6/socket.py", line 586, in readinto
    return self._sock.recv_into(b)
  File "/usr/lib64/python3.6/ssl.py", line 971, in recv_into
`;

// create a data set with groups
var group_names = [];
var groups = new vis.DataSet();
for (let i = 0; i < nGroups; i++) {
  group_names.push('GROUP_' + i);
  groups.add({
    id: group_names[i],
    content: group_names[i]
  });
}

// add meta group
groups.add({
  id: -1,
  content: '&nbsp;'
});

// create a dataset with items
let items = new vis.DataSet();
for (let i = 0; i < nGroups; i++) {
  let nSubGroups = randInt(1, maxSubGroups);
  //console.log('group='+i+' nSubGroups='+nSubGroups);
  let lastStartAdd = 0;
  for (let sg = 0; sg < nSubGroups; sg++) {
    let start_end = getStartEnd(lastStartAdd);
    let start = now.clone().add(start_end['startAdd'], 'hours');
    let end = now.clone().add(start_end['endAdd'], 'hours');
    let itemID = 'G' + i + '_S' + sg + '_item';
    let subgroupID = 'G' + i + '_S' + sg;
    let subgroupOrder = sg;
    let newItem = {
      id: itemID,
      group: group_names[i],
      subgroup: subgroupID,
      subgroupOrder: subgroupOrder,
      content: 'ITEM_DU_' + 'G' + i + '_S' + sg,
            start: start,
      end: end,
      title: 'ITEM_DU_' + 'G' + i + '_S' + sg
    };
    //console.log(group_names[i] + ', ' + 'S' + sg + ', ' +start_end['startAdd'] + ', ' + start_end['endAdd']);    
    items.add(newItem);
    lastStartAdd = start_end['startAdd'];
    
    // random crashes
    if(Math.random() <= tcCrashProbability) {
        let crashStart = now.clone().add(randInt(start_end['startAdd'], start_end['endAdd']), 'hours');
      let newCrashItem = {
        id: 'crash_' + itemID,
        group: group_names[i],
        subgroup: subgroupID,
        subgroupOrder: subgroupOrder,
        content: 'Crash',
        start: crashStart,
        type: 'box',
        className: 'timeline-tc-crash',
        title: '<pre>' + stackTrace + '</pre>'
      };
      items.add(newCrashItem);
    }
    
  }
}

// generate some meta events
for (let i = 0; i < metaEventCount; i++) {
  let start = now.clone().add(Math.random() * 200, 'hours');
  items.add({
    id: 'M' + i,
    group: -1,
    content: 'Crash',
    title: '<pre>' + stackTrace + '</pre>',
    className: 'timeline-event-crash',
    start: start,
    type: 'box'
  });
}

// create visualization
var container = document.getElementById('visualization');
var options = {
  groupOrder: 'content',
  stack: false,
  stackSubgroups: true,
  orientation: {
    axis: 'both'
  },
  showCurrentTime: false
};

var timeline = new vis.Timeline(container);
timeline.setOptions(options);
timeline.setGroups(groups);
timeline.setItems(items);
#visualization {
  box-sizing: border-box;
  width: 100%;
  height: 300px;
}

.timeline-event-crash {
  background-color: red !important;
  border-color: darkred !important;
  color: white !important;
  font-family: monospace;
  box-shadow: 0 0 10px gray;

}

.timeline-tc-crash {
  color: red !important;
  border-color: red !important;
  background-color: #F4BBB5 !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.3.10/seedrandom.min.js"></script>
<link href="https://visjs.github.io/vis-timeline/styles/vis-timeline-graph2d.min.css" rel="stylesheet" />
<script src="https://visjs.github.io/vis-timeline/standalone/umd/vis-timeline-graph2d.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

<html>

  <head>
    <title>Timeline</title>
  </head>

  <body>
    <div id="visualization"></div>
  </body>

</html>

1 Answers

I don't know if you ever tried this, but when i am investigating the issue, when my mouse is over the left column i was able to scroll vertically. So not a fix from code side, but if you could not find a proper solution maybe you can add some guidance for your users like "if you want to see groups down below you should scroll when the mouse is over them".

P.S Though i did not deeply investigated this yet but i think this is an expected behavior from vis side, because as far as i have seen from their examples they do not have any option to horizontal scroll when mouse is over timeline

Related