general update pattern III, update values not showing up - d3.js

I am working on the modification of Mike Bostock's general update pattern III block and having a hard time understanding why, though the enter and exit values show up, the update values are not. I've read that assigning the specific value instead of using the data array value will help, as with a key, but this did not work. How do I modify this so entering values show up with their fill style, red color? I have read SO posts and re-read "How Selections Work" but still can't make it work.
Here is the code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
text {
font: bold 28px monospace;
}
.enter {
fill: green;
}
.update {
fill: red;
}
.exit {
fill: blue;
}
</style>
<body>
<script src="../d3.v3.js"></script>
<script>
function randomData() {
return Math.floor(Math.random() * 200);
}
var the_values = [];
function randomEntry() {
var numlist = [];
var randomEntry;
var maximum,minimum;
maximum = 10; minimum = 1
var random_in_range = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
var length_of_array = random_in_range;
console.log("length_of_array", length_of_array);
for (i = 0; i < length_of_array; i++) {
numlist.push([randomData(), randomData()]);
}
return numlist;
}
the_values = randomEntry();
console.log("the_values", the_values);
var width = 360,
height = 400;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(32," + (height / 2) + ")");
function update(data) {
// DATA JOIN
// Join new data with old elements, if any.
var text = svg.selectAll("text")
.data(data, function(d) {
return d;
})
.attr("transform", "translate(20," + (30) + ")");
var circles = svg.selectAll("circle")
.data(data, function(d) {
return d;
})
.attr("transform", "translate(20," + (30) + ")");
// UPDATE
// Update old elements as needed.
circles.attr("class", "update")
.transition()
.duration(750)
.attr("opacity", 0.3)
.attr("cx", function(d, i) {
return d[0];
})
.attr("cy", function(d, i) {
return d[1];
})
text.attr("class", "update")
.transition()
.duration(750)
.attr("x", function(d, i) {
return d[0];
})
.attr("y", function(d, i) {
return d[1];
})
// ENTER
// Create new elements as needed.
circles.enter().append("circle")
.attr("class", "enter")
.attr("opacity", 0.3)
.attr("r", 25)
.attr("cx", function(d, i) {
return d[0];
})
.attr("cy", function(d, i) {
return d[1];
})
.style("fill-opacity", 1e-6)
.transition()
.duration(750)
.attr("r", 30)
.style("fill-opacity", 1);
text.enter().append("text")
.attr("class", "enter")
.attr("dy", ".25em")
.attr("x", function(d) {
return d[0];
})
.attr("y", function(d) {
return d[1];
})
.style("fill-opacity", 1e-6)
.text(function(d) {
return d[0];
})
.transition()
.duration(750)
.style("fill-opacity", 1);
// EXIT
// Remove old elements as needed.
text.exit()
.attr("class", "exit")
.transition()
.duration(750)
.attr("y", 60)
.style("fill-opacity", 1e-6)
.remove();
circles.exit()
.attr("class", "exit")
.transition()
.duration(750)
.style("fill-opacity", 1e-6)
.remove();
}
// The initial display.
update(the_values);
// Grab a random sample of letters from the alphabet, in alphabetical order.
setInterval(function() {
update(randomEntry());
}, 1500);
</script>

From a quick glance at your code, it seems to be doing what you are looking for. Your enter circles are actually filled green, so you are actually seeing those. Updates are changed to red, but you don't see many of those because you are picking a few random numbers from 1-200. It's just unlikely that you will end up with any in the update selection, because that means that you selected the same number twice in a row.
To see some update circles, change:
return Math.floor(Math.random() * 200);
To:
return Math.floor(Math.random() * 10);
This throws the positions off, but you should soon see some red circles.

The reason is that in the update function you are always changing the whole array of input.
You are doing:
setInterval(function() {
update(randomEntry());//this will change the full array set
}, 1500);
This should have been:
setInterval(function() {
the_values.forEach(function(d){
//change the data set for update
})
update(the_values);
}, 1500);
Please note above i have not created a new array but I am passing the same array with changes to the update function.
Working fiddle here
Hope this helps!

Related

How to apply text transition to number in d3

