display text inside circle on mousehover using d3.js - javascript

I have been struggling past 2 days to achieve below requirements.
Circle is behaving in abnormal and indeterminate way.
Requirements
1.Text should come inside the circle when circle is full hovered.
2.When circle is hovered and you take mouse cursor on text,the circle and text should not flicker.
3.Invisible circle should come back to its previous radius.
Here is the code
d3.select("#outerG")
.insert("g", "#invisibleG").attr("class", "maincircles").append("circle")
.attr("cx",120)
.attr("cy", 120)
.attr("r", 30)
.attr("class", "circle")
.style('fill', '#19b2e8');
d3.select("#invisibleG").append("g")
.attr("class","invisibleCircle")
.append("circle")
.attr("cx",120)
.attr("cy",120)
.attr("r",30)
.attr("class","inviCircle")
.style('fill-opacity', '0')
.attr("prevRadius",30)
.attr("xcoords",120)
.attr("ycoords",120)
.on("mouseover",function(){
var r=d3.select(this).attr("r");
d3.select(this).style('fill','#19b2e8')
.style('fill-opacity','1')
.transition()
.duration(1000)
.attr("r",50);
var prevRadius=d3.select(this).attr("prevRadius");
var xcoord= d3.select(this).attr("xcoords");//to centre text
var ycoord= d3.select(this).attr("ycoords");//to centre text
d3.select(this.parentNode).append("text")
.transition()
.duration(1000)
.text("Hello")
.attr("x",120)
.attr("y",120)
.style("font-family","sans-serif")
.style("font-size","20px")
.style("text-anchor","middle");
})
.on("mouseout",function(){
d3.select(this.parentNode).select("text").remove();
//d3.select(this).interrupt();
var r=d3.select(this).attr("prevRadius");
d3.select(this)
.transition()
.duration(1000)
.attr("r",r).attr("stroke-width",0)
.style('fill-opacity','0');
});
here is the HTML
<svg width="300" height="300">
<g id="outerG">
<g id="invisibleG"></g>
</g>
</svg>
Check fiddle http://jsfiddle.net/razC9/5/

Problem is that text is messing up mouse events on mouseover and solution is to add this css
svg text {
pointer-events: none;
}
Here is how it looks like now - http://jsfiddle.net/razC9/7/

Based on your requirements, here is a working fiddle. It is done with 1 circle, instead of two, drying up the code some. EDIT: Timing set to flash text once radius is fully expanded:
http://jsfiddle.net/razC9/42/
Key snippet:
.on("mouseover", function(){
d3.select(this)
.transition().attr("r", 50).duration(750)
.style("fill", "#19b2e8")
.style('fill-opacity','1');
d3.select("#outerG") .append("text")
.transition().delay(750)
.attr("class", "text")
.text("Hello")
.attr("x", 100)
.attr("y", 120)
.attr("fill", "black");
})
.on("mouseout", function(){
d3.select(this)
.transition().attr("r", 20)
.style("fill", "#19b2e8");
d3.select("#outerG").select(".text").remove();
});

Related

Why does this text "text 1" appended on the rectangular not render? [duplicate]

