首页 > 解决方案 > 如何使用 D3.js v5 访问数组中的特定数据以获取堆叠条形图工具提示

问题描述

我在下面有一个 JSON 文件。我将字段 1 中的所有值和字段 2 中的所有值等相加......然后尝试获取工具提示上显示的总金额。因此,当我将鼠标悬停在每个“矩形”上时,我有一个总字段数量以及组数量。

    [
  {
    "group": "Field 1",
    "0-45": 12345,
    "46-65": 91568,
    "66-85": 13087,
    "85+": 7045
  },
  {
    "group": "Field 2",
    "0-45": 62345,
    "46-65": 15347,
    "66-85": 37688,
    "85+": 7007
  }
]

这是示例代码...

let totalColVal = d3.nest()
  .key(function(d){return (d.group)})   
  .rollup(function(totals){
    return d3.sum(totals, function(d) {return d3.sum(d3.values(d))})
  }).entries(data)

  console.log(totalColVal)

这返回

(4) [{…}, {…}, {…}, {…}]
0: {key: "Field 1", value: 124045}
1: {key: "Field 2", value: 122387}
2: {key: "Field 3", value: 172041}
3: {key: "Field 4", value: 67594}

上面获取每个字段的总量,但是当我将鼠标悬停在每个矩形上时,如何让工具提示显示每个值。

  //tooltip
  let mouseover = function(d) {
    let subgroupName = d3.select(this.parentNode).datum().key;
      // console.log(subgroupName)
    let subgroupValue = d.data[subgroupName];
      // console.log(subgroupValue)    
  
    tooltip.html('<b>Field:</b> <span style="color:yellow">' + d.data.group + '</span>' + '<br>\
      ' + '<b>Count:</b> <span style="color:yellow">' + formater(subgroupValue) + '</span>' + '<br>\
      ' + ' <b>Size Band:</b> <span style="color:yellow">' + subgroupName + '</span>' + '<br>\
      ' + '<b>Field Total:</b>  <span style="color:yellow">' + totalColVal + '</span>')
        .style("opacity", 1)
  };
  let mousemove = function(d) {
    tooltip.style('left', (d3.event.pageX - 70) + 'px')
      .style('top', (d3.event.pageY - 85) + 'px')
  };
  let mouseleave = function(d) {
    tooltip.style("opacity", 0)
  };

totalColVal 在我的工具提示上返回 [object Object],[object Object],[object Object],[object Object]。

非常感谢您的帮助。

JS小提琴在这里

标签: javascriptarraysjsond3.js

解决方案


要从i第 th 条获取值,实际上在 旁边有第二个参数,d您可以在所有 d3 函数中访问该参数。i表示您传递给它的数组中该元素的索引。

我将鼠标悬停功能更改为仅i在此处使用,它解决了您的问题:

const canvas = d3.select('#stacked');

const stackedSvg = canvas.append('svg')
  .attr('preserveAspectRatio', 'xMinYMin meet')
  .attr('viewBox', '0 0 900 500')
  .classed('svg-content', true);

let margin = {top: 40, right: 30, bottom: 20, left: 70},
  width = 260 - margin.left - margin.right,
  height = 250 - margin.top - margin.bottom;

// append the svg object to the body of the page
let svg = stackedSvg.append('g')
  .attr('width', width)
  .attr('height', height)  
  .attr('transform', `translate(${margin.left}, ${margin.top})`);

//number formater   
const formater = d3.format(',d');

let myData = [
  {
    "group": "Field 1",
    "0-45": 12345,
    "46-65": 91568,
    "66-85": 13087,
    "85+": 7045
  },
  {
    "group": "Field 2",
    "0-45": 62345,
    "46-65": 15347,
    "66-85": 37688,
    "85+": 7007
  },
  {
    "group": "Field 3",
    "0-45": 11457,
    "46-65": 28456,
    "66-85": 124564,
    "85+": 7564
  },
  {
    "group": "Field 4",
    "0-45": 19234,
    "46-65": 26754,
    "66-85": 14153,
    "85+": 7453
  }
]

//tooltip
let tooltip = d3.select('body').append('div')
  .attr('class', 'tooltip')
  .style('opacity', 0);

