Is it possible to add a computed value to the bar's end in Chart.JS?

I'm creating a bar graph that has two bars representing different weeks. Currently, it looks like this:

https://i.stack.imgur.com/6Avni.png

However, I want it to look like this:

https://i.stack.imgur.com/TAVqe.png

In order to achieve the desired result, I need to calculate the percentage difference between week 1 (top/green bar) and week 2 (bottom/orange bar), and display it at the end of the week 2 bars.

Currently, I am adding the values to the bars using the following code:

Chart.pluginService.register({
    afterDraw: function (chartInstance) {
        var ctx = chartInstance.chart.ctx;
        // render the value of the chart above the bar
        ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);
        ctx.textAlign = 'center';
        ctx.textBaseline = 'bottom';

        chartInstance.data.datasets.forEach(function (dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
                var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
                ctx.fillText(addCommas(dataset.data[i]), model.base + 20, model.y + 6);
            }
        });
    }
});

However, I don't know how to perform the calculation and append it to the end of the bars.

For the complete implementation of the chart, here is the rest of the jQuery code:

var ctxForecastChart = $("#forecastLineChart").get(0).getContext("2d");
var forecastChartData = {
    labels: [
        "Total Qty", "Total Sales"
    ],
    datasets: [
        {
            label: "9/18/2016",
            backgroundColor: "rgba(34,139,34,0.75)",
            hoverBackgroundColor: "rgba(34,139,34,1)",
            data: [100, 1000.00]
        },
        {
            label: "9/25/2016",
            backgroundColor: "rgba(255,153,51,0.75)",
            hoverBackgroundColor: "rgba(255,153,51,1)",
            data: [110, 1110.11]
        }
    ]
};

var optionsForecast = {
    tooltips: {
        enabled: true
    }
};

var forecastBarChart = new Chart(ctxForecastChart,
{
    type: 'horizontalBar',
    data: forecastChartData,
    options: optionsForecast
});

UPDATE

I have tried Tektiv's code and it seemed promising initially, but for some reason, it completely breaks my charts. When using my original code:

Chart.pluginService.register({
    afterDraw: function (chartInstance) {
        var ctx = chartInstance.chart.ctx;
        // render the value of the chart above the bar
        ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);
        ctx.textAlign = 'center';
        ctx.textBaseline = 'bottom';

        chartInstance.data.datasets.forEach(function (dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
                var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
                ctx.fillText(addCommas(dataset.data[i]), model.base + 20, model.y + 6);
            }
        });
    }
});

I see the following result:

https://i.stack.imgur.com/75dIB.png

But when I replace it with the new code provided:

Chart.pluginService.register({
    afterDraw: function (chartInstance) {
        var ctx = chartInstance.chart.ctx;
        ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);

        // `start` makes a better rendering IMO
        ctx.textAlign = 'start';
        ctx.textBaseline = 'bottom';
        ctx.fillStyle = '#666';

        chartInstance.data.datasets.forEach(function (dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
                var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
                ctx.fillText(dataset.data[i], model.base + 5, model.y + 6);

                if (i == 1) {
                    // Calculate percentage difference between week 1 and week 2

                    // If needed, access other dataset
                    var otherDataset = chartInstance.data.datasets[(dataset._meta[0].controller.index == 1) ? 0 : 1];

                    // Get the value to display - percentage difference
                    var value = Math.round((Math.abs(dataset.data[i] - otherDataset.data[i]) / dataset.data[i]) * 100);

                    // Display the calculated value at the end of the bar using the `x` property of the model
                    ctx.fillText(value + "%", model.x + 5, model.y + 6);
                }
            }
        });
    }
});

I see the following result:

https://i.stack.imgur.com/1w4Hs.png

I don't understand why this is happening as the new code seems fine to me.

UPDATE 2

The updated code is an improvement, but it still distorts the other charts:

https://i.stack.imgur.com/Zo5Tm.png

UPDATE 3

After further investigation, I realized that there is a conflict between multiple `onDraw()` events. There is another event handling the Price Compliance chart:

Chart.pluginService.register({
    afterDraw: function (chartInstance) {
        var ctx = chartInstance.chart.ctx;
        // render the value of the chart above the bar
        ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);
        ctx.textAlign = 'center';
        ctx.textBaseline = 'bottom';

        chartInstance.data.datasets.forEach(function (dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
                var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
                ctx.fillText(addCommas(dataset.data[i]), model.base + 20, model.y + 6);
                //ctx.fillText(dataset.data[i], model.base + 20, model.y + 6);
            }
        });
    }
});

Answer №1

As you performed with model.base while adding the information, implement the following :

Chart.pluginService.register({
    afterDraw: function(chartInstance) {
        var cnv = chartInstance.chart.canvas;
        var ctx = chartInstance.chart.ctx;
        ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);

        // The text is preferably aligned to the start
        ctx.textAlign = 'start';
        ctx.textBaseline = 'bottom';
        ctx.fillStyle = '#666';

        chartInstance.data.datasets.forEach(function(dataset) {
            for (var i = 0; i < dataset.data.length; i++) {
                var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
                ctx.fillText(dataset.data[i], model.base + 5, model.y + 6);

                if (dataset._meta[0].controller.index == 1) {
                    // Orange bar (2nd dataset) values here

                    // Fetch the other dataset
                    var otherDataset = chartInstance.data.datasets[(dataset._meta[0].controller.index == 1) ? 0 : 1];

                    // Calculate the percentage difference
                    var value = Math.round((Math.abs(dataset.data[i] - otherDataset.data[i]) / otherDataset.data[i]) * 100);

                    // Display it
                    ctx.fillText(value + "%", model.x + 5, model.y + 6);
                }
            }
        });
    }
});