I'm looking to append html onto a rectangle in D3 to give me a multiple line tooltip. The bottom part is how I'm adding a rectangle which may be part of the problem. The top is the code that should work in my world.
newRect.().html(" <textArea font-family=Verdana font-size=20 fill=blue > Test " + "</br>" + "Test2 </textArea>");
Which does insert a text field into the SVG, it just doesn't display:
HTML:
<rect id="rectLabel" x="490" y="674" width="130" height="160" fill="red">
<textarea fill="blue" font-size="20" font-family="Verdana"> Test </br>Test2 </textarea>
</rect>
I have a mouse over function which runs the following:
newRect = svg.append("rect")
.attr("x", xCor)
.attr("y", yCor)
.attr("width", 130)
.attr("height", 160)
.attr("fill", "red")
.attr("id", "rectLabel");
I think I should be doing this but it doesn't work. It just removes the g.node that I'm trying to append to.
newRect = $(this).enter().append("rect")
.attr("x", xCor)
.attr("y", yCor)
.attr("width", 130)
.attr("height", 160)
.attr("fill", "red")
.attr("id", "rectLabel");
Question:
Why doesn't my text appear? Ive tried .html, .textArea. I want a multiple line label so I don't think .text will work correct? Also, how should I be appending the rectangle?
A rect can't contain a text element. Instead transform a g element with the location of text and rectangle, then append both the rectangle and the text to it:
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("rect")
.attr("width", x)
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d; });
http://bl.ocks.org/mbostock/7341714
Multi-line labels are also a little tricky, you might want to check out this wrap function.
Have you tried the SVG text element?
.append("text").text(function(d, i) { return d[whichevernode];})
rect element doesn't permit text element inside of it. It only allows descriptive elements (<desc>, <metadata>, <title>) and animation elements (<animate>, <animatecolor>, <animatemotion>, <animatetransform>, <mpath>, <set>)
Append the text element as a sibling and work on positioning.
UPDATE
Using g grouping, how about something like this? fiddle
You can certainly move the logic to a CSS class you can append to, remove from the group (this.parentNode)

d3 Choropleth map with hover effect does not display tooltip

I am trying to add a tooltip to the d3 Choropleth map with hover effect from this page https://www.d3-graph-gallery.com/graph/choropleth_hover_effect.html
The tooltip seems to be appendend to the svg, as I can inspect the map and see an inner div with the suppossed text that should appear when hovering the countries, but when doing so, nothing shows up, even though the tooltip parameters seem to be updated on each different hover.
I've taken the tooltip from another graph as the current map I am trying to implement does not have tooltip. I've tried creating the tooltip variable within function ready(error, topo)and the styles/data inside the mouseover and mouseleave functions but it does not display the tooltip over the countries
Here is the coode in my xhtml
<head>
<!-- Load d3.js and the geo projection plugin -->
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script>
jQuery(document).ready(function() {
// The svg
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
// Map and projection
var path = d3.geoPath();
var projection = d3.geoMercator()
.scale(70)
.center([0,20])
.translate([width / 2, height / 2]);
// Data and color scale
var data = d3.map();
var colorScale = d3.scaleThreshold()
.domain([100000, 1000000, 10000000, 30000000, 100000000, 500000000])
.range(d3.schemeBlues[7]);
// Load external data and boot
d3.queue()
.defer(d3.json, "https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson")
.defer(d3.csv, "https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world_population.csv", function(d) { data.set(d.code, +d.pop); })
.await(ready);
var tooltip = d3.select("#my_dataviz")
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px")
function ready(error, topo) {
let mouseOver = function(d) {
d3.selectAll(".Country")
.transition()
.duration(200)
.style("opacity", .5)
d3.select(this)
.transition()
.duration(200)
.style("opacity", 1)
.style("stroke", "black")
tooltip.style("opacity", 1)
.html("The exact value of<br/>this cell is: " + d.pop)
.style("left", (d3.mouse(this)[0]+70) + "px")
.style("top", (d3.mouse(this)[1]) + "px")
}
let mouseLeave = function(d) {
d3.selectAll(".Country")
.transition()
.duration(200)
.style("opacity", .8)
d3.select(this)
.transition()
.duration(200)
.style("stroke", "transparent")
tooltip.style("opacity", 0)
}
// Draw the map
svg.append("g")
.selectAll("path")
.data(topo.features)
.enter()
.append("path")
// draw each country
.attr("d", d3.geoPath()
.projection(projection)
)
// set the color of each country
.attr("fill", function (d) {
d.total = data.get(d.id) || 0;
return colorScale(d.total);
})
.style("stroke", "transparent")
.attr("class", function(d){ return "Country" } )
.style("opacity", .8)
.on("mouseover", mouseOver )
.on("mouseleave", mouseLeave )
}
})
</script>
</head>
<div>
<div>
<h1>Graphs</h1>
<!-- Create an element where the map will take place -->
<svg id="my_dataviz" width="400" height="300"></svg>
</div>
</div>
After rendering the map (working with just hovering function) I inspect it and can see the following.
If I change the country, the data on the tooltip div varies.
--UPDATE--
Following #enxaneta 's advice, I tried taking the div outside svg. The result is having the tooltip in a separate div. This time the tooltip appears in the page but is displayed at the bottom of the map, not over it, how I would like to see it.
I am still figuring out how to implement foreignObject.
--UPDATE 2--
Adding tooltip position absolute toke the div and put it relative to the mouse hovering. However, even though it is vertically aligned with the country selected, it is way up at the top of the page.
<style>
.tooltip{position:absolute;}
</style>