// Parse the Data
Promise.resolve(myData)
  .then(data => {
    // console.log(data);

  //select the size bands
  let keys = d3.keys(data[0]).slice(1); 
    // console.log(keys);

  // List of groups in JSON. value of the first column called group
  let groups = d3.map(data, function(d){return(d.group)}).keys(); 
    // console.log(groups);

  // X axis
  let x = d3.scaleBand()
    .domain(groups)
    .range([0, width])
    .padding([0.2])
  svg.append('g')    
    .attr('transform', 'translate(0,' + height + ')')
    .call(d3.axisBottom(x)
    .tickSizeOuter(0));  
    
  svg.append('text')
    .attr('class', 'xLabel')
    .attr('text-anchor', 'end')
    .attr('x', width / 2 + 20)
    .attr('y', height + 30)                          
    .text('Size Band')
    .style('font-size', 10);    

  // Y axis
  let y = d3.scaleLinear()
    .domain([0, d3.max(data, d => d3.sum(keys, k => +d[k]))])
    .range([ height, 0 ]); 
    
  svg.append('g')
    .call(d3.axisLeft(y)
      .tickSizeOuter(0)
      .tickSizeInner(- width)
      .tickPadding(5))      
      .selectAll(".tick")
      .style("stroke-dasharray", ("1, 1")) //dash line across graph.
      .each(function (d, i) {
        if ( d == 0 ) {
            this.remove();
        }
    });
    
  svg.append('text')
    .attr('class', 'yLabel')
    .attr('transform', 'rotate(-90)')
    .attr('y', - 40)
    .attr('x', - height / 2 + 20)
    .attr('text-anchor', 'end')
    .text('Units')
    .style('font-size', 10);

  // color
  let color = d3.scaleOrdinal()
    .domain(keys)
    .range(['brown', 'steelblue', 'olivedrab', 'darkorange']);

  //stack the data --> stack per subgroup
  let stackedData = d3.stack()
    .keys(keys)
    (data);
    console.log(stackedData)

    let totalColVal = d3.nest()
      .key(function(d){return (d.group)})   
      .rollup(function(totals){
        return d3.sum(totals, function(d) {return d3.sum(d3.values(d))})
      }).entries(data)
    
      console.log(totalColVal)

  //tooltip
  let mouseover = function(d, i) {
    let subgroupName = d3.select(this.parentNode).datum().key;
      // console.log(subgroupName)
    let subgroupValue = d.data[subgroupName];
      // console.log(subgroupValue)    
  
    tooltip.html('<b>Field:</b> <span style="color:yellow">' + d.data.group + '</span>' + '<br>\
      ' + '<b>Count:</b> <span style="color:yellow">' + formater(subgroupValue) + '</span>' + '<br>\
      ' + ' <b>Size Band:</b> <span style="color:yellow">' + subgroupName + '</span>' + '<br>\
      ' + '<b>Field Total:</b>  <span style="color:yellow">' + totalColVal[i].value + '</span>')
        .style("opacity", 1)
  };
  let mousemove = function(d) {
    tooltip.style('left', (d3.event.pageX - 70) + 'px')
      .style('top', (d3.event.pageY - 85) + 'px')
  };
  let mouseleave = function(d) {
    tooltip.style("opacity", 0)
  };

  // Show the bars
  svg.append('g')
    .selectAll('g')
    // Enter in the stack data = loop key per key = group per group
    .data(stackedData)
    .enter().append('g')
      .attr('fill', function(d) { return color(d.key) })
      .selectAll('rect')

      // enter a second time = loop subgroup per subgroup to add all rectangles
      .data(function(d) { return d; })
      .enter().append('rect')  
        .on('mouseover', mouseover)
        .on('mousemove', mousemove)
        .on('mouseleave', mouseleave)
        .transition()
          .attr('y', d => y(d.height))
          .delay(function(d, i) {
            return i * 100
          })      
          .ease(d3.easeLinear)       
        .attr('x', function(d) { return x(d.data.group); })
        .attr('y', function(d) { return y(d[1]); })
        .attr('height', function(d) { return y(d[0]) - y(d[1]); })
        .attr('width',x.bandwidth())        
        .style('stroke', 'black')
        .style('opacity', 0.9);     
 
});
body {
  font-family: halyard-display, sans-serif;
/*   background-color: black; */
}

div.tooltip {
  position: absolute;
  text-align: left;
  width: fit-content;
  height: fit-content;
  padding: 5px 5px;   
  font-size: 16px;
  background:rgb(24, 23, 23);
  color: white;    
  border-radius: 5px;
  pointer-events: none;    
}

.yLabel {
  fill: #DEDC00;
  font-style: italic;
  font-weight: 600;
}

.xLabel {
  fill: #DEDC00;
  font-style: italic;
  font-weight: 600;
}

g .tick text {
  font-size: 8px;
  color: grey;
}

g .tick text:hover {
  font-size: 12px;
}

g .tick {
  color: #DEDC00;
}
<script src="https://d3js.org/d3.v5.js"></script>
<div id="stacked"></div>


推荐阅读