Determining the percentage of a bar in d3.js when hovering over it

I am working with a d3 chart and looking to implement a tooltip feature that displays the label of the bar section along with the percentage it represents in relation to the total bar. Initially, I considered calculating the height of the hovered section to determine its percentage contribution to the whole bar. Here is my current approach within the mouseover event that logs data on hover:

d3.select(this).style("height")

While this method works as intended, I am struggling to find a way to retrieve the overall height of the entire bar (inclusive of all sections) to create my equation.

What would be the best way to proceed with this task? Alternatively, are there more efficient methods for obtaining the percentage value?

Answer №1

Each rectangle's data includes all the bars in its column within a data property:

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

The data also contains the top and bottom values for the rectangle itself: d[0] and d[1].

We can calculate a percentage using this information:

.on("mouseover", function(d) { 
   var rectHeight = d[1] - d[0]; // Represents the top and bottom values of the rectangle.
   var columnHeight = d3.sum(d3.keys(d.data),function(k) {
      return +d.data[k]; // Sum of all bar heights in that column.
  });
  var percentHeight = rectHeight/columnHeight*100;

The units for rect height and column height mentioned above are not SVG units, but correspond to the dataset's units since we access the data directly without scaling.

d3.sum disregards NaN or undefined values, while the unary plus operator converts a string to a number, returning NaN if conversion is not possible. If you have a numerical x attribute, it needs to be excluded from the sum.

This results in the following snippet:

<!DOCTYPE html>
<meta charset="utf-8">
<svg id="chart" width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var data = [
  {month: "January", toys: 16591, games: 1047, books: 0, crafts: 5757},
  {month: "February", toys: 42337, games: 129, books: 835, crafts: 0},
  {month: "March", toys: 3385, games: 1053, books: 6260, crafts: 10},
  {month: "April", toys: 353, games: 3724, books: 4038, crafts: 0}
];

var series = d3.stack()
    .keys(["toys", "games", "books", "crafts"])
    .offset(d3.stackOffsetDiverging)
    (data);

var svg = d3.select("svg"),
    margin = {top: 20, right: 30, bottom: 30, left: 60},
    width = +svg.attr("width"),
    height = +svg.attr("height");

var x = d3.scaleBand()
    .domain(data.map(function(d) { return d.month; }))
    .rangeRound([margin.left, width - margin.right])
    .padding(0.1);

var y = d3.scaleLinear()
    .domain([d3.min(series, stackMin), d3.max(series, stackMax)])
    .rangeRound([height - margin.bottom, margin.top]);

var z = d3.scaleOrdinal(d3.schemeCategory10);

var colors = ['#000','#000','#000','#000'];

svg.append("g")
  .selectAll("g")
  .data(series)
  .enter().append("g")
    .attr("fill", function(d) { return z(d.key); })
  .selectAll("rect")
  .data(function(d) { return d; })
  .enter().append("rect")
    .attr("width", x.bandwidth)
    .attr("x", function(d) { return x(d.data.month); })
    .attr("y", function(d) { return y(d[1]); })
    .attr("height", function(d) { return y(d[0]) - y(d[1]); })
    .on("mouseover", function(d){
        var rectHeight = d[1] - d[0];
        var columnHeight = d3.sum(d3.keys(d.data),function(k) {
           return +d.data[k];
        });
        console.log(Math.round(rectHeight/columnHeight*100)+"%");
    })

svg.append("g")
    .attr("transform", "translate(0," + y(0) + ")")
    .call(d3.axisBottom(x));

svg.append("g")
    .attr("transform", "translate(" + margin.left + ",0)")
    .call(d3.axisLeft(y));

function stackMin(serie) {
  return d3.min(serie, function(d) { return d[0]; });
}

function stackMax(serie) {
  return d3.max(serie, function(d) { return d[1]; });
}

</script>

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

Accessing the URL causes malfunctioning of the dynamic routing in Angular 2

I am currently working on implementing dynamic routing functionality in my Angular application. So far, I have successfully achieved the following functionalities: Addition of routing to an existing angular component based on user input Removal of routin ...

"What could be the reason for web3.eth.getAccounts() method returning an empty array when used with console.log

Upon executing web3.eth.getAccounts().then(console.log);, I encountered an empty array and also received a warning stating ./node_modules/web3-eth-accounts/src/scrypt.js Critical dependency: the request of a dependency is an expression. The project began w ...

When the clearOnBlur setting is set to false, Material UI Autocomplete will not

I recently encountered an issue in my project while using Material UI's Autocomplete feature. Despite setting the clearOnBlur property to false, the input field keeps getting cleared after losing focus. I need assistance in resolving this problem, an ...

Utilize external functions in evaluated code

After working with a TypeScript file containing the following code: import { functionTest } from './function_test' function runnerFunctionTest() { console.log("Test"); } export class Runner { run(source : string) { eva ...

When defining a stripe in TypeScript using process.env.STRIPE_SECRET_KEY, an error of "string | undefined" is encountered

Every time I attempt to create a new stripe object, I encounter the error message "Argument of type 'string | undefined' is not assignable to parameter of type 'string'. Type 'undefined' is not assignable to type 'string& ...

Warning: MaxListenersExceededNotification may occur during the installation or creation of Node.js projects on macOS

Encountering a warning message while attempting to set up or generate Node.js projects on macOS (darwin): (node:80101) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 close listeners added to [TLSSocket]. Use emitter.setMaxList ...

Enhance the impact of "dialogs" to the fullest extent or minimize it to achieve the

How can I position the minimized dialog on the bottom of the div when appending dialogs next to each other in a FB messaging system? To achieve a Facebook messaging effect, the titlebar must go down when minimizing/maximizing the dialog. Perform the follo ...

Experiencing a lack of information in express?

Whenever I attempt to send a POST request (using fetch) with the body containing the state of the application, I receive an empty object on the server side. What am I doing wrong here? I should be receiving the object with the properties name, username, an ...

animations are not triggering when using ng-click inside ng-repeat, is there a reason why the event is not firing?

Check out the code in jsFiddler here After reviewing the code, it's clear that when the add button is clicked, a new item is pushed to the $scope.p. Each item in the ng-repeat loop has an event-binding and everything functions correctly. However, onc ...

I'm encountering inexplicable duplications of elements on my Wordpress site

Here is an example of my template header: <header> <?php if ( function_exists( 'jetpack_the_site_logo' ) ) jetpack_the_site_logo(); ?> <a class="menu-toggle">menu</div> <?php wp_nav_menu( array('them ...

Encountering a loading error with r.js while attempting to optimize

In my main.js file, I have the following configuration for Require.js: /*--- Setting up Require.js as the main module loader ---*/ require.config({ baseUrl: '/javascripts/libs/home/', waitSeconds: 0, paths : { jquer ...

Leveraging recompose utility within the structure

I am exploring the use of recompose utility functions as a React element, enabling me to incorporate them into JSX as higher-order components (HOC). const enhancedInput = props => { return (<OnlyUpdateForKeys keys={['name']> ...

Obtain the breakpoint value from Bootstrap 5

We have recently updated our project from Bootstrap 4 to Bootstrap 5. I am trying to retrieve the value of a breakpoint in my TypeScript/JavaScript code, which used to work in Bootstrap 4 with: window .getComputedStyle(document.documentElement) .g ...

Adjusting the display of HTML elements depending on geolocation authorization

I am currently facing an issue with my HTML code where I want to show an element only if the user declines to share their location with the browser. However, my code is not functioning as expected when the user rejects the location request. On the other ha ...

The sequence in which functions are executed when bound to an event in JavaScript

Recently, I found myself diving into the world of JavaScript to uncover details about how functions bound to a page event are executed. Take, for instance, when using an EventListener. Let's say you bind three functions - A(), B(), and C() - to the s ...

Unable to showcase information in the center of an HTML document

Hello, I'm facing an issue with my HTML page that has a left vertical nav-bar. Despite my efforts, I can't seem to display content (text) in the center of the page as shown in the screenshot with the red oval. I've attempted inserting text ...

Nuxt's dynamic route generation results in a 400 error status code

Currently integrating Nuxt with my app and aiming to establish a connection with my server to retrieve data. To create dynamic routes, I am utilizing the built-in generate method but facing some challenges. Upon executing the generate command, I encounte ...

Implement AngularJS to ensure that scripts are only loaded after the page has finished rendering

I am having trouble implementing the TripAdvisor widget on my website. It functions correctly when the page is refreshed, but it does not appear when navigating through links. Additionally, an error message is displayed stating that the document could not ...

What methods are available to gradually increase a counter until it reaches a specific number by a designated date?

I have a unique idea for my website - I want to create a special counter that gradually increases to a specific number, but does so over the course of an entire year. Imagine starting at 0 and aiming to reach 35340340 after exactly one year has passed. It ...

Invoke a React component within a conditional statement

There is a function for exporting data in either csv or xls format based on an argument specifying the type. The functionality works flawlessly for xls but encounters issues with csv. Here's the code snippet: const exportFile = (exportType) => { ...