I am new to d3. I created a bar chart. Append text and percentage in the bar chart with animation. When bar chart draw then the number and percentage go from bottom to top to the desired location. Here is the code
svg.selectAll(".bar")
.data(data)
.enter()
.append("g")
.attr("class", "g rect")
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.label); })
.attr("y", h)
.on("mouseover", onMouseOver) //Add listener for the mouseover event
... // attach other events
.transition()
.ease(d3.easeLinear)
.duration(2000)
.delay(function (d, i) {
return i * 50;
})
.attr("y", function(d) { return y(d.percentage.slice(0, -1)); })
.attr("width", x.bandwidth() - 15) // v4’s console.log(bands.bandwidth()) replaced v3’s console.log(bands.rangeband()).
.attr("height", function(d) { return h - y(d.percentage.slice(0, -1)); }) // use .slice to remove percentage sign at the end of number
.attr("fill", function(d) { return d.color; });
var legend = svg.append("g");
svg.selectAll(".g.rect").append("text")
.text(function(d) { return d.value })
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 15; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.attr("y", function(d) { return y(d.percentage.slice(0, -1) / 2);}) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.style("stroke", "papayawhip")
.style("fill", "papayawhip");
svg.selectAll(".g.rect").append("text")
.text(function(d) { return d.percentage; })
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 20; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.attr("y", function(d) { return y(d.percentage.slice(0, -1)) - 10; }) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.attr("fill", function(d) { return d.color; });
Now I want to apply text transition. Like instead of just printing say 90%(d.percentage). I want that it starts from 0 and goes to d.percentage gradually. How can I apply text transition in this case. I tried the following but it didn't work
svg.selectAll(".g.rect").append("text")
.text(function(d) { return d.percentage; })
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 20; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.tween("text", function(d) {
var i = d3.interpolate(0, d.percentage.slice(0, -1));
return function(t) {
d3.select(this).text(i(t));
};
})
.attr("y", function(d) { return y(d.percentage.slice(0, -1)) - 10; }) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.attr("fill", function(d) { return d.color; });
The problem is the this value.
Save it in the closure (that).
Use a number interpolator so you can round the result to a number of decimals.
var ptag = d3.select('body').append('p');
ptag.transition()
.ease(d3.easeLinear)
.duration(2000)
.tween("text", function(d) {
var that = this;
var i = d3.interpolate(0, 90); // Number(d.percentage.slice(0, -1))
return function(t) {
d3.select(that).text(i(t).toFixed(2));
};
})
<script src="https://d3js.org/d3.v5.min.js"></script>
Your problem is that you return function(t) { ... } and try to access this of parent function inside. The solution is to return arrow function, which does not shadow this value:
return t => d3.select(this).text(i(t));
(by the way, you may also want to round percentage before printing)
-------------Edit --------------
Here is the working code
var numberFormat = d3.format("d");
svg.selectAll(".g.rect").append("text")
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 20; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.tween("text", function(d) {
var element = d3.select(this);
var i = d3.interpolate(0, d.percentage.slice(0, -1));
return function(t) {
var percent = numberFormat(i(t));
element.text(percent + "%");
};
//return t => element.text(format(i(t)));
})
.attr("y", function(d) { return y(d.percentage.slice(0, -1)) - 10; }) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.attr("fill", function(d) { return d.color; });
Thanks :)

How to fix this toggle function in d3?

