DEV Community

Cover image for Creating a Roman History Timeline Chart in JavaScript
andreykh for AnyChart

Posted on • Originally published at anychart.com

Creating a Roman History Timeline Chart in JavaScript

Ever caught yourself thinking about the Roman Empire more often than you’d like to admit? You’re not alone, and we’ve got just the thing for you! With the recent AnyChart JS Charts 8.13.0 release, we’ve enhanced our timeline chart for better support of historical data visualization.

We’d love to showcase this improvement in action. We’ve added a Roman History Timeline to our gallery of timeline chart examples, and we now invite you to discover how it was developed from scratch. In this in-depth JavaScript charting tutorial, we’ll cover everything from A to Z, meticulously guiding you through each step — from setting up the HTML environment to adding detailed data and customizing the visualization to make the chart both informative and engaging. The final result is an interactive timeline that you can proudly call your own Roman Empire.

So, whether you’re a history enthusiast caught up in the recent meme trend or a developer eager to expand your data visualization skills, follow along and learn how to create compelling historical timeline charts using JavaScript!

Table of Contents

Introduction

Visualizing historical data can be challenging, especially when dealing with extensive timelines and precise dates. With the latest improvements in the AnyChart JavaScript charting library, creating detailed and accurate timeline charts is more accessible than ever.

In this tutorial, we’ll build an interactive JS timeline for a web page, which illustrates significant periods and events in Roman history, complete with custom tooltips and convenient navigation. Take a glance below to see how the final result will look, and let’s begin! A preview of the interactive timeline chart of Roman history built along this JavaScript timeline charting tutorial

Step 1: Setting Up Environment

To get started with building our timeline, we need to set up a basic HTML structure for our webpage. This includes setting up the document, creating a container to display the chart, and linking the necessary resources.

1.1. Create Basic HTML Document

First, we need to create a basic HTML document that defines the structure of the page. This is the foundation of any webpage.

<!DOCTYPE html>
<html>
  <head>
    <title>Roman Civilization Timeline with All Data</title>
  </head>
  <body>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

1.2. Create Container for Chart

Next, we need a container where the timeline chart will be displayed. This can be a simple <div> element with a specific ID, placed in the <body> section:

<div id="container"></div>
Enter fullscreen mode Exit fullscreen mode

This element will act as the placeholder for our chart. We will reference it in the JavaScript code to generate the visualization.

1.3. Include All Necessary Resources

Now, it’s time to include the necessary resources for our web project. Let’s use a custom web font. For example, Cinzel from Google Fonts. Simply include the relevant links in the <head> section:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400..900&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap" rel="stylesheet">
Enter fullscreen mode Exit fullscreen mode

We also need to connect the AnyChart JavaScript library to bring in the charting functionality. Link the required modules similarly in the <head> section:

<script src="https://cdn.anychart.com/releases/8.13.0/js/anychart-core.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.13.0/js/anychart-timeline.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.13.0/js/anychart-ui.min.js"></script>
Enter fullscreen mode Exit fullscreen mode

These <script> tags include the core charting library, the dedicated timeline chart module, and the UI module to customize the user interface. Now, we are ready to place the JavaScript timeline charting code within the <script> tag in the <body> section:

<script>
  // The place for the JavaScript charting code
</script>
Enter fullscreen mode Exit fullscreen mode

1.4. Complete HTML Setup

At this point, here’s what the complete HTML structure looks like:

<!DOCTYPE html>
<html>
  <head>
    <title>Roman Empire Timeline</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400..900&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap" rel="stylesheet">
    <script src="https://cdn.anychart.com/releases/8.13.0/js/anychart-core.min.js"></script>
    <script src="https://cdn.anychart.com/releases/8.13.0/js/anychart-timeline.min.js"></script>
    <script src="https://cdn.anychart.com/releases/8.13.0/js/anychart-ui.min.js"></script>
  </head>
  <body>
    <div id="container"></div>
    <script>
      // The place for the JavaScript charting code
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Step 2. Adding Data

Now that we’ve set up the structure, it’s time to add data that will populate our timeline. There are multiple ways to do this. Here, within the JavaScript code of the chart, we’ll create a function called loadData() to add all the data as objects:

function loadData(){
  // The data comes here…
};
Enter fullscreen mode Exit fullscreen mode

