D3js - Force Layout. Selecting Specific Node - javascript

I am trying to select a specific node and do some animation on it.
This is how I try to select it:
var selectedToEnlarge = node.filter(function (d, i) {
return d.name == selectedVal;
});
This variable actually return an array containing my node. Please see attached image:
The node returned is the one I need.
Now I try to animate it a bit, like this:
selectedToEnlarge.transition().duration(2000)
.attr("stroke-width", 20)
.attr("r", 10)
.transition()
.duration(2000)
.attr('stroke-width', 0.5)
.attr("r", 200)
.ease('sine')
But nothing is happening. I am assuming it is something with selectors.
Any insight is appreaciated :)

Related

Adding a correctly sized rectangle behind text

I am currently appending text nodes to an SVG as follows:
var node = svg.selectAll(...);
node.append("text")
.attr("dy", 10)
.attr("dx", 4)
.text(function (d) { return d["name"]; });
There are currently about 10 nodes, each with a name.
I want to add a rectangle below each of the text nodes, using the correct width, I have tried this:
node.select<SVGTSpanElement>("text")
.each(function(x, i) {
console.log(this.getComputedTextLength());
node.append("rect")
.attr("fill", "#cccccc05")
.attr("width", this.getComputedTextLength())
.attr("height", 20)
});
My problem is that (kind of obviously) I am creating 10 rectangles per node, not one for each node.
How do I include the calculation for text width, and add a single rectangle per text element?
Without refactoring your code too much, simply change where you're appending the rectangles. In this case, the parent node of the texts themselves:
node.select<SVGTSpanElement>("text")
.each(function(x, i) {
console.log(this.getComputedTextLength());
d3.select(this.parentNode).append("rect")
.attr("fill", "#cccccc05")
.attr("width", this.getComputedTextLength())
.attr("height", 20)
});
However, the most idiomatic way is using each() for the node selection, not for the selection of texts inside it. Then, you get the text length for each node element, something like this:
node.each(function(x, i) {
d3.select(this).append("rect")
.attr("fill", "#cccccc05")
.attr("width", d3.select(this).select("text").node().getComputedTextLength())
.attr("height", 20)
});

Place text inside a circle. d3.js

I am trying to modify a code, and in the modifications that I have made, I have not been able to put a text in the middle of a circle. I've tried many things, and I've seen several examples but it does not work for me. How can I do it?
I know it should be done in this piece, and I add a text tag but it does not work.
bubbles.enter().append('circle')
.classed('bubble', true)
.attr('r', 0)
.attr('fill', function (d) { return fillColor(d.group); })
.attr('stroke', function (d) { return d3.rgb(fillColor(d.group)).darker();
})
.attr('stroke-width', 2)
.on('mouseover', function(){})
.on('mouseout', function(){});
http://plnkr.co/edit/2BCVxQ5n07Rd9GYIOz1c?p=preview
Create another selection for the texts:
var bubblesText = svg.selectAll('.bubbleText')
.data(nodes, function(d) {
return d.id;
});
bubblesText.enter().append('text')
.attr("text-anchor", "middle")
.classed('bubble', true)
.text(function(d) {
return d.name
})
And move them inside the tick function.
Here is the updated plunker: http://plnkr.co/edit/UgDjqNhzbvukTWU6J9Oy?p=preview
PS: This is a very generic answer, just showing you how to display the texts. This answer doesn't deal with details like size or transitions, which are out of the scope of the question and that you'll have to implement yourself.

D3: Surround mouse point with "Crosshair"

I'm trying to surround the mouse point with a circle, much like a crosshair, and have this circle track the mouses movement. So far the best strategy I have is using D3 enter-update-exit:
Append circle on mouse point underpinned by data.
on mouse move add another circle to data array with data = new mouse point.
if data array exceeds 1, shift() the first value out.
update visualisation.
jsfiddle here - http://jsfiddle.net/hiwilson1/kur2bbv9/1/ - though I think it's largely irrelevent as this strategy is fundamentally flawed. The circle appears as though it's lagging behind the cursor and flickers. A lot. Which I don't want.
Key part of code here:
function onMove() {
var m = d3.mouse(this);
var point = {x: m[0], y: m[1]};
area.push(point)
document.getElementById("svg").onmousedown = function() {
mouseDown++;
addNode(m);
};
document.getElementById("svg").onmouseup = function() {
mouseDown--;
};
if (mouseDown > 0) {
addNode(m);
}
//if theres two circles, remove the first leaving just the second.
if (area.length > 1) {
area.shift();
}
var crosshair = svg.selectAll(".area")
.data([area])
crosshair
.attr("class", "area")
.attr("cx", m[0])
.attr("cy", m[1])
.attr("fill", "none")
.attr("stroke", "grey")
.attr("stroke-width", "3px")
.attr("r", 30)
crosshair.enter()
.append("circle")
.attr("class", "area")
.attr("cx", m[0])
.attr("cy", m[1])
.attr("fill", "none")
.attr("stroke", "grey")
.attr("stroke-width", "3px")
.attr("r", 30)
crosshair.exit().remove()
};
Is there another way of accomplishing this? Happy to accept non D3 strategies.
I couldn't get your JSfiddle to display anything, so I'm not sure if I'm totally missing the point, but could you just use a custom CSS cursor on top of your SVG element? It seems that .cur cursor files have the most wide-spread support. That would be a native alternative for custom hacks (thus giving better performance), and it would also degradate gracefully on un-supported browsers.

