Replace old content d3 - javascript

I am creating a div container which I then fill with a svg
var someContainer = d3.select("#abc")
.append("div")
.classed("svg-container", true)
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox","0 0 400 100")
.classed("svg-content-responsive", true)
.selectAll("circle")
.data(someScale.range());
I then append my data to it
someContainer.remove();
someContainer.enter()
.append("circle")
.attr("x", function (d, i) {
return i * 2;
})
.attr("y", 5)
.attr("height", 15)
....;
However, whenever I update the content of the svg, i.e. append new circles, a completely new div-container and svg-container gets created. That leaves me with the old data visually staying in its place and right on the bottom (meaning, 100px further down) there is the new data. Its basically a visual copy, but with the new data... Whenever I udpate my data, a new coy gets replaced under the old one, leaving me with n graphics.
Here is the css that styles the relative container and makes sure, it scales when the window size is changed. Source: Resize svg when window is resized in d3.js */
.svg-container {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 50%; /* aspect ratio */
vertical-align: top;
overflow: hidden;
}
.svg-content-responsive {
display: inline-block;
position: absolute;
top: 10px;
left: 0;
}
Any ideas what I am doing wrong?

If you just need to delete all the old circles you can do it as follows:
someContainer.selectAll("circle").remove()
And then add new circles by data -> enter -> append sequence.
someContainer.selectAll("circle")
.data(new_circle_data)
.enter()
.append("circle")
.attr("x", function (d, i) {
return i * 2;
})
.attr("y", 5)
.attr("height", 15)
....;
If you only want to delete some of the existing circles and keep the others you can use the general update pattern. You need to do something like this:
var circleUpdate = someContainer.selectAll("circle")
.data(new_circle_data)
circleUpdate.enter().append("circle")
.attr("x", function (d, i) {
return i * 2;
})
.attr("y", 5)
.attr("height", 15)
....;
circleUpdate.exit().remove()

The problem is that each time the data gets updated, a new div gets created.
var someContainer = d3.select("#abc")
.append("div")
.classed("svg-container", true)
...
In order to update the data, the div needs to be replaced instead of creating a new div each time the data changes.
That can be done by adding this line
d3.select(".svg-container").remove();
above the var someContainer = d3.select...

Related

Vertically centering a circle on a responsive D3 svg

I am trying to draw some circles on an a responsive svg.
Here is the code for the svg:
const width = window.innerWidth;
const circleWidth = width / 2;
let h = 700;
const svgBackground = d3.select("#container")
.append("svg")
.attr("viewBox", `0 0 ${width} ${h}`)
.classed("svg-content", true)
.on("mouseleave", function () {
d3.selectAll("circle.neo")
.style("stroke", ringColour);
d3.select("div#container")
.selectAll("p")
.remove();
})
It scales responsively but I can't figure out how to draw the circles so they are centered vertically
let height = svgBackground.style("height");
height = height.slice(0, (height.length - 2));
const halfHeight = height / 2;
let circles = svgBackground.selectAll("circle.neo")
.data(radius)
.enter()
.append("circle")
.attr("class", "neo")
let circleAttributes = circles
.attr("cx", circleWidth)
.attr("cy", halfHeight)
.attr("r", function (d) { return d })
.style("stroke", ringColour)
.style("fill", "none")
If anyone has any tips for how to do this, I would appreciate it. Here's the full code on js fiddle http://jsfiddle.net/ncbtdk8m/1/
I think your problem is that a part of the bottom of your SVG is not visible because .svg-container has a overflow:hidden property. This makes it appear like it was not in the center.
If you remove that CSS property and configure your height correctly things start to look different: http://jsfiddle.net/9dprfwj1/
Part of the problem is the styling of the div#container and the svg.svg-content.
No need to use the position and the display attributes.
You don't set a width and height to the svg and you don't have a resize handler, so how can you be responsive.
Why add a new center circle each time you use the sliders.
You only get 80 stars with radius 0.5.

D3 align text on left side of the axis from the right most character

So I have a D3 bar chart that looks like: bar chart. The problem appears for the smaller data bars when they overlap over the right side of the axis. I want to align the data text along the left side of the graph axis (as shown with the larger numbers) regardless of how small the bar is.
var data = [200.000001,3.00001,300.00001,1.00001,400.00001,5.0001,100.00001,20.0001,40.0001,50.00001, 2.00001];
//bar chart
var bars = d3.select("#chart").append("div").attr("class","chartstyle");
var b = bars.selectAll("div")
.data(data)
.enter().append("div")
.style("width", function(d) { return d + "px"; })
.text(function(d) { return d; });
});
//from stylesheet
<style>
.chartstyle div {
font: 10px;
background-color: red;
text-align: right;
padding: 0px;
margin: 0px;
color: black;
}
</style>
You'd better use svg:text elements with a text-anchor property set to end (see spec) instead of mixing nodes from different namespaces.
var gBars = d3.select("#chart")
.selectAll(".bar-container")
.data(data)
.enter().append("g")
.attr("class", "bar-container");
// bars
gbars.append("rect")
.attr("class", "bar")
[...] // bars positioning and sizing logic
// labels
gBars.append("text")
.attr("class", "bar-label")
.attr("text-anchor", "end")
[...] // same positioning logic
.text(Object);

