I have implemented a scatterplot using d3.js v3.
I would like to change the color of the datapoint if for that particular data point annotation has been added.
But when I try to do so, it is changing the color of the first data point in the array instead of the the circle where the annotation has been added.
The way I am implementing is :
eventGroup.select("circle")
.attr("class", "dot")
.attr("r", 4)
.attr("cx", 10)
.attr("cy", 10)
.attr("fill", function(d) {
return d.evtColor ? d.evtColor : "#229ae5";
})
.attr("stroke", function(d) {
return d.evtColor ? d.evtColor : "#229ae5";
})
.attr("stroke-width", 2)
.on("contextmenu", function(d) {
var position = d3.mouse(this.parentNode);
d3.event.stopPropagation(); // to avoid over-ridding of click event on the chart background
d3.select("#context-menu")
.style("position", "absolute")
.style("left", (d3.event.pageX - 220) + "px")
.style("top", (d3.event.pageY - 70) + "px")
.style("display", "inline-block")
.on("click", function() {
d3.select("#context-menu").style("display", "none");
d3.select("#annotateBox")
.style("position", "absolute")
.style("left", (d3.event.pageX - 220) + "px")
.style("top", (d3.event.pageY - 70) + "px")
.style("display", "inline-block");
d3.select("#eventLabel").text(d.label);
d3.select("#eventTime").text(d.time);
d3.select("#textArea").node().value = d.textArea || "";
d3.select("#done").on("click", function() {
d.textArea = d3.select("#textArea").node().value;
d3.select("#annotateBox").style("display", "none");
if (d.textArea) {
d3.select("circle.dot").style("fill", "#ed6a1c");
}
});
});
});
I cannot do d3.select(this.parentNode) as the parent element is not the circle.dot. What element should be selected in order to change the color of datum where annotation text has been added?
Keep a reference to the clicked DOM element (i.e., the circle)...
.on("contextmenu", function(d) {
var thisCircle = this
etc...
And use it afterwards:
if (d.textArea) {
d3.select(thisCircle).style("fill", "#ed6a1c");
}
Related
I'm working on a heatmap chart in D3 and I can't figure out how to add the text on mouseover. I am not sure how to proceed. If you could give me some clues, I would appreciate it. In the following snippet you can find the code. both the working and the non-working codeblocks. Thanks!
console.log(d3)
let screenWidth = 800
let screenHeight = 400
//load data
d3.csv('./datos/locations.csv').then(function(data){
let filtered = []
for(let item of data) {
if(item.location === "location one") {
filtered.push(item)
}
}
build(filtered)
})
//Create canvas
function createSVG() {
let container = d3.select('#container')
svg = container.append('svg')
.attr('id', 'canvas')
.attr('width', screenWidth)
.attr('height', screenHeight)
}
//Create chart
function build(data) {
let rectWidth = screenWidth / 24
let rectHeight = screenHeight / 7
let rects = svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('x', function(d,i) {
return (parseInt(d.hour) - 1) * rectWidth})
.attr('y', function(d,i){
return (parseInt(d.day) - 1) * rectHeight})
.attr('height', rectHeight)
.attr('width', rectWidth)
.style('fill', 'black')
.style('stroke', 'white')
.on('mouseover', function(d,i) {
let rects = d3.select(this)
.append('text')
.attr('x')
.attr('y')
.style('font-weight', 500)
.style('font-family', 'Arial')
.style('fill', 'red')
.text(function (d,i) {return d.value})})
}
function main() {
createSVG()
build()
}
main()
```
You can append a <div> with position: absolute to body and position it on mousemove event. Change the opacity to update its display or hidden.
var div = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
...
.on('mouseover', function(d) {
div.transition()
.duration(200)
.style('opacity', .9);
div.html('<h3>' + d.status + '</h3>' + '<p>' + timeFormat(new Date(d.date)) + ' at ' + monthDayFormat(new Date(d.date)) + '</p>')
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
})
https://jsfiddle.net/z9ucLqu2/
<text> nodes cannot be children of <rect>s, only just as <line>s or <circle>s can't. They are figure nodes and are not meant to have children. Append the tooltip to the SVG or a <g> instead.
This means that you cannot access d.value through the function (d,i) {return d.value}) anymore, but you can get it because you have access to d from .on('mouseover', function(d,i) {, just remove everything but d.value.
If you use x and y from the <rect>, what is going to happen is that the <text> element covers the <rect>, catches the mouse event and triggers a mouseout immediately on the <rect>. Since you'll probably want to remove the tooltip on mouseout, you'll get the text node flickering on and off. Either move the text to the right by at least rectWidth or use d3.event to get the mouse coordinates of the event and position it a little down and to the right, using something like .attr('x', d3.event.clientX + 10) to move it right.
I have a scatter plot similar to: http://plnkr.co/edit/MkZcXJPS7hrcWh3M0MZ1?p=preview
I want to give a tooltip on mouse hover for every combination. The tooltip code that i have currently does like:
var tooltip = d3.select("body").append("div") // tooltip code
.attr("class", "tooltip")
.style("opacity", 0);
var circles = svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.petalWidth); })
.attr("cy", function(d) { return y(d.petalLength); })
.style("fill", function(d) { return color(d.species); })
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", 1.0);
tooltip.html(d.petalLength+", "+d.petalWidth)
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
This will fail to return the correct tooltip for combinations other than petalWidth and d.petalLength.
Is there any way of knowing which combination has been selected and the associated numerical value for the combination?
To do this:
First store the tool-tip info in a new variable(displayX/displayY) like this:
.attr("cx", function(d) {
d.displayX = d.petalWidth;//so displayX holds the x info
return x(d.petalWidth);
})
.attr("cy", function(d) {
d.displayY = d.petalLength;//so displayY holds the y info
return y(d.petalLength);
})
When you set the combo reset the variables accordingly.
svg.selectAll(".dot").transition().attr("cy", function(d) {
d.displayY = d[yAxy];//reset the variable displayY
return y(d[yAxy]);
});
Same for
svg.selectAll(".dot").transition().attr("cx", function(d) {
d.displayX = d[xAxy];//reset the variable displayX
return x(d[xAxy]);
});
Now in the tool tip mouse hover use variable(displayX/displayY)
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", 1.0);
tooltip.html(d.displayY + ", " + d.displayX)//use displayX and displayY
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
working code here
Hope this helps!
I have 7 declared divs with different class attributes using jquery. I would like to use the 7 divs in mouseover. How would I do this?
My 7 divs are divOne, divTwo ... until divSeven.
I have this sample mouseover code but only one div is used.
nodeEnter.append("circle")
.attr("r", 30)
.style("stroke","white")
.on("mouseover", function(d) {
divOne.transition()
.duration(200)
.style("opacity", .9);
divOne.html(
"Name" + "<br />" + "Address"
)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
divOne.transition()
.duration(500)
.style("opacity", 0);
});
How to add the other divs during mouseover? Any help. Thanks
You need to have something like this.
nodeEnter.append("circle")
.attr("r", 30)
.style("stroke", "white")
.on("mouseover", function(d) {
onMouseOver();
})
.on("mouseout", function(d) {
onMouseOut();
});
var divElements = $('.classofdiv1', '.classofdiv2'......); //add all the div's class
function onMouseOver() {
$(divElements).each(function(index) {
$(this).transition()
.duration(200)
.style("opacity", .9);
$(this).html(
"Name" + "<br />" + "Address"
)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
});
}
function onMouseOut() {
$(divElements).each(function(index) {
$(this).transition()
.duration(500)
.style("opacity", 0);
});
}
Hope this helps :)
I am making a d3 graph and trying to put a border around my rect elements. The rect elements are appended to a cell and the text elements are appended to the same cell. Thus if I change the stroke in the rect I lose all the text for some reason, and if I change the stroke in the cell the borders and fonts change too.
This is a portion of my code for drawing the graph.
this.svg = d3.select("#body").append("div")
.attr("class", "chart")
.style("position", "relative")
.style("width", (this.w +this.marginTree.left+this.marginTree.right) + "px")
.style("height", (this.h + this.marginTree.top + this.marginTree.bottom) + "px")
.style("left", this.marginTree.left +"px")
.style("top", this.marginTree.top + "px")
.append("svg:svg")
.attr("width", this.w)
.attr("height", this.h)
.append("svg:g")
.attr("transform", "translate(.5,.5)");
this.node = this.root = this.nestedJson;
var nodes = this.treemap.nodes(this.root)
.filter(function(d) { return !d.children; });
this.tip = d3.tip()
.attr('class', 'd3-tip')
.html(function(d) {
return "<span style='color:white'>" + (d.name+",\n "+d.size) + "</span>";
})
this.svg.call(this.tip);
var cell = this.svg.selectAll("g")
.data(nodes)
.enter().append("svg:g")
.attr("class", "cell")
.call(this.position)
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", function(d) { return this.zoom(this.node == d.parent ? this.root : d.parent); })
.style("border",'black');
var borderPath = this.svg.append("rect")
.attr("x", this.marginTree.left)
.attr("y", this.marginTree.top)
.attr("height", this.h - this.marginTree.top - this.marginTree.bottom )
.attr("width", this.w - this.marginTree.left - this.marginTree.right)
.style("stroke", 'darkgrey')
.style("fill", "none")
.style("stroke-width", '3px');
cell.append("svg:rect")
.attr("id", function(d,i) { return "rect-" + (i+1); })
.attr("class","highlighting2")
.attr("title", function(d) {return (d.name+", "+d.size);})
.attr("data-original-title", function(d) {return (d.name+",\n "+d.size);})
.attr("width", function(d) { return d.dx - 1; })
.attr("height", function(d) { return d.dy ; })
.on('mouseover', this.tip.show)
.on('mouseout', this.tip.hide)
.style("fill", function(d) {return coloring(d.color);});
cell.append("svg:text")
.attr("class", "treemap-text nameTexts")
.attr("id", function(d,i) { return "name-" + (i+1); })
.attr("x", cellMargin)
.attr("y", function(d) { return parseInt($('.treemap-text').css('font-size'))+cellMargin; })
.text(function(d) {return (d.name);});
cell.append("svg:text")
.attr("class", "treemap-text sizeTexts")
.attr("id", function(d,i) { return "size-" + (i+1); })
.attr("x", cellMargin)
.attr("y", function(d) { return 2*parseInt($('.treemap-text').css('font-size'))+2*cellMargin; })
.text(function(d) {return (d.size);});
Additionally, I thought about creating lines and drawing four lines around each rect element, but was wondering if there is an easier way. Thanks.
I didn't check fully through your source, it would also be helpful to work with jsbin, codepen, jsfiddle or other online platforms to show your problem.
Actually I think you just have misinterpreted the SVG presentation attributes and their styling with CSS. For SVG elements only SVG presentation attributes are valid in CSS. This means there is no border property as you have it in your code. Also note that for <text> elements the fill color is the font-body color and the stroke is the outline of the font. Consider that stroke and fill are inherited down to child element which means that if you have a rectangle with a stroke style and some containing text element that they will have the stroke applied as outline and you'd need to override the styles there.
Hope you can solve your issue.
Cheers
Gion
I am wondering why doing the following is not possible:
div.text(function(d) {return " bought " + d.USD;})
It works fine for the either red or green circles earlier in the code:
.style("fill", function(d) {if (d.USD <= 0) {return "green"}
else { return "red" };})
Here is the extract of my code responsible for drawing the circles and adding a tooltip:
// Draw circle around values
svg.selectAll("dot")
.data(bitstamp_data)
.enter().append("circle")
.attr("r", function(d) { return Math.sqrt(Math.abs(d.USD)) - 5; })
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.Price); })
.style("fill-opacity", 0.7)
.attr("stroke", "black")
// Show different colour depending on buying or selling USD based on positive or negative d.USD
.style("fill", function(d) {
if (d.USD <= 0) {return "green"}
else { return "red" }
;})
// Tooltip section
.on("mouseover", function(d) {
div.transition()
.duration(400)
.style("opacity", .9);
div.text(function(d) {return " bought " + d.USD
;})
.style("left", (d3.event.pageX + 15) + "px")
.attr("stroke", "black")
.style("top", (d3.event.pageY - 28) + "px");
})