d3js attach object to svg

I am creating a very long div containing hundreds of svg lines created by the following method:
function visualizeit(ORFdata,max) {
var browser = d3.select("#viewer")
.append("svg")
.attr("width", max/10)
.attr("height",'50%');
//Add svg to the svg container
for (orf in ORFdata) {
var line = browser.append("svg:line");
var object = ORFdata[orf]
line.datum(object)
line.attr("id", 'mygroup'+orf)
line.attr("x1", function(d){ return ORFdata[orf]["start"]/10})
line.attr("x2", function(d){ return ORFdata[orf]["stop"]/10})
line.attr("y1", function(d){ if (ORFdata[orf]["strand"] == "+1") {return 50} else {return 10}})
line.attr("y2", function(d){ if (ORFdata[orf]["strand"] == "+1") {return 50} else {return 10}})
line.style("stroke", "rgb(6,120,155)")
line.style("stroke-width", orf)
line.on('mouseover', function(d){console.log(d3.select("#mygroup"+orf).datum())})
}
}
However, when I do a mouseover on no matter what line I only get the data back from the last element. At first I thought it was due to 'mygroup' so I added a counter to it +orf but it somehow still erases my older stored data.
When I look in the created html code a svg seems correct by ID at least.
<line id="mygroup50" x1="103356.7" x2="103231.1" y1="10" y2="10" style="stroke: #06789b; stroke-width: 50px;"></line>
But somewhere the link goes awfully wrong...
How I fixed it so far...
var svgContainer = d3.select("body").append("svg")
.attr("width", max/10)
.attr("height", '50%');
//Add svg to the svg container
var lines = svgContainer.selectAll("line")
.data(ORFdata)
.enter()
.append("line")
.attr("x1", function(d){ return d.start/10})
.attr("y1", function(d){ if (d.strand == "+1") {return 65} else {return 10}})
.attr("x2", function(d){ return d.stop/10})
.attr("y2", function(d){ if (d.strand == "+1") {return 65} else {return 10}})
.attr("stroke-width","25")
.attr("stroke",function(d) {if (d.strand == "+1") {return 'green'} else {return 'red'}})
.on('mouseover', function(d) {console.log(d.start)})
}
You're creating a bunch of closures in a loop. Each of the functions you create have the variable orf in their closure scope but your loop is changing the value of orf. By the time the function runs when the mouse over event fires, orf has its final value so therefore your #mygroup + orf selection will always pick up the last element.
Here's a good page on closures that has a section detailing the pitfalls of closures in a loop: http://conceptf1.blogspot.ca/2013/11/javascript-closures.html.
In D3 you can get around this problem by using data joins instead of an external loop. Here's a good tutorial that should help to understand how this works:
http://bost.ocks.org/mike/join/
You need to create different event handlers for each line object, what I mean is store those line ojects them in an associated array or something. This way you are probably overwriting each time.
If you could provide a jsfiddle or something I would be happy to test this theory out for you...

d3.js, click to link to another URL encoded with variables

I made a scatter plot, and want to add a link to each dot.
chart.selectAll("scatter-dots")
.data(data)
.enter().append("circle")
.attr("cx", function (d) { return x(d.position[0]); } )
.attr("cy", function (d) { return y(d.position[1]); } )
.attr("r", 4)
.style("fill", "#666")
.style("opacity", 0.5)
.on("click", function(){
var url = "http://somelink.com/link.php?id=";
url += d.link_id;
//$(location).attr('href', url);
//window.location = url;
});
It works if I just put the pure String link such as
window.location = "http://stackoverflow.com"
However, if I add queries to the end of the URL from a variable, the page does not redirected.
Neither jquery nor javascript worked (as commented.)
I also tried an external js file, still fail.
This is in a PHP file, if this helps.
If it works with a static string then something is wrong with d.link_id. Try seeing what's inside either by doing alert(d.link_id) or if you use a firebug or similars do console.log(d.link_id).
Alternatively, you should use real anchors for linking your nodes instead of setting click events. This would go something like...
chart.selectAll("scatter-dots")
.data(data)
.enter()
.append("a")
.attr("xlink:href", function(d) {return "http://somelink.com/link.php?id=" + d.link_id})
.append("circle")
.attr("cx", function (d) { return x(d.position[0]); } )
.attr("cy", function (d) { return y(d.position[1]); } )
.attr("r", 4)
.style("fill", "#666")
.style("opacity", 0.5)
(I can't seem to recall if this is the exact way of doing it, you might need to save the anchors in a variable and then append the circles to them.)

Categories