The data to be visualized consists of two main types:

  • Periods: Time ranges representing significant durations, like reigns of emperors or historical eras.
  • Events: Specific historical moments, such as battles or important occurrences.

Let’s proceed by adding the data for each type.

2.1. Define Periods (Ranges)

Periods are represented as time ranges. Each one has:

  • name: The label or title, such as “Legendary” or “Emperors.”
  • direction: Defines the orientation on the timeline as either pointing "up" (up) or "down" (down).
  • ranges: An array of specific data ranges, each with a name or title (name) to identify the period, start (start) and end (end) dates that mark the time span, and additional attributes like images (img) and descriptions (description).

For example, here’s the code defining the period of “Legendary” kings:

var periods = [{}];
periods[0] = {};
periods[0].name = "Legendary";
periods[0].direction = "up";
periods[0].ranges = [
  {
    name: "Romulus", 
    start: new Date(Date.UTC(-753, 0, 0)), 
    end: new Date(Date.UTC(-717, 0, 0)),
    img: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Brogi%2C_Carlo_%281850-1925%29_-_n._8226_-_Certosa_di_Pavia_-_Medaglione_sullo_zoccolo_della_facciata.jpg/250px-Brogi%2C_Carlo_%281850-1925%29_-_n._8226_-_Certosa_di_Pavia_-_Medaglione_sullo_zoccolo_della_facciata.jpg",
    description: "The legendary founder and first king of Rome, who established the city's foundations."
  },
  // Other kings follow
];
Enter fullscreen mode Exit fullscreen mode

Add more periods in a similar manner:

// 1) Add periods (ranges):
var periods = [{}];

// legendaries' years of life
periods[0] = {};
periods[0].name = "Legendary";
periods[0].direction = "up";
periods[0].ranges = [
  {
    name: "Romulus", 
    start: new Date(Date.UTC(-753, 0, 0)), 
    end: new Date(Date.UTC(-717, 0, 0)),
    img: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Brogi%2C_Carlo_%281850-1925%29_-_n._8226_-_Certosa_di_Pavia_-_Medaglione_sullo_zoccolo_della_facciata.jpg/250px-Brogi%2C_Carlo_%281850-1925%29_-_n._8226_-_Certosa_di_Pavia_-_Medaglione_sullo_zoccolo_della_facciata.jpg",
    description: "The legendary founder and first king of Rome, who established the city's foundations."
  },

  // Other legendary kings follow

];

// consuls' years of rule
periods[1] = {};  
periods[1].name = "Consuls";
periods[1].direction = "up";
periods[1].ranges = [
  {
    name: "Consulate", 
    start: new Date(Date.UTC(-509, 0, 0)), 
    end: new Date(Date.UTC(-27, 0, 16)),
    img: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/98/Roman_SPQR_banner.svg/150px-Roman_SPQR_banner.svg.png",
    description: "Consuls were elected annually, and the position was often shared by two individuals, making it difficult to track the exact reign periods for all consuls over the centuries."
  }
];

// emperors' years of life
periods[2] = {};    
periods[2].name = "Emperors";
periods[2].direction = "up";
periods[2].ranges = [
  {
    name: "Augustus",
    start: new Date(Date.UTC(-27, 0, 16)),
    end: new Date("0014-08-19"),
    img: "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Augustus_of_Prima_Porta_%28inv._2290%29.jpg/250px-Augustus_of_Prima_Porta_%28inv._2290%29.jpg",
    description: "First Roman emperor, marking the transition from the Roman Republic to the Empire."
  },

  // Other emperors follow

];

// historical states' years of existence
periods[3] = {};
periods[3].name = "Historical states";
periods[3].direction = "down";
periods[3].ranges = [
  {
    x: "Roman Kingdom", 
    start: new Date(Date.UTC(-753)), 
    end: new Date(Date.UTC(-509)),
    img: "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/Platner_-_Ancient_Rome_city_growth.jpg/375px-Platner_-_Ancient_Rome_city_growth.jpg",
    description: "The Roman Kingdom is the earliest period of Roman history, traditionally dated from the founding of Rome in 753 BC to the overthrow of the last king in 509 BC, marking the transition from a monarchy to a republic."
  },

  // Other states follow

];

Enter fullscreen mode Exit fullscreen mode

2.2. Define Events (Moments)