Making svg container appear one below another on array looping

I have the following code where I created two svg container of different height and width and it is created for every element in the array. The code works well but I want the svg container text1 which contains title1 to appear below the svg container text2 that displays the title2 rather than side by side that's how it appears now, i.e., next to each other. How to make container 2 to appear just below the container 1
Here is the code
function draw(data) {
data.forEach(function(d) {
var text1 = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 100);
var title1 = d.values.title1;
text1.append("text")
.attr("x", "50%")
.attr("y", "10%")
.text(title1);
var text2 = d3.select("body").append("svg")
.attr("width", 300)
.attr("height", 500);
var title2 = d.values.title2;
text2.append("text")
.attr("x", "40%")
.attr("y", "10%")
.text(title2);
});
}
You probably can solve your problems just changing your CSS. By default, SVGs will display side by side, if there is enough room in the page. In this snippet, 5 SVGs are produced (click "run code snippet"):
var data = d3.range(5);
var body = d3.select("body");
var svg = body.selectAll("svg")
.data(data)
.enter()
.append("svg")
.attr("width", 100)
.attr("height", 100);
svg {
background-color: teal;
margin-right: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
This is exactly the same code, but setting the display in the CSS:
display: block;
Check the difference (click "run code snippet"):
var data = d3.range(5);
var body = d3.select("body");
var svg = body.selectAll("svg")
.data(data)
.enter()
.append("svg")
.attr("width", 100)
.attr("height", 100);
svg {
background-color: teal;
display: block;
margin-bottom: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

D3 js tooltip issue for Choropleth Map

i am new to D3.js. I have been trying to add tooltip to the existing d3 chloropleth Map by Michelle Chandra. However i am unable to make any progress, the tooltip doesnt seem to appear. Where am i doing wrong? Any Help will be appreciated. http://bl.ocks.org/michellechandra/0b2ce4923dc9b5809922.
Thanks
<style type="text/css">
/* On mouse hover, lighten state color */
path:hover {
fill-opacity: .7;
}
/* Style for Custom Tooltip */
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: white;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
/* Legend Font Style */
body {
font: 11px sans-serif;
}
/* Legend Position Style */
.legend {
position:absolute;
left:800px;
top:350px;
}
</style>
</head>
<body>
<script type="text/javascript">
/* This visualization was made possible by modifying code provided by:
Scott Murray, Choropleth example from "Interactive Data Visualization for the Web"
https://github.com/alignedleft/d3-book/blob/master/chapter_12/05_choropleth.html
Malcolm Maclean, tooltips example tutorial
http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html
Mike Bostock, Pie Chart Legend
http://bl.ocks.org/mbostock/3888852 */
//Width and height of map
var w = 900;
var h = 600;
// D3 Projection
var projection = d3.geo.albersUsa()
.translate([w/2, h/2]) // translate to center of screen
.scale([1000]); // scale things down so see entire US
// Define path generator
var path = d3.geo.path() // path generator that will convert GeoJSON to SVG paths
.projection(projection); // tell path generator to use albersUsa projection
// Define linear scale for output
var color = d3.scale.linear()
.range(["rgb(24,143,95)","rgb(51,188,196)","rgb(155,226,183)","rgb(217,91,67)"]);
var legendText = ["Cities Lived", "States Lived", "States Visited", "States Not Visited Yet"];
//Create SVG element and append map to the SVG
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Append Div for tooltip to SVG
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Load in my states data!
d3.csv("stateslived.csv", function(data) {
color.domain([0,1,2,3]); // setting the range of the input data
// Load GeoJSON data and merge with states data
d3.json("us-states.json", function(json) {
// Loop through each state data value in the .csv file
for (var i = 0; i < data.length; i++) {
// Grab State Name
var dataState = data[i].state;
// Grab data value
var dataValue = data[i].visited;
// Find the corresponding state inside the GeoJSON
for (var j = 0; j < json.features.length; j++) {
var jsonState = json.features[j].properties.name;
if (dataState == jsonState) {
// Copy the data value into the JSON
json.features[j].properties.visited = dataValue;
// Stop looking through the JSON
break;
}
}
}
// Bind the data to the SVG and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke", "#fff")
.style("stroke-width", "1")
.style("fill", function(d) {
// Get data value
var value = d.properties.visited;
if (value) {
//If value exists…
return color(value);
} else {
//If value is undefined…
return "rgb(213,222,217)";
}
});
// Map the cities I have lived in!
d3.csv("cities-lived.csv", function(data) {
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("r", function(d) {
return Math.sqrt(d.years) * 4;
})
.style("fill", "rgb(217,91,67)")
.style("opacity", 0.85)
// add browser tooltip of city name
//.append("title")
//.text(function(d) {
// return d.place;
//});
// Modification of custom tooltip code provided by Malcolm Maclean, "D3 Tips and Tricks"
// http://www.d3noob.org/2013/01/adding-tooltips-to-d3js-graph.html
.on("mouseover", function(d) {
d3.select(this).transition().duration(300).style("opacity", 1);
div.transition().duration(200)
.style("opacity", .9);
div.text(d.properties.visited)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
// fade out tooltip on mouse out
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
});
// Modified Legend Code from Mike Bostock: http://bl.ocks.org/mbostock/3888852
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", 140)
.attr("height", 200)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.data(legendText)
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
});
});
/* This code generate paths without mapping to other data
// Load JSON file and generate path for each state
d3.json("us-states.json", function(json) { // file path, callback function called when data loaded
svg.selectAll("path") // creates empty references to all the paths
.data(json.features) // loop through our data (the states in the array) and bind to paths
.enter() // create placeholder to reference the new elements
.append("path") // add to the DOM!
.attr("d", path) // generate paths for each state
.style("fill", "steelblue"); // make the states blue!
}); */
</script>
I'm not sure what this statement is supposed to do:
d3.select(this).transition().duration(300).style("opacity", 1);
It appears to select the window object.
One would need to look at your json file to see if you're getting the right data into the div.
Shameless plug. Take a look at foxToolTip.js. I think its easier and more flexible than d3tip.
https://github.com/MichaelRFox/foxToolTip.js
Once div is a div, you have to use html, not text:
div.transition().duration(200)
.style("opacity", .9);
div.html(d.properties.visited)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");

3d.js (Force) - get current mouse position on the link for the tooltip

I am trying to enable the tooltip when the mouse is on the line. This is the current setup that I have:
HTML:
<div id="graphContainer"></div>
<div id='hoveringTooltip' style='position:fixed;'></div>
3d.js Code - Basic Setup:
var width = 1200,
height = 900;
var svg = d3.select("#graphContainer").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.charge(-120)
.linkDistance(80)
.size([width, height]);
//Create all the line svgs but without locations yet
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function (link) {
return link.thick
})
.attr("data-info", function (link) {
return link.info;
})
.style("marker-end", "url(#suit)")
.on("mouseover", mouseOverLink)
function mouseOverLink (e) {
//d3.select(this).style("stroke","red");
d3.select(this).attr("class", "link_selected");
var that = this;
var value = Number( this.attributes.x1.value );
var xx = d3.select(this).attr("cx") + "px"
var yy = d3.select(this).attr("cy") + "px"
var xxx = d3.event.pageX;
var yyy = d3.event.pageY;
var coordinates = [0, 0];
coordinates = d3.mouse(this);
var x = coordinates[0];
var y = coordinates[1];
var value = this.attributes[1].value;
$('#hoveringTooltip').show();
$('#hoveringTooltip').html(value);
$('#hoveringTooltip').css({
"top": xxx,
"left": yyy
});
}
In the mouseOverLink function, I have tried all scenarios that I could find on SO and on internet. I do get the values for the X/Y mouse, but they are always wrong. I also tried to attach mouseover link using Jquery eventing, but those values are also wrong.
If there is another way to display the toolitip on the link, I would be more then happy.
Since you did not provide a working fiddle so I have made a force-directed Plunk to explain the solution.
First give style for the tool tip div like this:
div.tooltip {
position: absolute;// because we are going to give the position.
text-align: center;
width: 160px;
height: 50px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
Next make a div and append it to the body like this:
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);//so that its not visible initially
Now on link mouse over/mouse out do
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.on("mouseover", function (d, i) {//show tooltip
tooltip.transition()
.duration(200)
.style("opacity", 0.9);
tooltip.html("<p>source:"+d.source.name+ "</p><p>target:"+d.target.name+ "</p>")//tool tip html
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function (d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);//hde tooltip
})
Working example here.
Hover over the link to see the tool tip.
My first thought is if you're using xxx and yyy in a css style rule, you need to add "px" to the end of the values like you do for xx and yy

Categories

Resources