d3js drag a text from html DOM and add it to SVG on drop

As I am learning d3js I am trying to make a small program where there are (planets in solar system) as texts in the HTML DOM and there are ellipses(rings) around a circle (sun). I need to be able to drag the text and as they are dropped it should identify on which ellipse and append into the group. For now, it does not need to check if the location is correct just want to append a circle when the text gets dropped into the circle.
I looked at some Jquery UI and other samples, but I am not having much luck on this issue.
var svg = d3.select('body')
.append("svg")
.attr("id", "program")
.attr("height", 500)
.attr("width", 500);
var sun = svg.append("circle")
.attr("cx", 250)
.attr("cy", 250)
.attr("r", 25)
.attr("fill", "orange");
var mercg = svg
.append("g")
.attr("id", "mercury")
.append("ellipse")
.attr("class", "droppable")
.attr("cx", 250)
.attr("cy", 250)
.attr("rx", 55)
.attr("ry", 45)
.attr("fill", "none")
.attr("stroke", "black");
var venus = svg
.append("g")
.attr("id", "venus")
.append("ellipse")
.attr("class", "droppable")
.attr("cx", 250)
.attr("cy", 250)
.attr("rx", 85)
.attr("ry", 65)
.attr("fill", "none")
.attr("stroke", "black");
var drag = d3.behavior.drag()
.on("drag", dragged)
.on("dragend", dragend);
function dragged(d) {
d3.select(this).attr("cx", d3.event.x).attr("cy", d3.event.y);
}
function dragend(d) {
// Here, How do i find on what ring the item was dropped?
// I want the circle to be on the selected ring group
}
var ex = svg.append("circle")
.attr("transform", 'translate(0,0)')
.attr("cx", 279)
.attr("cy", 212)
.attr("r", 10)
.attr("fill", "blue")
.call(drag);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="mySidenav" class="sidenav">
<ul>
<li draggable="true" ondragstart="drag()" class="draggable"> Mercury</li>
<li draggable="true" class="draggable">Venus </li>
</ul>
</div>
In the end I am looking for something that looks like the image produced at the end.
My solution
You can drag-n-drop html to svg and find drop target. Now you can add if statement to detect if target is ellipse. Also you can add additional ellipse to each existing ellipses with opacity 0 and wider stroke-width parameter to avoid pixel hunting.
update: new fiddle you can drag-n-drop HTML elements to svg orbits and new circle will append (if you drop planet name on its orbit)

D3 - How can I show/hide a text element when hovering a circle element created from different attributes of the same data entry?

