简体   繁体   中英

how to add tooltip in react d3 v4 bar chart

I have to add the tooltip on react d3 v4 bar chart on mouseover.I have tried customized function mentioned below,

onMouseOverHandler(d){
   var tooltip = d3Select("body").append("div")   
    .attr("class", "tooltip")               
    .style("opacity", 0);

    tooltip.transition().duration(200).style("opacity", .9);      
    tooltip.html(d)  
    .style("left", d3Select(this).attr("cx") + "px")     
    .style("top", d3Select(this).attr("cy") + "px");

but it's not working. someone can you help me for this one.

Thanks, Arun S

I added a tooltip to the example you linked to in your comment to this fork of the original GitHub repository .

I created a Tooltip component. Of course, keep in mind that this isn't necessarily the "right" or only way to add a tooltip to a React application that uses D3.

I went through the following steps:

  1. Created state in the Chart component that tracks the data for which bar, if any, is currently hovered

  2. Created onMouseOver and onMouseOut events in the Bars component to determine which bar has just been hovered or left and pass that up to the Chart component to set the new state

  3. Passed the state from the Chart component back down to a Tooltip component I created

The Tooltip component looks like this:

export default ({hoveredBar, scales}) => {
  const { xScale, yScale } = scales
  const styles = {
    left: `${xScale(hoveredBar.title) - 30}px`,
    top: `${yScale(hoveredBar.value)}px`
  }

  return (
    <div className="Tooltip" style={styles}>
      <table>
        <thead>
          <tr>
            <th colSpan="2">{hoveredBar.title}</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td colSpan="1">Bodies</td>
            <td colSpan="1">{hoveredBar.value}</td>
          </tr>
          <tr>
            <td colSpan="1">Year</td>
            <td colSpan="1">{hoveredBar.year}</td>
          </tr>
        </tbody>
      </table>
    </div>
  )
}

I used it in the Chart component, and tracked the currently hovered bar as state:

class Chart extends Component {
  constructor(props) {
    super(props)
    this.xScale = scaleBand()
    this.yScale = scaleLinear()

    this.state = {
      hoveredBar: null
    }
  }

  render() {
    const margins = { top: 50, right: 20, bottom: 100, left: 60 }
    const svgDimensions = {
      width: Math.max(this.props.parentWidth, 300),
      height: 500
    }

    const maxValue = Math.max(...data.map(d => d.value))

    const xScale = this.xScale
      .padding(0.5)
      .domain(data.map(d => d.title))
      .range([margins.left, svgDimensions.width - margins.right])

    const yScale = this.yScale
      .domain([0, maxValue])
      .range([svgDimensions.height - margins.bottom, margins.top])

    return (
      <div className="Chart">
        <svg width={svgDimensions.width} height={svgDimensions.height}>
          <Axes
            scales={{ xScale, yScale }}
            margins={margins}
            svgDimensions={svgDimensions}
          />
          <Bars
            scales={{ xScale, yScale }}
            margins={margins}
            data={data}
            maxValue={maxValue}
            svgDimensions={svgDimensions}
            onMouseOverCallback={datum => this.setState({hoveredBar: datum})}
            onMouseOutCallback={datum => this.setState({hoveredBar: null})}
          />
        </svg>
        { this.state.hoveredBar ?
          <Tooltip
            hoveredBar={this.state.hoveredBar}
            scales={{ xScale, yScale }}
          /> :
          null
        }
      </div>
    )
  }
}

And I set the onMouseOver and onMouseOut events in the Bars component:

export default class Bars extends Component {
  constructor(props) {
    super(props)

    this.colorScale = scaleLinear()
      .domain([0, this.props.maxValue])
      .range(['#F3E5F5', '#7B1FA2'])
      .interpolate(interpolateLab)
  }

  render() {
    const { scales, margins, data, svgDimensions } = this.props
    const { xScale, yScale } = scales
    const { height } = svgDimensions

    const bars = (
      data.map(datum =>
        <rect
          key={datum.title}
          x={xScale(datum.title)}
          y={yScale(datum.value)}
          height={height - margins.bottom - scales.yScale(datum.value)}
          width={xScale.bandwidth()}
          fill={this.colorScale(datum.value)}
          onMouseOver={() => this.props.onMouseOverCallback(datum)}
          onMouseOut={() => this.props.onMouseOutCallback(null)}
        />,
      )
    )

    return (
      <g>{bars}</g>
    )
  }
}

Here I have described a solution to the same problem.

https://stackoverflow.com/a/56674517/2903711

The result looked this

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM