I'm newbie in D3 and I want to create a scatterplot and when I put the mouse over a point I would like to show a text near to the point, like a tooltip.
I can change the color of the point but I cannot access to the state of React Object because I've got an error:
The code where previously I set the state is this:
componentDidMount(){
let canvas = this.setCanvas();
let scales = this.setScales(this.props.data);
this.setState({
canvas: canvas,
scales: scales
}, () => {
this.setAxesToCanvas(canvas, scales);
this.setPointsToCanvas(canvas, this.props.data, scales);
});
}
The method setCanvas returns a svg:
setCanvas(){
// Add the visualization svg canvas to the container <div>
let svg = d3.select("#" + this.props.idContainer)
.append("svg")
.style("background-color", "#354560")
.style("color", "#FFFFFF") //With this we've got the color of the axis too
.attr("height", this.state.height)
.attr("width", this.state.width);
return svg;
}
And setScales returns a json object:
setScales(data){
let xRange = [this.state.margin.left, this.state.width - this.state.margin.right];
let yRange = [this.state.margin.top, this.state.height - this.state.margin.top - this.state.margin.bottom]; // flip order because y-axis origin is upper LEFT
let xScale = d3.scaleLinear()
.domain([ d3.min(data, d => parseFloat(d.value_x)) - 1, d3.max(data, d => parseFloat(d.value_x)) + 1])
.range(xRange);
let yScale = d3.scaleLinear()
.domain([ d3.max(data, d => parseFloat(d.value_y)) + 1, d3.min(data, d => parseFloat(d.value_y)) - 1])
.range(yRange);
return {"xScale" : xScale, "yScale" : yScale, "xRange" : xRange, "yRange" : yRange};
}
After setting the state I call the functions setAxesToCanvas and setPointsToCanvas. It's this last function where I have defined to catch the even "onMouseOver" how you can see:
setPointsToCanvas(canvas, data){
let xRange = [this.state.margin.left, this.state.width - this.state.margin.right];
let yRange = [this.state.margin.top, this.state.height - this.state.margin.top - this.state.margin.bottom]; // flip order because y-axis origin is upper LEFT
let xScale = d3.scaleLinear()
.domain([ d3.min(data, d => parseFloat(d.value_x)) -1, d3.max(data, d => parseFloat(d.value_x)) + 1])
.range(xRange);
let yScale = d3.scaleLinear()
.domain([ d3.max(data, d => parseFloat(d.value_y)) + 1, d3.min(data, d => parseFloat(d.value_y)) - 1])
.range(yRange);
canvas.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 5.5) //Radius size, could map to another dimension
.attr("cx", function(d) { return xScale(parseFloat(d.value_x)); }) //x position
.attr("cy", function(d) { return yScale(parseFloat(d.value_y)); }) //y position
.style("fill", "#FFC107")
.on("mouseover", this.tipMouseOver);
}
The method that I call when the event is fire is tipMouseOver and its code is:
tipMouseOver(data, iter){
// Use D3 to select element, change color and size
d3.select(this)
.style("fill", "green")
.style("radius", "5 em");
// Specify where to put label of text
console.log("Before to set the text - data: " + data + " iter: " + iter);
this.state.canvas.append("text").attr({
id: "t" + data.value_x + "-" + data.value_y + "-" + iter, // Create an id for text so we can select it later for removing on mouseout
x: function() { return this.state.scales.xScale(data.value_x) - 30; },
y: function() { return this.state.scales.yScale(data.value_y) - 15; }
})
.text(function() {
return [data.value_x, data.value_y]; // Value of the text
});
}
How you can see in the screencap all the is executed until its arrive to the line
this.state.canvas.append("text")
If I try to pass the canvas and scales to the method which is fired when the event occurs
.on("mouseover", this.tipMouseOver(canvas, scales));
And I modify the function ...
tipMouseOver(data, iter, canvas, scales){
....
}
But when I reload the page, I've got this error:
I have checked that I cannot access to the props of the react object too.
Therefore, how can I get access to the state and props of the object to get canvas and scales?
Edit I:
I add the code in a codesandbox: