Using JavaScript for Making a Goal Timeline With vis.js

October 18, 2016

Using JavaScript for Version Controlled Goals With vis.js

Picture of timeline using visjs

I’ve tried different ways of writing down goals over the years.

Here’s a list of the things I’ve tried:

  • A good old-fashioned handwritten list (which I quickly ditched in favor of more computer based solutions)
  • A list in Evernote
  • An Excel sheet

In all these methods, I’ve always been missing a visual way to look at how I’m doing on my goals over time.

A Goal Timeline

I read an interesting article on medium by someone who apparently started 6 companies about How To 3x Your Productivity in 10 Minutes a Week.

There’s a lot of the standard stuff about setting goals in specific areas of your life (family, career, etc.) but the most interesting thing was he used a timeline to showcase all of his goals with their respective end dates.

Here’s the image from the article I’m referencing (items are blocked out for the original author’s privacy)

Enter vis.js and its timeline feature

Being a programmer, I wanted to be able to version control my goals. I figured there must be some JavaScript library that could help me do this. After hitting Google for a few minutes, I eventually stumbled across vis.js and its timeline library.

Step 1 – Construct a basic HTML template

I’m going to give you the skeleton code I used at the end of this post, so don’t worry if you’re not getting this all at once. First, setup a standard basic HTML template.

Step 2 – Link to the css and js libraries via a CDN

Next, I just linked to 2 files – one was the CSS and the other was the JavaScript library provided by vis.js.

Step 3 – Add the code you need by looking at the documentation

The timeline documentation is pretty good in my opinion. So it’s pretty easy to get started with a basic timeline.

Skeleton code sample

Below is the source code I used (it’s one giant HTML page) sanitized for privacy reasons. But you can see the different CSS styles I used to color the timeline bars differently.

<!DOCTYPE html>
<html>
  <head>
    <title>Timeline basic demo</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.16.1/vis.min.js"></script>
    <link
      href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.16.1/vis.min.css"
      rel="stylesheet"
      type="text/css"
    />

    <style type="text/css">
      body,
      html {
        font-family: sans-serif;
      }
      .vis-item.green {
        background-color: greenyellow;
        border-color: green;
      }
      .vis-item.firebrick {
        background-color: firebrick;
        border-color: darkred;
        color: white;
      }
      .vis-item.orange {
        background-color: orange;
        border-color: darkorange;
      }
      .vis-item.crimson {
        background-color: crimson;
        border-color: purple;
      }
      .vis-item.blue {
        background-color: darkturquoise;
        border-color: blue;
      }
      .vis-item.gold {
        background-color: gold;
        border-color: blue;
      }
      .vis-item.mediumpurple {
        background-color: mediumpurple;
        border-color: blue;
        color: white;
      }
      .vis-item.done {
        background-color: limegreen;
        border-color: black;
      }
    </style>
  </head>
  <body>
    <div id="visualization"></div>

    <script type="text/javascript">
      //create goal data set with groups
      var container = document.getElementById('visualization');
      var groupNames = [
        'Relationships',
        'Financial',
        'Career',
        'Business',
        'Fun',
        'Spiritual',
        'Health',
      ];
      var groupStyles = [
        'crimson',
        'green',
        'blue',
        'mediumpurple',
        'orange',
        'gold',
        'firebrick',
      ];
      var groups = new vis.DataSet();
      for (var g = 0; g < groupNames.length; g++) {
        groups.add({id: g, content: groupNames[g]});
      }

      // create a dataset with items
      var items = new vis.DataSet();
      // date months are 0-based index
      var start = new Date(2016, 0, 1);
      var end = new Date(2016, 11, 31);
      var group = 0;
      var i = 0;
      // relationships
      items.add({
        id: i++,
        group: group,
        content: 'Goal 1 (' + groupNames[group] + ')',
        className: groupStyles[group++],
        start: start,
        end: end,
        type: 'range',
      });
      // financial
      items.add({
        id: i++,
        group: group,
        content: 'Goal 2 (' + groupNames[group] + ')',
        className: groupStyles[group],
        start: start,
        end: end,
        type: 'range',
      });
      items.add({
        id: i++,
        group: group,
        content: 'Goal 3 (' + groupNames[group] + ')',
        className: groupStyles[group++],
        start: start,
        end: end,
        type: 'range',
      });
      // career
      items.add({
        id: i++,
        group: group,
        content: 'Goal 4 (' + groupNames[group] + ')',
        className: groupStyles[group],
        start: start,
        end: end,
        type: 'range',
      });
      items.add({
        id: i++,
        group: group,
        content: 'Goal 5 (' + groupNames[group] + ')',
        className: groupStyles[group],
        start: start,
        end: end,
        type: 'range',
      });
      items.add({
        id: i++,
        group: group,
        content: 'Goal 6 (' + groupNames[group] + ')',
        className: groupStyles[group],
        start: start,
        end: end,
        type: 'range',
      });
      items.add({
        id: i++,
        group: group,
        content: 'Goal 7 (' + groupNames[group] + ')',
        className: groupStyles[group],
        start: start,
        end: end,
        type: 'range',
      });
      items.add({
        id: i++,
        group: group,
        content: 'Goal 8 (' + groupNames[group] + ')',
        className: 'done',
        start: start,
        end: end,
        type: 'range',
      });
      items.add({
        id: i++,
        group: group,
        content: 'Goal 9 (' + groupNames[group] + ')',
        className: groupStyles[group++],
        start: start,
        end: end,
        type: 'range',
      });
      // business
      items.add({
        id: i++,
        group: group,
        content: 'Goal 10 (' + groupNames[group] + ')',
        className: groupStyles[group],
        start: start,
        end: end,
        type: 'range',
      });
      items.add({
        id: i++,
        group: group,
        content: 'Goal 11 (' + groupNames[group] + ')',
        className: groupStyles[group++],
        start: start,
        end: end,
        type: 'range',
      });
      // fun
      items.add({
        id: i++,
        group: group,
        content: 'Goal 12 (' + groupNames[group] + ')',
        className: groupStyles[group],
        start: start,
        end: end,
        type: 'range',
      });
      items.add({
        id: i++,
        group: group,
        content: 'Goal 13 (' + groupNames[group] + ')',
        className: groupStyles[group++],
        start: start,
        end: end,
        type: 'range',
      });
      // spiritual
      items.add({
        id: i++,
        group: group,
        content: 'Goal 14 (' + groupNames[group] + ')',
        className: groupStyles[group++],
        start: start,
        end: end,
        type: 'range',
      });
      // health
      items.add({
        id: i++,
        group: group,
        content: 'Goal 15 (' + groupNames[group] + ')',
        className: groupStyles[group],
        start: start,
        end: end,
        type: 'range',
      });
      items.add({
        id: i++,
        group: group,
        content: 'Goal 16 (' + groupNames[group] + ')',
        className: groupStyles[group++],
        start: start,
        end: end,
        type: 'range',
      });
      // create visualization
      var container = document.getElementById('visualization');
      var options = {
        groupOrder: 'content', // groupOrder can be a property name or a sorting function
      };

      var timeline = new vis.Timeline(container);
      timeline.setOptions(options);
      timeline.setGroups(groups);
      timeline.setItems(items);
    </script>
  </body>
</html>

Profile picture

Written by Bruce Park who lives and works in the USA building useful things. He is sometimes around on Twitter.