Events are individual moments that don't span long periods but occur on specific dates. Each one has:

  • name: The event’s title (e.g., "Sack of Rome by Gauls").
  • direction: Orientation on the timeline (up or down).
  • moments: An array of events that include names (y), dates (x), and additional attributes like images (img) and descriptions (description).

For example, here's how to define wars and battles:

var events = [];
events[0] = {};  
events[0].name = "Wars and Battles";
events[0].direction = "up";
events[0].moments = [
  {
    y: "Sack of Rome by Gauls", 
    x: new Date(Date.UTC(-390, 0, 0)),
    img: "https://upload.wikimedia.org/wikipedia/commons/8/88/La_Bataille_de_l%27allia_-_G.Surand.jpg",
    description: "Gauls under Brennus sacked Rome after the Battle of the Allia, causing major devastation."
  },
  // Other wars and battles follow
];
Enter fullscreen mode Exit fullscreen mode

Add more events similarly:

// 2) Add moments (events):
var events = [];

// wars and battles
events[0] = {};  
events[0].name = "Wars and Battles";
events[0].direction = "up";
events[0].moments = [
  {
    y: "Sack of Rome by Gauls", 
    x: new Date(Date.UTC(-390, 0, 0)),
    img: "https://upload.wikimedia.org/wikipedia/commons/8/88/La_Bataille_de_l%27allia_-_G.Surand.jpg",
    description: "Gauls under Brennus sacked Rome after the Battle of the Allia, causing major devastation."
  },
  // Other wars and battles follow
];

// establishments
events[1] = {};  
events[1].name = "Establishments";
events[1].direction = "down";
events[1].moments = [
  {
    y: "Establishment of Rome", 
    x: new Date(Date.UTC(-753, 0, 0)),
    img: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Kapitolinische_Wölfin_Museum_Capitolini.jpg/330px-Kapitolinische_Wölfin_Museum_Capitolini.jpg",
    description: "Rome was founded, according to legend, by Romulus and Remus."
  },

  // Other establishments follow

];

// disasters
events[2] = {};  
events[2].name = "Disasters";
events[2].direction = "down";
events[2].moments = [
  {
    y: "Assassination of Julius Caesar", 
    x: new Date(Date.UTC(-44, 0, 0)),
    img: "https://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Vincenzo_Camuccini_-_La_morte_di_Cesare.jpg/375px-Vincenzo_Camuccini_-_La_morte_di_Cesare.jpg",
    description: "Caesar was killed by Roman senators to stop his increasing power."
  },

// Other disasters follow

];
Enter fullscreen mode Exit fullscreen mode

2.3. Complete Data Loading Function

Once all periods and events are defined, the loadData() function can be finalized, ensuring it returns both periods and events to populate the timeline chart:

// Function to add data for the timeline chart:
function loadData(){

  // 1) Add periods (ranges)
  // …

  // 2) Add events (moments)
  // …

  return {"periods": periods, "events": events}

};
Enter fullscreen mode Exit fullscreen mode

This loadData() function is crucial, as it prepares the dataset that will feed into the chart graphically representing the periods and events of Roman history. With this step complete, we’re now ready to configure the timeline visualization of this data.

Step 3. Creating Timeline Chart

Now, let’s initialize and generate the timeline chart with the data we’ve added.

3.1. Prepare Ground

As previously mentioned, all the JavaScript code responsible for chart creation should be inside the <script> tag within the <body> section of the HTML document. Additionally, to ensure all the main charting code runs only after the entire page has fully loaded with all external resources, wrap it within the anychart.onDocumentReady() function. This prevents errors related to accessing elements before they exist. Here’s the basic structure:

<body>
  <script>
    anychart.onDocumentReady(function () {
      // The place for the main JavaScript Timeline Chart code
    });
  </script>
</body>
Enter fullscreen mode Exit fullscreen mode

Then, declare a chart variable. In a typical case, it could be done within the anychart.onDocumentReady() function. However, since we will need to access it from other functions as well, we will declare it outside the function. The rest of the JavaScript code will be placed inside anychart.onDocumentReady(), unless specified otherwise.

<body>
  <script>
    // Create a chart variable here to be accessible from various places in the script
    var chart;
    // Ensure the main charting code runs after the page has loaded with all external resources
    anychart.onDocumentReady(function () {
      // Main JavaScript Timeline Chart code goes here
    });
    // Other functions go here
  </script>
</body>
Enter fullscreen mode Exit fullscreen mode

