D3 工具提示未在 React 组件中正确更新
D3 tooltip not updating correctly in a React Component
我花了很长时间制作这个代码片段来突出一个非常令人沮丧的问题,我 运行 使用 d3 并一起做出反应,特别是尝试根据组件状态动态更新工具提示。
首先是代码片段,我已尽力保持简短和完整(为简洁起见,您可以跳过 css。只需注意单选按钮组,并将鼠标悬停在图形组件):
class GraphComponent extends React.Component {
constructor(props) {
super(props);
}
// Lifecycle Components
drawGraph() {
const { buttonName } = this.props;
const myData = [
{x:25, y:30, name:"tommy", age:"24"},
{x:108, y:82, name:"joey", age:"21"},
{x:92, y:107, name:"nicky", age:"23"},
{x:185, y:50, name:"peter", age:"27"},
{x:65, y:80, name:"mickie", age:"4"},
{x:165, y:80, name:"gregie", age:"14"},
{x:154, y:10, name:"tammie", age:"24"},
{x:102, y:42, name:"benny", age:"29"}
]
// var myD3tip = d3Tip()
var myD3tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-20,0])
.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`)
console.log('buttonName outside mouseover', buttonName)
const divToolTip = d3.select('div#myTooltip')
const points = d3.select('#mySvg').select('g.points')
points.call(myD3tip)
points
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 8)
.attr('fill', 'blue')
.on('mouseover', function(d) {
myD3tip.show(d)
console.log('buttonName in mouseover', buttonName)
divToolTip
.style('opacity', 1)
.style('left', d3.mouse(this)[0] + 'px')
.style('top', (d3.mouse(this)[1]) + 'px')
.html(`<p>Div Tip: ${buttonName}: ${d[buttonName]}</p>`)
})
.on('mouseout', function(d) {
myD3tip.hide(d)
divToolTip
.style('opacity', 0)
})
}
componentDidUpdate() {
this.drawGraph()
}
componentDidMount() {
this.drawGraph()
}
render() {
return (
<svg id="mySvg">
<g className="points" />
</svg>
)
}
}
class GraphContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
ageOrName: "age"
}
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
this.setState({ ageOrName: event.target.value })
}
render() {
const { ageOrName } = this.state;
const ageOrNameOptions = ["age", "name"];
const ageOrNameButtons =
<form>
<div>
{ageOrNameOptions.map((d, i) => {
return (
<label key={i+d}>
<input
type={"radio"}
value={ageOrNameOptions[i]}
checked={ageOrName === ageOrNameOptions[i]}
onChange={this.handleChange}
/>
<span>{ageOrNameOptions[i]}</span>
</label>
)
})}
</div>
</form>;
return (
<div>
{ageOrNameButtons}
<GraphComponent
buttonName={ageOrName}
/>
</div>
)
}
}
ReactDOM.render(
<GraphContainer />,
document.getElementById('root')
);
/* Div ToolTip */
#myTooltip {
opacity: 0;
position: absolute;
pointer-events: none;
background-color: lightblue;
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* ========== */
/* D3 ToolTip */
/* ========== */
.d3-tip {
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: #444;
content: "BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
/* ========== */
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script>
<div id="root">
<svg id="mySvg">
</div>
<div id="myTooltip"></div>
概览是这样的:
我有 2 个组件,一个容器组件和一个图形组件。容器组件具有 ageOrName 状态,以及一个带有 "age" 和 "name" 的单选按钮来更新此状态。然后容器组件将这个值传递给图形组件。
接收 ageOrName 作为 prop 的图形组件使用此 prop 更改工具提示中的文本。但是,它不起作用。为了绝对完整,我包含了 2! 个不同的工具提示:
- 使用 d3-tip 库创建的工具提示
- 自定义工具提示,从页面上的 div 创建
我有两个 console.logs() 来显示图形组件内 ageOrName 属性的值,我已经意识到一些非常令人担忧。 prop 的值是不同的 取决于我是否在 "on.mouseover" 调用中。我不知道为什么。
让这个工作对我的应用程序很重要,非常感谢任何对此的帮助!
编辑:几乎肯定会在 2 天内(现在想)悬赏这个问题。早点回答也会奖励。
即使在选择 name 输入后,{buttonName}
仍然选择 age 的主要原因是 mouseover
函数在第一次 drawGraph
调用期间绑定到圆圈,下次再次调用时,会出现 d3 update 逻辑,它不会添加 new 鼠标悬停事件到现有的圈子(基于新的{buttonName}
。
我想出了 2 种方法来解决这个问题:
只需在鼠标悬停函数本身中获取当前 {buttonName}
。方法如下:
.on('mouseover', function(d) {
var { buttonName } = that.props;
myD3tip.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`).show(d);
class GraphComponent extends React.Component {
constructor(props) {
super(props);
}
// Lifecycle Components
drawGraph() {
var that = this;
const { buttonName } = this.props;
const myData = [
{x:25, y:30, name:"tommy", age:"24"},
{x:108, y:82, name:"joey", age:"21"},
{x:92, y:107, name:"nicky", age:"23"},
{x:185, y:50, name:"peter", age:"27"},
{x:65, y:80, name:"mickie", age:"4"},
{x:165, y:80, name:"gregie", age:"14"},
{x:154, y:10, name:"tammie", age:"24"},
{x:102, y:42, name:"benny", age:"29"}
]
// var myD3tip = d3Tip()
var myD3tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-20,0])
.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`)
const divToolTip = d3.select('div#myTooltip')
const points = d3.select('#mySvg').select('g.points')
points.call(myD3tip)
points
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 8)
.attr('fill', 'blue');
d3.select('#mySvg').select('g.points').selectAll('circle')
.on('mouseover', function(d) {
var { buttonName } = that.props;
myD3tip.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`).show(d);
divToolTip
.style('opacity', 1)
.style('left', d3.mouse(this)[0] + 'px')
.style('top', (d3.mouse(this)[1]) + 'px')
.html(`<p>Div Tip: ${buttonName}: ${d[buttonName]}</p>`)
})
.on('mouseout', function(d) {
myD3tip.hide(d)
divToolTip
.style('opacity', 0)
})
}
componentDidUpdate() {
this.drawGraph()
}
componentDidMount() {
this.drawGraph()
}
render() {
return (
<svg id="mySvg">
<g className="points" />
</svg>
)
}
}
class GraphContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
ageOrName: "age"
}
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
this.setState({ ageOrName: event.target.value })
}
render() {
const { ageOrName } = this.state;
const ageOrNameOptions = ["age", "name"];
const ageOrNameButtons =
<form>
<div>
{ageOrNameOptions.map((d, i) => {
return (
<label key={i+d}>
<input
type={"radio"}
value={ageOrNameOptions[i]}
checked={ageOrName === ageOrNameOptions[i]}
onChange={this.handleChange}
/>
<span>{ageOrNameOptions[i]}</span>
</label>
)
})}
</div>
</form>;
return (
<div>
{ageOrNameButtons}
<GraphComponent
buttonName={ageOrName}
/>
</div>
)
}
}
ReactDOM.render(
<GraphContainer />,
document.getElementById('root')
);
/* Div ToolTip */
#myTooltip {
opacity: 0;
position: absolute;
pointer-events: none;
background-color: lightblue;
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* ========== */
/* D3 ToolTip */
/* ========== */
.d3-tip {
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: #444;
content: "BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
/* ========== */
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script>
<div id="root">
<svg id="mySvg"></svg>
</div>
<div id="myTooltip"></div>
在每个 drawGraph
调用期间绑定 mouseover
事件:(类似这样)
points.select('circle')
....
.attr('fill', 'blue');
d3.select('#mySvg').select('g.points').selectAll('circle')
.on('mouseover', function(d) {
.....
class GraphComponent extends React.Component {
constructor(props) {
super(props);
}
// Lifecycle Components
drawGraph() {
const { buttonName } = this.props;
const myData = [
{x:25, y:30, name:"tommy", age:"24"},
{x:108, y:82, name:"joey", age:"21"},
{x:92, y:107, name:"nicky", age:"23"},
{x:185, y:50, name:"peter", age:"27"},
{x:65, y:80, name:"mickie", age:"4"},
{x:165, y:80, name:"gregie", age:"14"},
{x:154, y:10, name:"tammie", age:"24"},
{x:102, y:42, name:"benny", age:"29"}
]
// var myD3tip = d3Tip()
var myD3tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-20,0])
.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`);
const divToolTip = d3.select('div#myTooltip')
const points = d3.select('#mySvg').select('g.points')
points.call(myD3tip)
points
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 8)
.attr('fill', 'blue');
d3.select('#mySvg').select('g.points').selectAll('circle')
.on('mouseover', function(d) {
myD3tip.show(d)
divToolTip
.style('opacity', 1)
.style('left', d3.mouse(this)[0] + 'px')
.style('top', (d3.mouse(this)[1]) + 'px')
.html(`<p>Div Tip: ${buttonName}: ${d[buttonName]}</p>`)
})
.on('mouseout', function(d) {
myD3tip.hide(d)
divToolTip
.style('opacity', 0)
})
}
componentDidUpdate() {
this.drawGraph()
}
componentDidMount() {
this.drawGraph()
}
render() {
return (
<svg id="mySvg">
<g className="points" />
</svg>
)
}
}
class GraphContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
ageOrName: "age"
}
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
this.setState({ ageOrName: event.target.value })
}
render() {
const { ageOrName } = this.state;
const ageOrNameOptions = ["age", "name"];
const ageOrNameButtons =
<form>
<div>
{ageOrNameOptions.map((d, i) => {
return (
<label key={i+d}>
<input
type={"radio"}
value={ageOrNameOptions[i]}
checked={ageOrName === ageOrNameOptions[i]}
onChange={this.handleChange}
/>
<span>{ageOrNameOptions[i]}</span>
</label>
)
})}
</div>
</form>;
return (
<div>
{ageOrNameButtons}
<GraphComponent
buttonName={ageOrName}
/>
</div>
)
}
}
ReactDOM.render(
<GraphContainer />,
document.getElementById('root')
);
/* Div ToolTip */
#myTooltip {
opacity: 0;
position: absolute;
pointer-events: none;
background-color: lightblue;
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* ========== */
/* D3 ToolTip */
/* ========== */
.d3-tip {
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: #444;
content: "BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
/* ========== */
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script>
<div id="root">
<svg id="mySvg"></svg>
</div>
<div id="myTooltip"></div>
(我个人会选择第一种方法)
希望这对您有所帮助。
我花了很长时间制作这个代码片段来突出一个非常令人沮丧的问题,我 运行 使用 d3 并一起做出反应,特别是尝试根据组件状态动态更新工具提示。
首先是代码片段,我已尽力保持简短和完整(为简洁起见,您可以跳过 css。只需注意单选按钮组,并将鼠标悬停在图形组件):
class GraphComponent extends React.Component {
constructor(props) {
super(props);
}
// Lifecycle Components
drawGraph() {
const { buttonName } = this.props;
const myData = [
{x:25, y:30, name:"tommy", age:"24"},
{x:108, y:82, name:"joey", age:"21"},
{x:92, y:107, name:"nicky", age:"23"},
{x:185, y:50, name:"peter", age:"27"},
{x:65, y:80, name:"mickie", age:"4"},
{x:165, y:80, name:"gregie", age:"14"},
{x:154, y:10, name:"tammie", age:"24"},
{x:102, y:42, name:"benny", age:"29"}
]
// var myD3tip = d3Tip()
var myD3tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-20,0])
.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`)
console.log('buttonName outside mouseover', buttonName)
const divToolTip = d3.select('div#myTooltip')
const points = d3.select('#mySvg').select('g.points')
points.call(myD3tip)
points
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 8)
.attr('fill', 'blue')
.on('mouseover', function(d) {
myD3tip.show(d)
console.log('buttonName in mouseover', buttonName)
divToolTip
.style('opacity', 1)
.style('left', d3.mouse(this)[0] + 'px')
.style('top', (d3.mouse(this)[1]) + 'px')
.html(`<p>Div Tip: ${buttonName}: ${d[buttonName]}</p>`)
})
.on('mouseout', function(d) {
myD3tip.hide(d)
divToolTip
.style('opacity', 0)
})
}
componentDidUpdate() {
this.drawGraph()
}
componentDidMount() {
this.drawGraph()
}
render() {
return (
<svg id="mySvg">
<g className="points" />
</svg>
)
}
}
class GraphContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
ageOrName: "age"
}
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
this.setState({ ageOrName: event.target.value })
}
render() {
const { ageOrName } = this.state;
const ageOrNameOptions = ["age", "name"];
const ageOrNameButtons =
<form>
<div>
{ageOrNameOptions.map((d, i) => {
return (
<label key={i+d}>
<input
type={"radio"}
value={ageOrNameOptions[i]}
checked={ageOrName === ageOrNameOptions[i]}
onChange={this.handleChange}
/>
<span>{ageOrNameOptions[i]}</span>
</label>
)
})}
</div>
</form>;
return (
<div>
{ageOrNameButtons}
<GraphComponent
buttonName={ageOrName}
/>
</div>
)
}
}
ReactDOM.render(
<GraphContainer />,
document.getElementById('root')
);
/* Div ToolTip */
#myTooltip {
opacity: 0;
position: absolute;
pointer-events: none;
background-color: lightblue;
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* ========== */
/* D3 ToolTip */
/* ========== */
.d3-tip {
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: #444;
content: "BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
/* ========== */
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script>
<div id="root">
<svg id="mySvg">
</div>
<div id="myTooltip"></div>
概览是这样的:
我有 2 个组件,一个容器组件和一个图形组件。容器组件具有 ageOrName 状态,以及一个带有 "age" 和 "name" 的单选按钮来更新此状态。然后容器组件将这个值传递给图形组件。
接收 ageOrName 作为 prop 的图形组件使用此 prop 更改工具提示中的文本。但是,它不起作用。为了绝对完整,我包含了 2! 个不同的工具提示:
- 使用 d3-tip 库创建的工具提示
- 自定义工具提示,从页面上的 div 创建
我有两个 console.logs() 来显示图形组件内 ageOrName 属性的值,我已经意识到一些非常令人担忧。 prop 的值是不同的 取决于我是否在 "on.mouseover" 调用中。我不知道为什么。
让这个工作对我的应用程序很重要,非常感谢任何对此的帮助!
编辑:几乎肯定会在 2 天内(现在想)悬赏这个问题。早点回答也会奖励。
即使在选择 name 输入后,{buttonName}
仍然选择 age 的主要原因是 mouseover
函数在第一次 drawGraph
调用期间绑定到圆圈,下次再次调用时,会出现 d3 update 逻辑,它不会添加 new 鼠标悬停事件到现有的圈子(基于新的{buttonName}
。
我想出了 2 种方法来解决这个问题:
只需在鼠标悬停函数本身中获取当前
{buttonName}
。方法如下:.on('mouseover', function(d) { var { buttonName } = that.props; myD3tip.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`).show(d);
class GraphComponent extends React.Component {
constructor(props) {
super(props);
}
// Lifecycle Components
drawGraph() {
var that = this;
const { buttonName } = this.props;
const myData = [
{x:25, y:30, name:"tommy", age:"24"},
{x:108, y:82, name:"joey", age:"21"},
{x:92, y:107, name:"nicky", age:"23"},
{x:185, y:50, name:"peter", age:"27"},
{x:65, y:80, name:"mickie", age:"4"},
{x:165, y:80, name:"gregie", age:"14"},
{x:154, y:10, name:"tammie", age:"24"},
{x:102, y:42, name:"benny", age:"29"}
]
// var myD3tip = d3Tip()
var myD3tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-20,0])
.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`)
const divToolTip = d3.select('div#myTooltip')
const points = d3.select('#mySvg').select('g.points')
points.call(myD3tip)
points
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 8)
.attr('fill', 'blue');
d3.select('#mySvg').select('g.points').selectAll('circle')
.on('mouseover', function(d) {
var { buttonName } = that.props;
myD3tip.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`).show(d);
divToolTip
.style('opacity', 1)
.style('left', d3.mouse(this)[0] + 'px')
.style('top', (d3.mouse(this)[1]) + 'px')
.html(`<p>Div Tip: ${buttonName}: ${d[buttonName]}</p>`)
})
.on('mouseout', function(d) {
myD3tip.hide(d)
divToolTip
.style('opacity', 0)
})
}
componentDidUpdate() {
this.drawGraph()
}
componentDidMount() {
this.drawGraph()
}
render() {
return (
<svg id="mySvg">
<g className="points" />
</svg>
)
}
}
class GraphContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
ageOrName: "age"
}
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
this.setState({ ageOrName: event.target.value })
}
render() {
const { ageOrName } = this.state;
const ageOrNameOptions = ["age", "name"];
const ageOrNameButtons =
<form>
<div>
{ageOrNameOptions.map((d, i) => {
return (
<label key={i+d}>
<input
type={"radio"}
value={ageOrNameOptions[i]}
checked={ageOrName === ageOrNameOptions[i]}
onChange={this.handleChange}
/>
<span>{ageOrNameOptions[i]}</span>
</label>
)
})}
</div>
</form>;
return (
<div>
{ageOrNameButtons}
<GraphComponent
buttonName={ageOrName}
/>
</div>
)
}
}
ReactDOM.render(
<GraphContainer />,
document.getElementById('root')
);
/* Div ToolTip */
#myTooltip {
opacity: 0;
position: absolute;
pointer-events: none;
background-color: lightblue;
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* ========== */
/* D3 ToolTip */
/* ========== */
.d3-tip {
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: #444;
content: "BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
/* ========== */
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script>
<div id="root">
<svg id="mySvg"></svg>
</div>
<div id="myTooltip"></div>
在每个
drawGraph
调用期间绑定mouseover
事件:(类似这样)points.select('circle') .... .attr('fill', 'blue'); d3.select('#mySvg').select('g.points').selectAll('circle') .on('mouseover', function(d) { .....
class GraphComponent extends React.Component {
constructor(props) {
super(props);
}
// Lifecycle Components
drawGraph() {
const { buttonName } = this.props;
const myData = [
{x:25, y:30, name:"tommy", age:"24"},
{x:108, y:82, name:"joey", age:"21"},
{x:92, y:107, name:"nicky", age:"23"},
{x:185, y:50, name:"peter", age:"27"},
{x:65, y:80, name:"mickie", age:"4"},
{x:165, y:80, name:"gregie", age:"14"},
{x:154, y:10, name:"tammie", age:"24"},
{x:102, y:42, name:"benny", age:"29"}
]
// var myD3tip = d3Tip()
var myD3tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-20,0])
.html(d => `<p>D3 Tip: ${buttonName}: ${d[buttonName]}</p>`);
const divToolTip = d3.select('div#myTooltip')
const points = d3.select('#mySvg').select('g.points')
points.call(myD3tip)
points
.selectAll('circle')
.data(myData)
.enter()
.append('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 8)
.attr('fill', 'blue');
d3.select('#mySvg').select('g.points').selectAll('circle')
.on('mouseover', function(d) {
myD3tip.show(d)
divToolTip
.style('opacity', 1)
.style('left', d3.mouse(this)[0] + 'px')
.style('top', (d3.mouse(this)[1]) + 'px')
.html(`<p>Div Tip: ${buttonName}: ${d[buttonName]}</p>`)
})
.on('mouseout', function(d) {
myD3tip.hide(d)
divToolTip
.style('opacity', 0)
})
}
componentDidUpdate() {
this.drawGraph()
}
componentDidMount() {
this.drawGraph()
}
render() {
return (
<svg id="mySvg">
<g className="points" />
</svg>
)
}
}
class GraphContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
ageOrName: "age"
}
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
this.setState({ ageOrName: event.target.value })
}
render() {
const { ageOrName } = this.state;
const ageOrNameOptions = ["age", "name"];
const ageOrNameButtons =
<form>
<div>
{ageOrNameOptions.map((d, i) => {
return (
<label key={i+d}>
<input
type={"radio"}
value={ageOrNameOptions[i]}
checked={ageOrName === ageOrNameOptions[i]}
onChange={this.handleChange}
/>
<span>{ageOrNameOptions[i]}</span>
</label>
)
})}
</div>
</form>;
return (
<div>
{ageOrNameButtons}
<GraphComponent
buttonName={ageOrName}
/>
</div>
)
}
}
ReactDOM.render(
<GraphContainer />,
document.getElementById('root')
);
/* Div ToolTip */
#myTooltip {
opacity: 0;
position: absolute;
pointer-events: none;
background-color: lightblue;
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* ========== */
/* D3 ToolTip */
/* ========== */
.d3-tip {
line-height: 0.50;
font-weight: bold;
padding: 8px 8px;
padding-bottom: 0px;
border-radius: 10px;
border: 2px solid #444;
font-size: 0.9em;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: #444;
content: "BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
/* ========== */
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.8.0-alpha.1/d3-tip.js"></script>
<div id="root">
<svg id="mySvg"></svg>
</div>
<div id="myTooltip"></div>
(我个人会选择第一种方法)
希望这对您有所帮助。