Can anyone help me recreate the stacked bar chart seen in Apple Health's Cardio Fitness article using chart.js and Bootstrap tabs?

On Apple Health, there is a fascinating article titled "Learn About Cardio Fitness" (located in the Browse tab at the bottom > Activity, then scroll down). While reading the article, I came across a chart that caught my eye, and I am eager to replicate it in HTML.

Here is the chart that I am referring to...

Here are the questions I have:

  • After exploring the official Chart.js documentation, I noticed a "Randomize" button that can smoothly switch between data. How can I incorporate Bootstrap tabs to achieve a similar data-switching effect like the one in the Apple Health article?
  • How can I include number labels in the bars and disable the hover feature?
  • Bonus: I attempted to use the code from this Stack Overflow post to rotate the "VO2 max" label horizontally, but it didn't work, possibly due to using chart.js 4.3.0. Any suggestions?

Below is the code and data I have assembled so far (I completely forgot to include the Male/Female/All tab *facepalm*):

function createChart(id, data) {
  const ctx = document.getElementById(id).getContext('2d');
  new Chart(ctx, {
    type: 'bar',
    data: {
      labels: ['20-29', '30-39', '40-49', '50-59', '60+'],
      datasets: data
    },
    options: {
      scales: {
        x: {
          stacked: true,
          title: {
            display: true,
            text: 'Age Ranges',
            align: 'start'
          }
        },
        y: {
          stacked: true,
          title: {
            display: true,
            text: 'VO2 max',
            align: 'end'
          },
          position: 'right'
        }
      },
      plugins: {
        legend: {
          display: false
        }
      }
    }
  });
}

const lowData = [{
  label: 'Low',
  data: {
    "20-29": [29, 38],
    "30-39": [27, 34],
    "40-49": [24, 31],
    "50-59": [21, 26],
    "60+": [17, 18]
  },
}];

... (data continues)

createChart('lowChart', lowData);
createChart('belowAverageChart', belowAverageData);
createChart('aboveAverageChart', aboveAverageData);
createChart('highChart', highData);


const triggerTabList = document.querySelectorAll('#chartTabs button')
triggerTabList.forEach(triggerEl => {
  const tabTrigger = new bootstrap.Tab(triggerEl)

  triggerEl.addEventListener('click', event => {
    event.preventDefault()
    tabTrigger.show()
  })
})
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="abc9c4c4dfd8dfd9cadbeb9e8598859b">[email protected]</a>/dist/css/bootstrap.min.css">
<ul class="nav nav-pills" id="chartTabs" role="tablist">
  <li class="nav-item" role="presentation">
    <button class="nav-link active" id="low-tab" data-bs-toggle="tab" data-bs-target="#low" type="button" role="tab" aria-controls="low" aria-selected="true">Low</button>
  </li>

... (HTML code continues)

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c0a2afafb4b3b4b2a1b080f5eef3eef0">[email protected]</a>/dist/js/bootstrap.bundle.min.js"></script>

Answer №1

In order to begin, it is important to ensure that the axes limits are consistent. For your data, I have set the maximum of the y-axis scale to 70. This value can be calculated from the data itself.

Additionally, you should disable animation by setting options.animation = false.

The tooltip box that appears on hover can be disabled by setting

options.plugins.tooltip.enable = false
.

If you wish to display the value statically, you can utilize the chart-plugin-datalabels. Refer to this example for more details.

By implementing these changes, your chart will look something like this:

 // JavaScript code for creating charts goes here 

I have observed a slight flickering effect when switching tabs for the first time. This is caused by Chart.js rendering the chart only when the tab becomes visible. To eliminate this issue, you can use an OffscreenCanvas or hidden charts. This modification will result in a smoother transition between tabs.

 // More JavaScript code for creating charts and handling tab switches goes here 

However, I believe a single chart with buttons is a safer and more versatile approach compared to multiple hidden charts. This method allows for smoother transitions and a seamless user experience, especially when resizing the window.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilize model instance methods in Sails.js for enhanced functionality within views

Is there a way to retain model instance functions when passed to a view? For example, my User model has a method called fullName which combines first name, last name, and prefix. In the controller: User.find().done(function(err,users){ res.view({ ...

Issue with Firebase Cloud function not terminating despite receiving a 204 response code

Currently, I am developing a cloud function to manage server operations for a gaming panel. Everything seems to be functioning correctly except that after the request is completed, it fails to trigger the expected "data", "end", or "closed" events which ...

Creating a dynamic `v-model` for computed properties that is dependent on the route parameter

I am creating a versatile component that can update different vuex properties based on the route parameter passed. Below is a simplified version of the code: <template> <div> <input v-model="this[$route.params.name]"/> </div&g ...

The debounce function seems to be malfunctioning as I attempt to refine search results by typing in the input field

Currently, I am attempting to implement a filter for my search results using debounce lodash. Although the filtering functionality is working, the debounce feature seems to be malfunctioning. Whenever I input text into the search bar, an API call is trigge ...

Combining two asynchronous requests in AJAX

In the process of customizing the form-edit-account.php template, I have added ajax requests to enhance the functionality of the account settings form. The form allows users to modify their name, surname, age, and other details. While the ajax implementati ...

What is the best way to find the index of the smallest value in an array using JavaScript?

I recently created this function that currently outputs -1. function sayHello() { let buildingLevelsArray = [11,10,10]; var smallestIndex = buildingLevelsArray.indexOf(Math.max(buildingLevelsArray)); console.log(smallestIndex); } sayHello() ...

Utilizing AJAX for remote execution of JavaScript code

Suppose there is a JavaScript page located at myaddress/service.js on a server. The code in this .js file looks something like: nsBob = { a: function(someParam) {...perform actions and return result}, b: function() {...perform actions and return result} ...

Is Angular 2 Really Suitable for Multi-Page Applications?

I am currently working on a multi-page app using Angular2 and have noticed that the load times are slower than desired when in development mode. While searching for solutions, I came across a thread on stackoverflow that explains how to set up Angular2 fo ...

Expanding choice by incorporating additional or default selections with ng-options

I am currently working on a tag-manager feature within an angular form that utilizes two dropdown menus (in this example, a food category and a specific item). The functionality I am aiming for is when a user selects a food category, the item dropdown shou ...

Refreshing JSON data every 10 seconds using SVG/D3

What is the best way to program a D3/json/ajax query that retrieves new data every 10 seconds? Here is my initial attempt at a solution, but I believe it may not be ideal: setInterval(function() { d3.json("http://1.....", function(json) { .... }) ...

The issue of Access-Control-Allow-Origin restriction arises specifically when using the gmap elevation API

My attempts to retrieve the elevation of a site using latitude and longitude have been unsuccessful due to an error I encountered while making an ajax call. Regardless of whether I use HTTP or HTTPS, I receive the following error message: "No 'Access ...

What could be the reason for jQuery not functioning properly as needed?

function toggleDisplayingRooms(nameSelect){ if(nameSelect){ firstroom = document.getElementById("firstroom").value; secondroom = document.getElementById("secondroom").value; thirdroom = ...

What is the purpose of using JSON.parse(decodeURIComponent(staticString))?

A specific approach is utilized by some dynamic web frameworks in the following code snippet <script> appSettings = JSON.parse( decodeURIComponent( "%7B%22setting1%22%3A%22foo%22%2C%22setting2%22%3A123%7D")); </script> Is there a part ...

AngularJS - Setting an initial delay for ng-bind

We have a span element with the following attributes: <span role="link" ng-show="showLink()" ng-bind="textLink"></span> (Just an fyi: we implemented a fade-in, fade-out animation for this link, hence the use of ng-show instead of ng-if) The ...

Having trouble accessing req.user on my Node.js server using Auth0 and Angular

Currently, I am utilizing auth0 for my admin panel's login system and it is functioning smoothly. However, I have encountered an issue in node where 'req.user' is returning as undefined for some unknown reason. This setup is fairly basic; I ...

Incorporating the id attribute into the FormControl element or its parent in Angular 7

I'm attempting to assign an id attribute to the first invalid form control upon form submission using Angular reactive forms. Here is my current component code: onSubmit() { if (this.form.invalid) { this.scrollToError(); } else { ...

Using the window.history.pushState function triggers a page reload every time it is activated

I've been attempting to navigate from page to page without the need for a reload. I came across suggestions that using window.history.pushState() could achieve this, however, it appears that the page is still reloading. Furthermore, an attempt ...

What could be causing the npm install command to not save packages in the /node_modules directory?

After running npm install to add a package, npm indicates that the installation was successful. However, upon checking the node_modules folder, I'm unable to locate the installed package. In order to access the package, I have resorted to using npm in ...

Retrieve a targeted data value from a JSON object based on an array value

Looking at the JSON array and another array provided below. JSON OBJECT { id: 1, name: 'abc', email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="dfbebdbc9fb8b2beb6b3f1bcb0b2">[emai ...

What is the best way to save a webpage when a user clicks a button before leaving the page with Javascript

I've exhausted all my options in trying to find a way to trigger a button that saves the page upon exit, but it never seems to work. I need the event to occur even if the button is not visible at the time of the page exit. The new security protocols o ...