I trying to add a simple tooltip to my scatterplot, the thing is that it shows below the graph, I don't know if it is for the "div", but I've tried with svg and g and still won't show.
Is there any other way I can add my tooltip?
const tooltip = d3.select("#CraftBeer")
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "1px")
.style("border-radius", "5px")
.style("padding", "10px")
const mouseover = function(d) {
tooltip
.style("opacity", 1)
}
const mousemove = function(d) {
tooltip
.html(d.beer + "<br/>" + "ABV:" + d.abv + "%" + "<br/>" + "IBU:" + d.ibu +"<br/>" + "SRM:" + d.srm)
.style("left", (d3.event.pageX+10)+"px") // It is important to put the +90: other wise the tooltip is exactly where the point is an it creates a weird effect
.style("top", (d3.event.pageY-10)+"px")
}
const mouseleave = function(d) {
tooltip
.transition()
.duration(300)
.style("opacity", 0)
}
plot = g.selectAll("bottle")
.data(data)
.enter()
.append("g")
.attr("transform", d => `translate(${xScale(d.abv)},${yScale(d.ibu)})`)
.style("fill", d => colors(d.srm))
.style("stroke", d => colors(d.srm))
.style("stroke-width", "13px")
plot.append("g")
.attr("transform", `scale(0.150) translate(-256,-256)`)
.selectAll()
.data(bottlePath)
.enter()
.append("path")
.attr("d", d => d)
plot.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave)
Try putting the style for your tooltip before the html like
const mousemove = function(d) {
tooltip
.style("left", (d3.event.pageX+10)+"px") // It is important to put the +90: other wise the tooltip is exactly where the point is an it creates a weird effect
.style("top", (d3.event.pageY-10)+"px")
.html(d.beer + "<br/>" + "ABV:" + d.abv + "%" + "<br/>" + "IBU:" + d.ibu +"<br/>" + "SRM:" + d.srm)
}
Related
When I hover over the data point it keeps the information for that data point locked in the left portion of the screen. How do I get it to be where the users mouse is? Here's what I have
var tooltip = d3.select("body")
.append("div")
.attr("id", "tooltip")
.attr("style", "position: absolute; opacity: 0;")
.style("border", "solid")
.style("border-width", "1px")
.style("border-radius", "5px")
.style("padding", "10px");````
...
.on("mouseover", function(d){
d3.select("#tooltip")
.transition()
.duration(200)
.style("opacity", .9)
.text("User: " + d.user + " " +
"Top Artist: " + " " + d.artist + " " +
"Top Song: " + d.track)
d3.select(this)
.transition()
.duration(100)
.attr("r", 23)
.attr("fill", "orange");
})
.on("mouseout", function(d) {
d3.select("#tooltip")
.style("opacity", 0)
d3.select(this)
.transition()
.duration(100)
.attr("r", 15)
.attr("fill", "black");
});
You can use d3.event.pageX and d3.event.pageY built into D3. When you're adding your tooltip, try this:
d3.select("#tooltip")
.transition()
.duration(200)
.style("opacity", .9)
.text("User: " + d.user + " " +
"Top Artist: " + " " + d.artist + " " +
"Top Song: " + d.track)
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY - 28 + "px");
You may need to adjust the top style based on the height of your tooltip.
I'm trying to color the circles per a .csv data, column "COLOR". The "COLOR" includes "red", "yellow" , "green" -but right now the colors are not transferring... just the default black...
var col = function(d) {return d.COLOR};
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 15)
.attr("cx", xMap)
.attr("cy", yMap)
.style("fill", col)
.style("opacity", ".5")
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d["Cereal Name"] + "<br/> (" + xValue(d)
+ ", " + yValue(d) + ")")
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
data.forEach(function(d) {
d.POPULATION = +d.POPULATION;
d.REVENUE = +d.REVENUE;
d.COLOR = +d.COLOR;
Your COLOR values in your CSV contain quotation marks " which will be part of the parsed strings in data. Therefore, you end up with attribute values like fill=""yellow"" which is not valid. Hence, the black default color.
One way around this might be to get rid of the quotation marks in the CSV itself. If this is not feasible, you might adjust your color accessor function col to something like the following:
var col = function(d) {return d.COLOR.substring(1, d.COLOR.length - 1)};
Have a look at this working demo:
var data = [{ COLOR: '"yellow"'}];
var col = function(d) { return d.COLOR.substring(1, d.COLOR.length - 1); };
d3.select("body").append("svg")
.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 10)
.attr("fill", col);
<script src="https://d3js.org/d3.v4.js"></script>
I am trying to plot a network graph using networkD3 in R. I wanted to make some changes to the display so that the text labels (which appears when mouseover) can be easily read.
Please refer to the link here for an example. Note: Jump to the d3ForceNetwork plot.
As seen in the example, the labels are hard to read due to its colour and it often gets obstructed by the surrounding nodes. I have been messing around with the JS file and managed to change the text label color to black. However, having no knowledge of JS or CSS (I can't even tell the difference between the 2 actually), I have no idea how I can change the stack order such that the text labels will always be displayed above any other objects.
Can anyone advise me on how I can achieve the desired outcome?
Below is the full JS file:
HTMLWidgets.widget({
name: "forceNetwork",
type: "output",
initialize: function(el, width, height) {
d3.select(el).append("svg")
.attr("width", width)
.attr("height", height);
return d3.layout.force();
},
resize: function(el, width, height, force) {
d3.select(el).select("svg")
.attr("width", width)
.attr("height", height);
force.size([width, height]).resume();
},
renderValue: function(el, x, force) {
// Compute the node radius using the javascript math expression specified
function nodeSize(d) {
if(options.nodesize){
return eval(options.radiusCalculation);
}else{
return 6}
}
// alias options
var options = x.options;
// convert links and nodes data frames to d3 friendly format
var links = HTMLWidgets.dataframeToD3(x.links);
var nodes = HTMLWidgets.dataframeToD3(x.nodes);
// get the width and height
var width = el.offsetWidth;
var height = el.offsetHeight;
var color = eval(options.colourScale);
// set this up even if zoom = F
var zoom = d3.behavior.zoom();
// create d3 force layout
force
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(options.linkDistance)
.charge(options.charge)
.on("tick", tick)
.start();
// thanks http://plnkr.co/edit/cxLlvIlmo1Y6vJyPs6N9?p=preview
// http://stackoverflow.com/questions/22924253/adding-pan-zoom-to-d3js-force-directed
var drag = force.drag()
.on("dragstart", dragstart)
// allow force drag to work with pan/zoom drag
function dragstart(d) {
d3.event.sourceEvent.preventDefault();
d3.event.sourceEvent.stopPropagation();
}
// select the svg element and remove existing children
var svg = d3.select(el).select("svg");
svg.selectAll("*").remove();
// add two g layers; the first will be zoom target if zoom = T
// fine to have two g layers even if zoom = F
svg = svg
.append("g").attr("class","zoom-layer")
.append("g")
// add zooming if requested
if (options.zoom) {
function redraw() {
d3.select(el).select(".zoom-layer").attr("transform",
"translate(" + d3.event.translate + ")"+
" scale(" + d3.event.scale + ")");
}
zoom.on("zoom", redraw)
d3.select(el).select("svg")
.attr("pointer-events", "all")
.call(zoom);
} else {
zoom.on("zoom", null);
}
// draw links
var link = svg.selectAll(".link")
.data(force.links())
.enter().append("line")
.attr("class", "link")
.style("stroke", function(d) { return d.colour ; })
//.style("stroke", options.linkColour)
.style("opacity", options.opacity)
.style("stroke-width", eval("(" + options.linkWidth + ")"))
.on("mouseover", function(d) {
d3.select(this)
.style("opacity", 1);
})
.on("mouseout", function(d) {
d3.select(this)
.style("opacity", options.opacity);
});
// draw nodes
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.style("fill", function(d) { return color(d.group); })
.style("opacity", options.opacity)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", click)
.call(force.drag);
node.append("circle")
.attr("r", function(d){return nodeSize(d);})
.style("stroke", "#fff")
.style("opacity", options.opacity)
.style("stroke-width", "1.5px");
node.append("svg:text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name })
.style("font", options.fontSize + "px " + options.fontFamily)
.style("opacity", options.opacityNoHover)
.style("pointer-events", "none");
function tick() {
node.attr("transform", function(d) {
if(options.bounded){ // adds bounding box
d.x = Math.max(nodeSize(d), Math.min(width - nodeSize(d), d.x));
d.y = Math.max(nodeSize(d), Math.min(height - nodeSize(d), d.y));
}
return "translate(" + d.x + "," + d.y + ")"});
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
}
function mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", function(d){return nodeSize(d)+5;});
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 13)
.style("stroke-width", ".5px")
.style("font", options.clickTextSize + "px ")
.style('fill', 'black')
.style('position','relative')
.style("opacity", 1);
}
function mouseout() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", function(d){return nodeSize(d);});
d3.select(this).select("text").transition()
.duration(1250)
.attr("x", 0)
.style("font", options.fontSize + "px ")
.style("opacity", options.opacityNoHover);
}
function click(d) {
return eval(options.clickAction)
}
// add legend option
if(options.legend){
var legendRectSize = 18;
var legendSpacing = 4;
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = legendRectSize;
var vert = i * height+4;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.style('fill', 'darkOrange')
.text(function(d) { return d; });
}
// make font-family consistent across all elements
d3.select(el).selectAll('text').style('font-family', options.fontFamily);
},
});
I suspect I need to make some changes to the code over here:
function mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", function(d){return nodeSize(d)+5;});
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 13)
.style("stroke-width", ".5px")
.style("font", options.clickTextSize + "px ")
.style('fill', 'black')
.style("opacity", 1);
}
You need to resort the node groups holding the circles and text so the currently mouseover'ed one is the last in that group, and thus the last one drawn so it appears on top of the others. See the first answer here -->
Updating SVG Element Z-Index With D3
In your case, if your data doesn't have an id field you may have to use 'name' instead as below (adapted to use the mouseover function you've got):
function mouseover(d) {
d3.selectAll("g.node").sort(function (a, b) {
if (a.name != d.name) return -1; // a is not the hovered element, send "a" to the back
else return 1; // a is the hovered element, bring "a" to the front (by making it last)
});
// your code continues
The pain might be that you have to do this edit for every d3 graph generated by this R script, unless you can edit the R code/package itself. (or you could suggest it to the package author as an enhancement.)
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've got this linechart, on each value in the chart I am placing a dot.
when hovering over the dot I would like to show the value using a d3-tip tooltip.
Here is what I got so far:
var svg = chart.append("svg")
.attr("width", outerWidth)
.attr("height", outerHeight)
.append("g")
.attr("transform", "translate(0,10)");
newData.graphSeries.forEach(function (current, index, all) {
//current = this exact part of the array
//index = the array index nr [0][1][2] etc
//all = the complete array
var graph = current.Values,
color = current.Color;
var nextLine = d3.svg.line()
.interpolate(current.Interpolate)
.x(function (d) {
return x(d.x);
})
.y(function (d) {
return y(d.y);
});
svg.append("path")
.datum(graph)
.attr("transform", "translate(" + yAxisWidth + ",0)")
.attr("class", "line stroke-" + color)
.attr("d", nextLine);
//Placing tooltips
if (current.Tooltips == true) {
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong> TEST: " + newData.y.Unit + "</strong><span>" + d.x + "</span>"
});
//create circles to place the tooltip on
svg.selectAll('dot')
.data(graph)
.enter().append("circle")
.attr("r", 3,5)
.attr("style", "cursor: pointer")
.attr("class", "circle-" + color)
.attr("transform", "translate(" + yAxisWidth + ",0)")
.attr("cx", function(d) { return x(d.x) })
.attr("cy", function(d) { return y(d.y) })
.on('mouseover', tip.show )
.on('mouseout', tip.hide);
svg.call(tip);
}
});
I checked if d3-tip exists in the code and it does.
I can console.log the tip variable, also the dots are showing and even the mouseover and mouseout are working correctly.
Still somehow tip.show doesn't seem to work.
I thought maybe it would show somewhere else in the document, but can't see it anywhere.
Could you please help out.
Best,
Bart
The problem was actually easier to solve then expected.
The tooltip might be 'pushed' away by all other html code.
Adding .style('z-index', '99999999999'); will help to get that straight.
See:
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.style('z-index', '99999999')
.html(function(d) {
return "<strong> TEST: " + newData.y.Unit + "</strong><span>" + d.x + "</span>"
});