3.2. Set Up Chart

Now, we need to set up the timeline chart. To create this type of data visualization, use the anychart.timeline() method. This initializes the chart object, which will be later configured with data and styling options.

chart = anychart.timeline();
Enter fullscreen mode Exit fullscreen mode

To make sure the chart displays data within the right time frame, set the minimum and maximum dates for the timeline's scale. This helps display all periods and events properly, from early Roman history to later stages.

chart.scale().minimum(Date.UTC(-1000));
chart.scale().maximum(Date.UTC(1500));
Enter fullscreen mode Exit fullscreen mode

These two lines define the visible range of the timeline from 1000 BC to 1500 AD.

3.3. Set Data to Chart

It’s time to populate the timeline with the data we’ve structured in the loadData() function in Step 2. Recall that this data includes periods (ranges) and events (moments).

3.3.1. Load Data

First, retrieve the structured data by calling loadData():

var data = loadData();
Enter fullscreen mode Exit fullscreen mode

Now, we’ll set up the data series.

3.3.2. Set Series for Periods (Ranges)

To display the periods, i.e. a range series, use the range() method. The data for periods is in the periods array, and we will loop through it to set each period in the chart. Here’s how to loop through the periods and bind them to the chart:

// 1) Set the range series for periods (e.g., rulers, empires)
for (let i = 0; i < data.periods.length; i++) {
  var period = data.periods[i];
  var range = chart.range(period.ranges);
  range.name(period.name);
  range.direction(period.direction);    
  range.labels().fontFamily("Cinzel");
};
Enter fullscreen mode Exit fullscreen mode

3.3.3. Set Series for Events (Moments)

For events, use the moment() method. Similarly, we’ll loop through the events array and create a moment series for each set of events.

// 2) Set the moment series for events (e.g., battles, establishment of states)
for (let i = 0; i < data.events.length; i++) {
  var event = data.events[i];
  var moment = chart.moment(event.moments);
  moment.name(event.name);
  moment.direction(event.direction);
  moment.labels().fontFamily("Cinzel");
};
Enter fullscreen mode Exit fullscreen mode

3.4. Customize Tooltips

Tooltips enhance user experience by providing additional details when users hover over periods and events in the timeline. Both the period (range) and event (moment) series support rich HTML content, allowing you to display descriptions, images, and other information in a user-friendly format. Let’s customize the tooltips.

3.4.1. Configure Period Tooltips

For example, here’s how you can customize the tooltip for the range series (periods). First, create a tooltip variable:

var tooltip = range.tooltip();
Enter fullscreen mode Exit fullscreen mode

Second, enable HTML content in the tooltip:

tooltip.useHtml(true);
Enter fullscreen mode Exit fullscreen mode

Third, format the tooltip to include all the information that needs to be displayed, in the desired way:

tooltip.format(function(){
  var img = this.getData("img");
  var description = this.getData("description");
  return "<center><img src='" + img + "'/></center><p style='max-width: 250px;'>" + description + "</p>";
});
Enter fullscreen mode Exit fullscreen mode

Fourth, if you don’t want to have the line separating the tooltip header from the tooltip content, simply remove it:

tooltip.separator(false);

Enter fullscreen mode Exit fullscreen mode

Finally, it’s possible to customize the tooltip header separately:

// enable HTML in the tooltip header
tooltip.title().useHtml(true);
// format the tooltip header
tooltip.titleFormat(function(){
  // use the anychart.format.dateTime() method to format the dates      
  var start = anychart.format.dateTime(this.start, "y G");
  var end = anychart.format.dateTime( this.end, "y G");
  return "<p>" + this.x + " (" + start + "" + end + ")</p>";
});
Enter fullscreen mode Exit fullscreen mode

Here’s how the entire tooltip customization code looks in this case:

// create a variable
var tooltip = range.tooltip();
// enable HTML in the tooltip
tooltip.useHtml(true);
// format the tooltip content
tooltip.format(function(){
  var img = this.getData("img");
  var description = this.getData("description");
  return "<center><img src='" + img + "'/></center><p style='max-width: 250px;'>" + description + "</p>";
});
// disable the tooltip separator
tooltip.separator(false);
// enable HTML in the tooltip header
tooltip.title().useHtml(true);
// format the tooltip header
tooltip.titleFormat(function(){
  // use the anychart.format.dateTime() method to format the dates      
  var start = anychart.format.dateTime(this.start, "y G");
  var end = anychart.format.dateTime( this.end, "y G");
  return "<p>" + this.x + " (" + start + "" + end + ")</p>";
});
Enter fullscreen mode Exit fullscreen mode

3.4.2. Configure Event Tooltips

Similarly, customization can be applied to the tooltip of the moment series (events):

// create a variable
var tooltip = moment.tooltip();
// enable HTML in the tooltip
tooltip.useHtml(true);
// format the tooltip content
tooltip.format(function(){
  var img = this.getData("img");
  var description = this.getData("description");
  return "<center><img src='" + img + "'/></center><p style= 'max-width: 250px;'>" + description + "</p>";
});
// disable the tooltip separator
tooltip.separator(false);
// enable HTML in the tooltip header
tooltip.title().useHtml(true);
// format the tooltip header
tooltip.titleFormat(function(){
  var x = new Date(this.x);
  // use the anychart.format.dateTime() method for date formatting
  var formatted = anychart.format.dateTime(x, "y G");
  return "<p>" + this.value + ", " + formatted + "</p>";
});
Enter fullscreen mode Exit fullscreen mode

3.4.3. Complete Tooltip Customization

Finally, let’s combine everything within the series configuration functions to ensure proper functionality in the current setting.

// 1) Set the range series for periods (e.g., rulers, empires)
for (let i = 0; i < data.periods.length; i++) {
  var period = data.periods[i];
  var range = chart.range(period.ranges);
  range.name(period.name);
  range.direction(period.direction);    
  range.labels().fontFamily("Cinzel");
  // Customize the range series tooltip:
  var tooltip = range.tooltip();
  // enable HTML in the tooltip
  tooltip.useHtml(true);
  // format the tooltip content
  tooltip.format(function(){
    var img = this.getData("img");
    var description = this.getData("description");
    return "<center><img src='" + img + "'/></center><p style='max-width: 250px;'>" + description + "</p>";
  });
  // disable the tooltip separator
  tooltip.separator(false);
  // enable HTML in the tooltip header
  tooltip.title().useHtml(true);
  // format the tooltip header
  tooltip.titleFormat(function(){
    // use the anychart.format.dateTime() method to format the dates      
    var start = anychart.format.dateTime(this.start, "y G");
    var end = anychart.format.dateTime( this.end, "y G");
    return "<p>" + this.x + " (" + start + "" + end + ")</p>";
  });
};

// 2) Set the moment series for events (e.g., battles, establishment of states)
for (let i = 0; i < data.events.length; i++) {
  var event = data.events[i];
  var moment = chart.moment(event.moments);
  moment.name(event.name);
  moment.direction(event.direction);
  moment.labels().fontFamily("Cinzel");
  // Configure the moment series tooltip:
  var tooltip = moment.tooltip();
  // enable HTML in the tooltip
  tooltip.useHtml(true);
  // format the tooltip content
  tooltip.format(function(){
    var img = this.getData("img");
    var description = this.getData("description");
    return "<center><img src='" + img + "'/></center><p style= 'max-width: 250px;'>" + description + "</p>";
  });
  // disable the tooltip separator
  tooltip.separator(false);
  // enable HTML in the tooltip header
  tooltip.title().useHtml(true);
  // format the tooltip header
  tooltip.titleFormat(function(){
    var x = new Date(this.x);
    // use the anychart.format.dateTime() method for date formatting
    var formatted = anychart.format.dateTime(x, "y G");
    return "<p>" + this.value + ", " + formatted + "</p>";
  });
};
Enter fullscreen mode Exit fullscreen mode

3.5. Enhance Navigation

Navigation plays a critical role in timeline charts, especially when working with extensive historical datasets. By default, you can use the mouse to navigate timeline charts: move along the timeline by dragging, and zoom in and out using the mouse wheel. To enrich this behavior and make the visualization even more navigable, let’s add a scrollbar element (scroller), zoom-on-click functionality (to zoom in on a moment or range), and zoom control buttons (to easily focus on specific eras in the timeline of Roman history).

3.5.1. Add Scrollbar

Adding a scrollbar allows users to navigate through the timeline easily. It can be enabled with a single line of code:

chart.scroller(true);
Enter fullscreen mode Exit fullscreen mode

The scroller will appear below the timeline.