I have 2 problems here:
1/ These buttons (you can run the code snippet below) should reverse back to their former opacity at the second click but they don't ! Can someone explain me what's wrong with the code ?
2/ How do I reverse back to the text former color on the second click without having to define that former color (again!) in the toggle function. ?
Explanation: As you can see when running the following snippet, the text (1,2,3...) turns white at the first click. I'd like to set it back to its former color at the second click without having to write:
var newTextFill = active ? "white" : "**former_color**";
For instance, in this example the author doesn't specify the font-size the legend should return to on a second click and yet you can see it shrinking back to its original size. How can I do the same thing here with the font colors ?
var name = ["123456789"];
var color = d3.scaleOrdinal()
.range(["#00baff", "#0014fe", "#00dbaf", "#f4bf02", "#ffa600", "#ff0000", "#ff00c4", "#ee693e", "#99958f"]);
var svg = d3.select("body")
.append("svg")
.attr("width", 300)
.attr("height",300)
var legende = svg.selectAll("bar")
.data(name)
.enter()
.append("g")
.attr("x", 0)
.attr("y", function (d,i) {return 12 + i * 20})
legende.append("text")
.text(function(d,i) { return name[i]})
.attr("class", "legtext")
.style("font-family", "helvetica")
.style("fill-opacity", 0.8)
.attr("x", 4)
.attr("y", function (d,i) {return 12 + i * 20})
.style("font-size", 10)
.style("fill", function(d,i) { return color(i)})
.style("text-decoration", "none")
.style("opacity", 1);
legende.append("rect")
.attr("class", "recta")
.attr("x", 2)
.attr("y", function (d,i) {return 1 + i * 20})
.attr("width", 65)
.attr("height", 15)
.style("fill", function(d,i) { return color(i)})
.style("opacity", 0.09);
legende.append("rect")
.attr("class", "rectastroke")
.attr("id", function(d,i) {return "str" + i})
.attr("x", 2)
.attr("y", function (d,i) {return 1 + i * 20})
.attr("width", 65)
.attr("height", 15)
.style("stroke", function(d,i) { return color(i)})
.style("stroke-width", 0.5)
.style("fill", "none")
.attr("display", "none");
legende.on("click", function (d,i)
{var active = d.active ? false : true;
var newOpacity = active ? 1 : 0.1;
var newTextFill = active ? "white" : "blue";
d3.select(this)
.select("rect")
.style("opacity", newOpacity)
d3.select(this)
.select("text")
.style("fill", newTextFill)
.raise()
d.active = active });
legende.on("mouseover", function(d, i)
{d3.select(this).selectAll(".rectastroke").attr("display", "true")});
legende.on("mouseout", function(d, i)
{d3.select(this).selectAll(".rectastroke").attr("display", "none")});
body {font-family: 'Open Sans', sans-serif;
font-size: 11px;
font-weight: 300;
fill: #242424;
text-align: center;
cursor: default;}
.legende {cursor: pointer;}
.recta {cursor: pointer;}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div class="colored buttons"></div>
</body>
</html>
In the bl.ocks you linked she is, in fact, specifying the font size the legend will return to on a second click... in the CSS.
What's happening is that if active is false, the setter will return undefined...
d3.select(this)
.style("font-size", function() {
if (active) {return "25px"}
})
... and the UA will set the style based on the CSS.
You can do the same thing to your rectangles without specifying any newOpacity:
d3.select(this)
.select("rect")
.style("opacity", function() {
if (active) return 1;
})
Here is the demo:
var data = "123456789".split("").map(function(d) {
return {
value: d
}
});
var color = d3.scaleOrdinal()
.range(["#00baff", "#0014fe", "#00dbaf", "#f4bf02", "#ffa600", "#ff0000", "#ff00c4", "#ee693e", "#99958f"]);
var svg = d3.select("body")
.append("svg")
.attr("width", 300)
.attr("height", 300)
var legende = svg.selectAll("bar")
.data(data)
.enter()
.append("g")
.attr("x", 0)
.attr("y", function(d, i) {
return 12 + i * 20
})
legende.append("text")
.text(function(d, i) {
return d.value
})
.attr("class", "legtext")
.style("font-family", "helvetica")
.style("fill-opacity", 0.8)
.attr("x", 4)
.attr("y", function(d, i) {
return 12 + i * 20
})
.style("font-size", 10)
.style("fill", function(d, i) {
return color(i)
})
.style("text-decoration", "none")
.style("opacity", 1);
legende.append("rect")
.attr("class", "recta")
.attr("x", 2)
.attr("y", function(d, i) {
return 1 + i * 20
})
.attr("width", 65)
.attr("height", 15)
.style("fill", function(d, i) {
return color(i)
});
legende.on("click", function(d, i) {
var active = d.active ? false : true;
d3.select(this)
.select("rect")
.style("opacity", function() {
if (active) return 1;
})
d3.select(this)
.select("text")
.style("fill", function(e, j) {
if (active) {
return "white";
} else {
return color(i)
}
})
.raise()
d.active = active
});
.legende {
cursor: pointer;
}
.recta {
opacity: 0.1;
cursor: pointer;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
For the text color, however, since you are not specifying it in the CSS, you'll have to use a normal if...else code.

Unable to change x position of legend text inside a group element using D3

I am trying to create a horizontal graph legend in D3.js. I am using a group element (g) as a container for all the legends and the individual legends (text) are also each wrapped inside a "g" element. The result is that the individual legends are stacked on top of each other rather than spaced out.
I have tried changing the x attribute on the legends and also transform/translate. Whilst the DOM shows that the x values are applied the legends don't move. So if the DOM shows the legend / g element is positioned at x = 200 it is still positioned at 0.
I have spent two days trying to solve this and probably looked at over 50 examples including anything I could find on StackExchange.
Below code is my latest attempt. It doesn't through any error and the x values are reflected in the DOM but the elements just won't move.
I have included the code covering the relevant bits (but not all code).
The legend container is added here:
/*<note>Add container to hold legends. */
var LegendCanvas = d3.select("svg")
.append("g")
.attr("class", "legend canvas")
.attr("x", 0)
.attr("y", 0)
.attr("width", Width)
.style("fill", "#ffcccc")
.attr("transform", "translate(0,15)");
There is then a loop through a json array of objects:
var PrevElemLength = 0;
/*<note>Loop through each data series, call the Valueline variable and plot the line. */
Data.forEach(function(Key, i) {
/*<note>Add the metric line(s). */
Svg.append("path")
.attr("class", "line")
.attr("data-legend",function() { return Key.OriginId })
/*<note>Iterates through the data series objects and applies a different color to each line. */
.style("stroke", function () {
return Key.color = Color(Key.UniqueName); })
.attr("d", Valueline(Key.DataValues));
/*<note>Add a g element to the legend container. */
var Legend = LegendCanvas.append("g")
.attr("class", "legend container")
.attr("transform", function (d, i) {
if (i === 0) {
return "translate(0,0)"
} else {
PrevElemLength += this.previousElementSibling.getBBox().width;
return "translate(" + (PrevElemLength) + ",0)"
}
});
/*<note>Adds a rectangle to pre-fix each legend. */
Legend.append("rect")
.attr("width", 5)
.attr("height", 5)
.style("fill", function () {
return Key.color = Color(Key.UniqueName); });
/*<note>Adds the legend text. */
Legend.append("text")
.attr("x", function() {
return this.parentNode.getBBox().width + 5;
})
/*.attr("y", NetHeight + (Margin.bottom/2)+ 10) */
.attr("class", "legend text")
.style("fill", function () {
return Key.color = Color(Key.UniqueName); })
.text(Key.UniqueName);
Here is a screen shot of what the output looks like:
enter image description here
Any help on how to create a horizontal legend (without over lapping legends) would be much appreciated. Chris
The problem is you are using local variables d and i as function parameters while setting the transform attribute. Parameter i in local scope overrides the actual variable. The value of local variable i would be always zero as there is no data bind to that element.
var Legend = LegendCanvas.append("g")
.attr("class", "legend container")
.attr("transform", function (d, i) { //Remove i
if (i === 0) {
return "translate(0,0)"
} else {
PrevElemLength += this.previousElementSibling.getBBox().width;
return "translate(" + (PrevElemLength) + ",0)"
}
});
I have also made slight updates to the code for improvements.
var LegendCanvas = d3.select("svg")
.append("g")
.attr("class", "legend canvas")
.attr("x", 0)
.attr("y", 0)
.attr("width", 500)
.style("fill", "#ffcccc")
.attr("transform", "translate(0,15)");
var PrevElemLength = 0;
var Data = [{
OriginId: 1,
UniqueName: "Some Long Text 1"
}, {
OriginId: 2,
UniqueName: "Some Long Text 2"
}];
/*<note>Loop through each data series, call the Valueline variable and plot the line. */
var Color = d3.scale.category10();
Data.forEach(function(Key, i) {
/*<note>Add a g element to the legend container. */
var Legend = LegendCanvas.append("g")
.attr("class", "legend container")
.attr("transform", function() {
if (i === 0) {
return "translate(0,0)"
} else {
var marginLeft = 5;
PrevElemLength += this.previousElementSibling.getBBox().width;
return "translate(" + (PrevElemLength + marginLeft) + ",0)"
}
});
/*<note>Adds a rectangle to pre-fix each legend. */
Legend.append("rect")
.attr("width", 5)
.attr("height", 5)
.style("fill", function() {
return Key.color = Color(Key.UniqueName);
});
/*<note>Adds the legend text. */
Legend.append("text")
.attr("x", function() {
return this.parentNode.getBBox().width + 5;
})
.attr("dy", "0.4em")
.attr("class", "legend text")
.style("fill", function() {
return Key.color = Color(Key.UniqueName);
})
.text(Key.UniqueName);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg height=500 width=500></svg>
The d3 way of implementation(Using data binding) would be as follows
var LegendCanvas = d3.select("svg")
.append("g")
.attr("class", "legend canvas")
.attr("x", 0)
.attr("y", 0)
.attr("width", 500)
.style("fill", "#ffcccc")
.attr("transform", "translate(0,15)");
var Data = [{
OriginId: 1,
UniqueName: "Some Long Text 1"
}, {
OriginId: 2,
UniqueName: "Some Long Text 2"
}];
var Color = d3.scale.category10();
var Legend = LegendCanvas.selectAll(".legend")
.data(Data)
.enter()
.append("g")
.attr("class", "legend container");
Legend.append("rect")
.attr("width", 5)
.attr("height", 5)
.style("fill", function(Key) {
return Key.color = Color(Key.UniqueName);
});
Legend.append("text")
.attr("x", function() {
return this.parentNode.getBBox().width + 5;
})
.attr("dy", "0.4em")
.attr("class", "legend text")
.style("fill", function(Key) {
return Key.color = Color(Key.UniqueName);
})
.text(function(Key){ return Key.UniqueName });
var PrevElemLength = 0;
Legend.attr("transform", function(d, i) {
if (i === 0) {
return "translate(0,0)"
} else {
var marginLeft = 5;
PrevElemLength += this.previousElementSibling.getBBox().width;
return "translate(" + (PrevElemLength + marginLeft) + ",0)"
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width=500 height=500></svg>
Try this :
//Legend
var legend = vis.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("image")
.attr("x", 890)
.attr("y", 70)
.attr("width", 20)
.attr("height", 18)
.attr("xlink:href",function (d) {
return "../assets/images/dev/"+d+".png";
})
legend.append("text")
.attr("x", 910)
.attr("y", 78)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d) {
return d;
});

D3: Get html element associated with node?

I'm working on a map (found here), that is using the svg viewbox attribute to scale with the size of the client.
Unfortunately the project I'm using, d3.geoAlbersUsa() does not seem to scale the tooltip correctly with the rest of the SVG. As in, it suddenly places the tooltip in the same spot it would be if the client width had been the original 960x500 specs.
Here's the tooltip code:
d3.tsv("CitiesTraveledTo.tsv",cityVisited, function(data) {
var cities = svg.selectAll(".city")
.data(data)
.enter()
.append("g")
.classed("city",true);
cities.append("line")
.attr("x1", function(d) {
return projection([d.Longitude, d.Latitude])[0];
})
.attr("x2", function(d) {
return projection([d.Longitude, d.Latitude])[0];
})
.attr("y1", function(d) {
return projection([d.Longitude, d.Latitude])[1]-pinLength;
})
.attr("y2", function(d) {
return (projection([d.Longitude, d.Latitude])[1]);
})
.attr("stroke-width",function(d) {
return 2;
})
.attr("stroke",function(d) {
return "grey";
});
cities.append("circle")
.attr("cx", function(d) {
return projection([d.Longitude, d.Latitude])[0];
})
.attr("cy", function(d) {
return projection([d.Longitude, d.Latitude])[1]-pinLength;
})
.attr("r", function(d) {
return 3;
})
.style("fill", function(d) {
if (d.Reason === "Work") {
return "rgb(214, 69, 65)";
}
else if (d.Reason === "Fun") {
return "rgb(245, 215, 110)";
}
else {
return "rgb(214, 69, 65)";
}
})
.style("opacity", 1.0)
// 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) {
div.transition()
.duration(200)
.style("opacity", .9);
div.text(d.City + ", " + d.State)
.style("left", function() {
var centerCircle = (projection([d.Longitude, d.Latitude])[0]);
return (centerCircle-26) + "px";
})
.style("top", function() {
var centerCircle = projection([d.Longitude, d.Latitude])[1];
var circleRadius = 3;
return ( centerCircle - circleRadius - 33-pinLength) + "px";
});
div.append("div").attr("class","arrow-down");
})
// fade out tooltip on mouse out
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
I thought that the scaling should just happen automatically for the tooltip as well. Wrong. I then tried to reset the height and width passed to the projection and that didn't work. What's the best way to get the element bound to a data "d" node?
I ask because it will likely be easier to say "for this node, get me this element, give me the bound html element", so that I can set the position of the tooltip relative to the new position of the bound element.

modifying MB's General Update Pattern III to reposition text

I would like to reuse the general update pattern III for a project and
want to know how to make the text labels line up better with the circle elements. My experiment is to attach circle elements and text to the "g", but I cannot place the text labels correctly.
Here is how I modified the block:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
text {
font: bold 28px monospace;
}
.enter {
fill: green;
}
.update {
//fill: #333;
fill: red;
}
.exit {
//fill: brown;
fill: blue;
}
</style>
<body>
<script src="../d3.v3.js"></script>
<script>
function randomData(){
return d3.range(~~(Math.random()*50)+1).map(function(d, i){return ~~(Math.random()*100);});
}
var alphabet = "";
var numlist = [];
var randomEntry;
for (i = 0; i< 2; i++) {
randomEntry = randomData();
numlist.push( randomEntry);
}
var temp = numlist.toString();
var temp2 = temp.split('"');
alphabet = temp2.pop();
console.log("alphabet", alphabet);
var temp3 = alphabet.toString();
console.log("temp3", temp3);
console.log("temp3 type", typeof(temp3));
var temp4 = alphabet.split(",");
alphabet = temp4;
console.log("alphabet", alphabet);
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(32," + (height / 2) + ")");
function update(data) {
// DATA JOIN
// Join new data with old elements, if any.
var text = svg.selectAll("text")
.data(data, function(d) { return d; });
var circles = svg.selectAll("circle")
.data(data, function(d) { return d; });
// UPDATE
// Update old elements as needed.
circles.attr("class", "update")
.transition()
.duration(750)
.attr("opacity", 0.3)
.attr("cx", function(d,i) { return (Math.random(i))*100;})
.attr("cy", function(d,i) { return (Math.random(i))*100;})
.attr("transform", "translate(200," + (-100) + ")");
text.attr("class", "update")
.transition()
.duration(750)
.attr("x", function(d,i) { return (Math.random(i))*100; })
.attr("y", function(d,i) { return (Math.random(i))*100; })
.attr("transform", "translate(200," + (-100) + ")");
// ENTER
// Create new elements as needed.
circles.enter().append("circle")
.attr("class", "enter")
.attr("opacity", 0.3)
.attr("r", 25)
.attr("cy", function(d,i) { return (Math.random(i))*270;})
.attr("cx", function(d,i) { return (Math.random(i))*270;})
.style("fill-opacity", 1e-6)
.transition()
.duration(750)
.attr("r", 30)
.style("fill-opacity", 1);
text.enter().append("text")
.attr("class", "enter")
.attr("dy", ".25em")
.attr("x", function(d) { return (Math.random(i))*100; })
.attr("y", function(d) { return (Math.random(i))*100; })
.style("fill-opacity", 1e-6)
.text(function(d) { return d; })
.transition()
.duration(750)
.style("fill-opacity", 1);
// EXIT
// Remove old elements as needed.
text.exit()
.attr("class", "exit")
.transition()
.duration(750)
.attr("y", 60)
.style("fill-opacity", 1e-6)
.remove();
circles.exit()
.attr("class", "exit")
.transition()
.duration(750)
.style("fill-opacity", 1e-6)
.remove();
}
// The initial display.
update(alphabet);
// Grab a random sample of letters from the alphabet, in alphabetical order.
setInterval(function() {
update(shuffle(alphabet)
.slice(0, Math.floor(Math.random() * 26))
.sort());
}, 1500);
// Shuffles the input array.
function shuffle(array) {
var m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m], array[m] = array[i], array[i] = t;
}
return array;
}
</script>
How can I change this so the text labels appear next to the circle elements? Thanks for any assistance.
You seem to making a random data for determining circle DOM's cx and cy of the circle:
.attr("cy", function(d,i) { return (Math.random(i))*270;})
.attr("cx", function(d,i) { return (Math.random(i))*270;})
In text DOM you make random points for determining x and y of text.
.attr("x", function(d) { return (Math.random(i))*100; })
.attr("y", function(d) { return (Math.random(i))*100; })
So as a fix you can have a common data which will decide the x/y for text and cx/cy for circle.
BY making a function like this:
function randomData() {
return (Math.random() * 500);//his generates a single random point
}
var alphabet = [];
function randomEntry() {
var numlist = [];
var randomEntry;
for (i = 0; i < 5; i++) {
//generate 5 random coordinate
//here first element willdecide the x and second element decide the y.
numlist.push([randomData(), randomData()]);
}
//this will contain 5 coordinate points.
return numlist;
}
Then set the 5 coordinates point data in the text and circel like this:
var text = svg.selectAll("text")
.data(data, function(d) {
return d;
});
var circles = svg.selectAll("circle")
.data(data, function(d) {
return d;
});
Then in the update
circles.attr("class", "update")
.transition()
.duration(750)
.attr("opacity", 0.3)
.attr("cx", function(d, i) {
return d[0];//here d[0] is the x coordinate which determine the circle center x
})
.attr("cy", function(d, i) {
return d[1];//here d[1] is the y coordinate which determine the circle center y
})
text.attr("class", "update")
.transition()
.duration(750)
.attr("x", function(d, i) {
return d[0];
})
.attr("y", function(d, i) {
return d[1];
})
Working code here
Hope this helps!

Resources