You can observe the plugin in action in this jsFiddle, and this is a visualization of its appearance :

https://i.stack.imgur.com/fXMJh.png

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

When a link is clicked, submit a form and send it to several email addresses simultaneously

My form has been styled using the uniform jQuery plugin. Instead of using an input type submit for submitting the form, I have utilized a link and added some effects to it to prevent it from being formatted with uniform. <form> <ul> ...

What is the process for removing a registered user from Realm Object Server with the use of the Javascript library?

I have been searching online for a solution, but I haven't been able to find an answer. I am attempting to remove a user from ROS, however, I cannot locate a designated API for this task. The version of my realm-js is 1.10.3. If this feature does not ...

Transfer of part of a JSON object

My current setup involves an API in PHP that sends JSON data to a Javascript webpage for processing. However, when dealing with large datasets, it can strain both the user's internet connection and computer capabilities. To address this issue, I want ...

Issues occurring with setting the variable using the Google latlng API

I've tried searching for solutions on various forums, including stackoverflow, but haven't been able to make it work. The issue lies in this code snippet where the variable 'pos' is not being set: var geocoder= new google.maps.Geocoder ...

The HTML status code is 200, even though the JQuery ajax request shows a status code of 0

My issue is not related to cross site request problem, which is a common suggestion in search results for similar questions. When attempting to make an ajax request using jquery functions .get and .load, I'm receiving xhr.status 0 and xhr.statusText ...

step-by-step guide on transferring the text content of an HTML paragraph element to another HTML paragraph element through JavaScript in ASP.NET

I'm looking for help with passing the text value from one HTML paragraph element to another when a JavaScript function is called. The function loads a div element with an enlarged image and a paragraph content. Below is the code I am using: JavaScrip ...

Error message: "An issue occurred: Unable to access undefined properties (specifically, borderRadius) in MUI react."

I recently created a navigation bar with an integrated search bar component. The styling for my search component was done using MUI styled from @emotion/styled in MUI library, where I applied a borderRadius: theme.shape.borderRadius. However, I encountere ...

Leverage Angular's constant feature in scenarios that extend beyond the

Even though it may not be recommended, I find it fascinating to use Angular services outside of the angular framework. One example is having .constant('APIprefix','/api') I am curious about how to access the value of APIprefix outside ...

Discover the power of utilizing the reduce function to extract the value of a specific field within an array of Objects

In the following example, we have an object with 3 forms: formA, formB, and formC. Form A and B are objects, while formC is an array of objects that can contain multiple items. const object: { "formA": { "details": {}, ...

Make sure to always select the alternative option in ajax

I am trying to create a condition where if the value of id=type_investor is either 1 or 6, an email should be sent using a different controller. Here is my complete code: function (isConfirm) { if (!isConfirm) return; $.ajax({ ...

The anchor tag does not seem to be functioning properly within the fancybox feature

Whenever I click on the "LOGOUT" button, I would like to be automatically redirected to the logout page. <div id="emailverification" style="display:none;"> <div style="width: 100%;float:left;background-color: #E4E5EA;"> $(document).ready( ...

Combining JWT authentication with access control lists: a comprehensive guide

I have successfully integrated passport with a JWT strategy, and it is functioning well. My jwt-protected routes are structured like this... app.get('/thingThatRequiresLogin/:id', passport.authenticate('jwt', { session: false }), thing ...

An unexplained line break is being added to content loaded via ajax requests

When using either .ajax or .load to load content from an .html page and insert it into an element, I am experiencing a strange issue where a mysterious new line is appended after the content. It is not a break element, but more like a \n or \r ch ...

Incorporate the key as a prop within a Child Component in a React application

I am trying to display a list of elements in React, where the key of each element is used as an index in front of the item. However, when I try to access props.key, it just returns undefined. Does anyone have any suggestions on how to access the key proper ...

Exploring the interception of ajax http responses beyond the scope of AngularJS

Is there a way to capture and manage http responses from outside of Angular? $httpProvider is not an option since the target script loads after Angular has initialized. I need a solution that functions similar to ajaxSuccess in jQuery. ...

Eliminating unique phrases from text fields or content sections with the help of jQuery or JavaScript

I'm currently working on a PHP website and have been tasked with the responsibility of removing certain special strings such as phone numbers, email addresses, Facebook addresses, etc. from a textarea that users input data into. My goal is to be able ...

Retrieve the identification of elements with dynamically generated ids

I've dynamically generated a set of elements using Handlebars, as shown below {{#floor as |room i|}} <div class="btn-group-toggle" data-toggle="buttons" > <label class="btn btn-secon ...

IE causing incorrect values to be retrieved from .attr using jQuery

One of the javascript functions I am working on involves using jQuery's .attr method to extract an href value from my pagination link. Here is how the function is called: $(".pager").click(function(){ var start = $(this).attr('href'); ...

Strange glitch in the Trebuchet MS font

I am currently attempting to utilize the jquery.fullcalendar plugin with jQuery UI theming. Everything seems to be functioning properly, except for an issue where the user's selection of a time range (week or day view) is displaced by approximately on ...

Issue with OnClientClick functionality not functioning as expected

I am having trouble with the validation function that is supposed to be triggered when clicking on the back and next buttons in my code. For some reason, the OnClientClick validation function is not being called when I click on the buttons. Can anyone pro ...