3.5.2. Set Up Zoom Interactivity

To make the timeline more interactive, let’s add an event listener that zooms into a specific period or event when clicked. Here’s how. First, add an event listener that triggers when a point (either a period or event) is clicked:

chart.listen("pointClick", function(e){
  // Configure the chart listener here
};
Enter fullscreen mode Exit fullscreen mode

Second, declare variables that will define the area for zooming:

var start, end, gap;
Enter fullscreen mode Exit fullscreen mode

Third, determine if the clicked element is a moment or a period:

var type = e.series.getType();
Enter fullscreen mode Exit fullscreen mode

Fourth, configure the behavior for moments. We’ll zoom in on them with a 50-year margin before and after:

if (type == "moment") {
  var momentDate =  e.point.get("x");
  start = momentDate;
  end = momentDate;
  // 50 years in milliseconds
  gap = 1577880000000;
}
Enter fullscreen mode Exit fullscreen mode

Fifth, specify the desired behavior for non-moment elements, i.e. periods. We’ll zoom into them with a 10% margin before and after.

else {
  start = e.point.get("start");
  end = e.point.get("end");
  gap = (end - start)/10;
}
Enter fullscreen mode Exit fullscreen mode

Now that all prerequisites have been set up, use the zoomTo() method to finalize the configuration of the zooming functionality within the event listener.

chart.zoomTo(start - gap, end + gap);
Enter fullscreen mode Exit fullscreen mode

Check out the entire code that configures this zoom interactivity within the event listener:

// Add an event listener to zoom into an element when it's clicked:
chart.listen("pointClick", function(e){
  var start, end, gap;
  // check if the clicked element is a moment or a period
  var type = e.series.getType();
  // configure the behavior for moments
  if (type == "moment") {
    var momentDate =  e.point.get("x");
    // zoom into the moment, showing 50 years before and after it
    start = momentDate;
    end = momentDate;
    // 50 years in milliseconds
    gap = 1577880000000;
  } else { // for periods (non-moment elements)
    // zoom into the period, showing 10% of its length before its start and after its end
    start = e.point.get("start");
    end = e.point.get("end");
    gap = (end - start)/10;
  };
  // zoom into the element when it's clicked
  chart.zoomTo(start - gap, end + gap);    
});
Enter fullscreen mode Exit fullscreen mode

Importantly, since we're working with years and centuries (without the need for quarters, months, and so on), we should patch the zoom levels on the timeline scale. Configure this using a new function placed outside of anychart.onDocumentReady().

// Function to patch the zoom levels on the timeline scale
function patchedZoomLevels(){
  var customZoomLevels = [
    [
      {"unit": "month", "count": 1},
      {"unit": "quarter", "count": 1},
      {"unit": "year", "count": 1}
    ],
    [
      {"unit": "quarter", "count": 1},
      {"unit": "year", "count": 1},
      {"unit": "year", "count": 10}
    ],
    [
      {"unit": "year", "count": 1},
      {"unit": "year", "count": 10},
      {"unit": "year", "count": 50}
    ],
    [
      {"unit": "year", "count": 10},
      {"unit": "year", "count": 50},
      {"unit": "year", "count": 100}
    ],
    [
      {"unit": "year", "count": 50},
      {"unit": "year", "count": 100},
      {"unit": "year", "count": 500}
    ]    
  ];
  return customZoomLevels;  
};
Enter fullscreen mode Exit fullscreen mode

Once the custom zoom levels are defined, call this function inside the anychart.onDocumentReady() block. This ensures the zoom levels are patched before the chart is drawn.

chart.scale().zoomLevels(patchedZoomLevels());
Enter fullscreen mode Exit fullscreen mode

One more important thing: don’t forget to disable the selection functionality to ensure click tracking works correctly for zooming.

chart.interactivity().selectionMode("none");
Enter fullscreen mode Exit fullscreen mode

3.5.3. Add Zoom Control Buttons

Next, let’s add buttons to facilitate zooming into key periods of Roman history: the Kingdom, Republic, Empire, and Two Empires. First, set up the period buttons in the <body> section of the HTML page.

<button id="period-button" onclick="zoomTo('kingdom')">Roman Kingdom</button>
<button id="period-button" onclick="zoomTo('republic')">Roman Republic</button>
<button id="period-button" onclick="zoomTo('empire')">Roman Empire</button>
<button id="period-button" onclick="zoomTo('empires')">Two Empires</button>
Enter fullscreen mode Exit fullscreen mode

Then, write JavaScript to make the period buttons functional, using zoomTo(period) to implement zooming into the target periods when buttons are clicked.

function zoomTo(period) {
  switch (period) {
    case "kingdom":
      chart.zoomTo(Date.UTC(-755), Date.UTC(-505));
      break
    case "republic":
      chart.zoomTo(Date.UTC(-515), Date.UTC(-20));
      break
    case "empire":
      chart.zoomTo(Date.UTC(-40), Date.UTC(410));
      break
    case "empires":
      chart.zoomTo(Date.UTC(380), Date.UTC(1460));
      break
  } 
};
Enter fullscreen mode Exit fullscreen mode

Let’s also add a button to reset the zoom level to fit the entire timeline within the chart, using zoomOut() with the chart.fit() method.

<button id="zoom-out" onclick="zoomOut()">Zoom out</button>
Enter fullscreen mode Exit fullscreen mode
function zoomOut() {
  chart.fit();  
};
Enter fullscreen mode Exit fullscreen mode

3.6. Add Era Designators

In this step, we’ll introduce era designators into our timeline chart. They will be appended to the years to indicate the era. AnyChart uses locales to manage date and time formats, including era designations such as “BC” (Before Christ) and “AD” (Anno Domini) or “BCE” (Before Common Era) and “CE” (Common Era), depending on the locale. You can modify the locale’s format properties to include era designators through a custom function placed outside the anychart.onDocumentReady() function.

function patchDateTimeLocale(){
  // read the current output locale and create a structured clone of it
  var currentOutputLocale =  structuredClone(anychart.format.locales[anychart.format.outputLocale()]);
  // read the formats object to prepare for creating a custom version of the locale
  var dateTimeLocaleFormats = currentOutputLocale.dateTimeLocale.formats;
  // loop through all available format keys to find those related to the current scale
  for (var format in dateTimeLocaleFormats){
    // "timeline_" is the scale we are looking for
    if (format.startsWith("timeline_")){
      // read and loop through an array of format values to insert an era designator
      var formatValues = dateTimeLocaleFormats[format];
      for (var i = 0; i < formatValues.length; i++) {
        // formats are usually represented by strings like "yyyy" or "dd MMMM yy HH:mm.SSS"
        var formatValue = formatValues[i];
        // there are two possible configurations of year representation inside a locale string "yyyy" and "yy"
        // we need to find both versions or neither to correctly modify the locale string
        var index4Y = formatValue.search(/y{4}/g)
        var index2Y = formatValue.search(/y{2}/g)
        // if there is a string representing a "yyyy" year, modify the format to include an era designator
        if (index4Y >= 0) {
          /*
          Create a new string that includes everything before and after the index identified earlier.
          The new string will include an era designator, but ensure the indexes are adjusted correctly,
          as we want to insert the era designator after the part of the string that represents the year.
          */
          var formatValuePatched1 = formatValue.slice(0, index4Y) + "y G" + formatValue.slice(index4Y + 4);
          formatValues[i] = formatValuePatched1;
        // if there is a string representing a "yy" year, modify the format to include an era designator
        } else if (index2Y > -1) {
          var formatValuePatched2 = formatValue.slice(0, index2Y + 1) + "y G" + formatValue.slice(index2Y + 3)
          formatValues[i] = formatValuePatched2
        }
      }
    }
  }
  // add the custom current output locale to AnyChart
  anychart.format.locales["custom"] = currentOutputLocale;
  // set the custom locale as the active locale
  anychart.format.outputLocale("custom");
};
Enter fullscreen mode Exit fullscreen mode

Once the locale patching function is set, call it in the anychart.onDocumentReady() block. Make sure to put it before rendering the chart, so the timeline properly displays the era designators.

patchDateTimeLocale();
Enter fullscreen mode Exit fullscreen mode

3.7. Set Font for Axis Labels

We’ve already applied the Cinzel font to series labels and tooltips. To maintain consistent typography throughout the timeline, we'll also set the axis label font to match the overall design. Here’s how to set it:

chart.axis().labels().fontFamily("Cinzel");
Enter fullscreen mode Exit fullscreen mode

3.8. Set Chart Title

A title helps users immediately understand what the chart represents. We can easily add a title using the chart.title() method:

chart.title({
  text: "Roman Civilization",
  fontFamily: "Cinzel",
  fontSize: 32
});
Enter fullscreen mode Exit fullscreen mode

3.9. Set Container and Draw Chart

To display the chart, we need to link it to a container and then render it. The container is the <div> element with id="container" that we created in the HTML.

chart.container("container");
Enter fullscreen mode Exit fullscreen mode

Finally, draw the chart.

chart.draw();
Enter fullscreen mode Exit fullscreen mode

This command renders the entire timeline chart in the specified container on the page.

3.10. Complete JavaScript Timeline Code

Feel free to view the complete JavaScript code for the timeline chart on AnyChart Playground. There, you can also download the entire project as an HTML file or ZIP archive.

Step 4. CSS Styling

Before viewing the final result, remember that CSS is essential for ensuring our page appears exactly how we want it. Let’s apply CSS to style the entire web timeline visualization, making the project look clean, organized, and visually appealing.

4.1. Style HTML, Body, Container

The following CSS ensures that the webpage and the chart container take up the full screen while maintaining a consistent layout. The container is positioned absolutely to cover the available space, ensuring the chart adjusts to fit the window properly.

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

#container {
  position: absolute;
  min-width: 1000px;
  width: 100%;
  top: 40px;
  bottom: 0;
}
Enter fullscreen mode Exit fullscreen mode