I have some data with 2 attributes: colour and value
I use the D3 enter selection to create circle elements, and append them to the body of the page. Their fill colour is determined by the "colour" attribute.
Then, I append text elements to the page. The text contents are determined by the "value" attribute.
Here is what I am working with:
// Set up svg element
var svg = d3.select("body")
.append("svg")
.attr("width", 300)
.attr("height", 300)
.style("background", "lightblue");
var dataset = [
{"colour":"red", "value":"First set of text"},
{"colour":"green", "value":"Second attempt"},
{"colour":"blue", "value":"Third and final!"}
];
// Create circles from the data
// On mouseover, give them a border (remove on mouseout)
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("r", 40)
.attr("cy", function(d, i) { return i*80 + 40; })
.attr("cx", 50)
.style("fill", function(d) {return d.colour;})
// HERE
// Can I somehow show and hide the text component that is
// associated with this circle when the circle is hovered, rather
// than the text itself?
.on("mouseover", function(d) {
d3.select(this).style("stroke", "black")
.style("stroke-width", 2)
})
.on("mouseout", function(d) {d3.select(this).style("stroke", "none")});
// Now add the text for each circle
// Same thing with mouseover and mouseout
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.attr("text-anchor", "middle")
.attr("y", function(d, i) { return i*80 + 40; })
.attr("x", 50)
.style("opacity", 0)
.on("mouseover", function(d) {d3.select(this).style("opacity", 1)})
.on("mouseout", function(d) {d3.select(this).style("opacity", 0)})
.text(function(d) { return d.value;});
I would like for the text to be hidden, until the associated circle is hovered over. How can I connect the text element with a particular circle, so that I can toggle whether the text is shown by hovering over the circle?
This fiddle below is an outline of what I am trying to do, and what I have got so far. I have the text showing up only when hovered, but not when the circle is hovered.
https://jsfiddle.net/aj4zpn6z/
There are several ways for achieving this. Since both circles and texts use the same dataset, my solution uses filter.
First, let's name the variables for the texts and circles:
var circles = svg.selectAll("circle")
//etc...
var texts = svg.selectAll("text")
//etc...
Then, inside the circles mouseover function, we filter the texts that have the same colour attribute:
.on("mouseover", function(d){
d3.select(this).style("stroke", "black").style("stroke-width", 2);
var tempTexts = texts.filter(function(e){
return e.colour === d.colour
});
tempTexts.style("opacity", 1);
});
This is your updated fiddle: https://jsfiddle.net/wxh95e9u/

D3 Appending Text to a SVG Rectangle

I'm looking to append html onto a rectangle in D3 to give me a multiple line tooltip. The bottom part is how I'm adding a rectangle which may be part of the problem. The top is the code that should work in my world.
newRect.().html(" <textArea font-family=Verdana font-size=20 fill=blue > Test " + "</br>" + "Test2 </textArea>");
Which does insert a text field into the SVG, it just doesn't display:
HTML:
<rect id="rectLabel" x="490" y="674" width="130" height="160" fill="red">
<textarea fill="blue" font-size="20" font-family="Verdana"> Test </br>Test2 </textarea>
</rect>
I have a mouse over function which runs the following:
newRect = svg.append("rect")
.attr("x", xCor)
.attr("y", yCor)
.attr("width", 130)
.attr("height", 160)
.attr("fill", "red")
.attr("id", "rectLabel");
I think I should be doing this but it doesn't work. It just removes the g.node that I'm trying to append to.
newRect = $(this).enter().append("rect")
.attr("x", xCor)
.attr("y", yCor)
.attr("width", 130)
.attr("height", 160)
.attr("fill", "red")
.attr("id", "rectLabel");
Question:
Why doesn't my text appear? Ive tried .html, .textArea. I want a multiple line label so I don't think .text will work correct? Also, how should I be appending the rectangle?
A rect can't contain a text element. Instead transform a g element with the location of text and rectangle, then append both the rectangle and the text to it:
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("rect")
.attr("width", x)
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d; });
http://bl.ocks.org/mbostock/7341714
Multi-line labels are also a little tricky, you might want to check out this wrap function.
Have you tried the SVG text element?
.append("text").text(function(d, i) { return d[whichevernode];})
rect element doesn't permit text element inside of it. It only allows descriptive elements (<desc>, <metadata>, <title>) and animation elements (<animate>, <animatecolor>, <animatemotion>, <animatetransform>, <mpath>, <set>)
Append the text element as a sibling and work on positioning.
UPDATE
Using g grouping, how about something like this? fiddle
You can certainly move the logic to a CSS class you can append to, remove from the group (this.parentNode)

Categories