4.2. Style Zoom Control Buttons

The following CSS styles the zoom control buttons, which are used to zoom in (and out) on specific periods of Roman history. The buttons are styled to look consistent with the Roman theme, using the “Cinzel” font and a modern blue shade for hover effects.

#period-button, #zoom-out {
  background-color: #bad5f1;
  border: none;
  padding: 5px 10px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  font-family: "Cinzel", serif;
  margin-top: 5px;
  margin-left: 5px;
}

#period-button:hover, #zoom-out:hover {
  color: #fff;
  background-color: #1976d2;
}

#zoom-out {
  position: absolute;
  right: 5px;
}
Enter fullscreen mode Exit fullscreen mode

4.3. Style Tooltips

To make the tooltip content, including images and text, more visually appealing and legible, we apply styles to the images and paragraphs that appear inside the tooltips.

img {
  max-width: 300px;
  max-height: 300px;
}

p {
  font-family: "Cinzel", serif;
  margin-top: 1px;
  margin-bottom: 1px;
}
Enter fullscreen mode Exit fullscreen mode

4.4. Complete CSS Code

Here’s the complete CSS code that styles the container, buttons, and tooltip content.

html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

#container {
  position: absolute;
  min-width: 1000px;
  width: 100%;
  top: 40px;
  bottom: 0;
}

#period-button, #zoom-out {
  background-color: #bad5f1;
  border: none;
  padding: 5px 10px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  font-family: "Cinzel", serif;
  margin-top: 5px;
  margin-left: 5px;
}

#period-button:hover, #zoom-out:hover {
  color: #fff;
  background-color: #1976d2;
}

#zoom-out {
  position: absolute;
  right: 5px;
}

img {
  max-width: 300px;
  max-height: 300px;
}

p {
  font-family: "Cinzel", serif;
  margin-top: 1px;
  margin-bottom: 1px;
}
Enter fullscreen mode Exit fullscreen mode

Result: Timeline Chart of Roman History

After following all the steps, including setting up the environment, adding data, configuring the chart, and applying styling, we now have a fully functional, interactive Timeline Chart of Roman Civilization with zoom control, custom tooltips, and a visually consistent design. The timeline displays historical periods and events with detailed information, providing an immersive experience.

Check it out on AnyChart Playground with the full source code, where you can also tweak the settings, modify the data, and experiment with new features.

Final JavaScript Timeline Chart of Roman Civilization History

Wrapping Up

Congratulations! We’ve successfully created a detailed, interactive timeline chart of Roman history using JavaScript. We’ve walked through everything from setting up the HTML structure, data, tooltips, zoom controls, and more. Hopefully, the steps were clear, and you’re now ready to build your own timeline visualizations.

Explore more examples in our timeline chart gallery and dive into the timeline chart documentation to discover other customization options and possibilities.

Happy JavaScript timeline charting!

Top comments (1)

Collapse
 
andreykh profile image
andreykh

Quick links to the Roman History Timeline: