I am building a force-directed graph, and as far as I can tell, I have written the code correctly. The DOM looks exactly right after this code runs. And yet, nothing is displayed.
var type_node_list = typeNodes(data);
shuffle(type_node_list);
initializePosition(type_node_list);
var links = typeLinks(type_node_list);
var svg = d3.select('#root');
var link = svg
.append('g')
.attr('id', 'links')
.selectAll('line')
.data(links)
.enter()
.append('line')
.attr('class', 'link');
var type_nodes = svg
.append('g')
.attr('id', 'nodes')
.selectAll('.node')
.data(type_node_list)
.enter()
.append(createTypeNode);
function updateTypeNodeLocations() {
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;});
type_nodes
.attr('x', nodeX)
.attr('y', nodeY);
}
updateTypeNodeLocations();
/*
var position_the_types = d3.forceSimulation()
.nodes( type_node_list )
.force("charge",d3.forceManyBody().strength(-10))
;
position_the_types.on('tick',updateTypeNodeLocations());
*/
The simulation portion is commented out because I'm trying to get the first part working. When I uncomment it, it only calls the 'tick' event once, even though the processing is clearly not complete. And there is nothing in the JavaScript console to explain it.
See http://jsfiddle.net/jarrowwx/gof5knaj/36/ for the full code.
I had things working this morning, and something changed and now nothing I do seems to work. I checked the D3 github, and the last commit appears to have been 11 days ago, so it's not likely caused by a change to the library.
Has anybody experienced something like this before? Any pointers on what I'm doing wrong? Have I uncovered a D3 bug?
The problem lie in my function createTypeNode.
I created the image element via: document.createElement('image'). That doesn't work. To create an image via JavaScript, one must use Namespaces.
See: Programmatically creating an SVG image element with javascript
Related
I have a very strange behave in my angularjs application.
1) I'm using d3js to draw a nice bar chart
graphG.append("rect")
.attr("id", function (d, i) {
return "bar_" + i;
})
.attr("x", 0)
.attr("y", 0)
.attr("height", barHeight - 1)
.attr("width", function (d, i) {
return rectWidth(d, i);
})
.attr("fill", rectColor);
2) After a data update there is a transition
barG.select("#bar_" + i)
.transition()
.duration(duration)
.attr("fill", rectColor(d))
.attr("width", rectWidth(d, i));
So far the width transition works fine. The color transitions with gradient doesn't work, but this is an another question. It changes the color but without smooth transition.
If i reroute to another page and come back the rect will not be rendered any more. The rect is in the dom. Width attribute got changes. No bar.
After F5 reload every thing is fine again.
And more strange behave: it happens in Chrome, it works fine in Firefox.
Anybody some ideas?
I have found the problem:
the error was that the gradient id (return of rectColor function) was same in both directives. So it seems that there is some caching in Chrome.
Rename the gradient id in one directive fixes the issue.
I would like to colour different links of a network graph according to a value within a data vector (where each element of the vector corresponds to a particular link). I have tried something of the form:
var data = [1,1,1,2];
var link = svg.selectAll(".link")
.data(force.links())
.enter()
.append("line")
.attr("class", "link")
.attr("marker-end", "url(#end)")
.on("mouseover", linkmouseover)
.on("mouseout", linkmouseout);
link.data(force.links()).enter().append(".link")
.style("stroke", function(d,i){
return ( (data[i]==2) ?
"red" : "black" )
});
However, this does not seem to work. If it's important I also have an svg styling:
.link {
stroke: #ff0000;
opacity: 0.6;
stroke-width: 1.5px;
}
Does anyone know why this isn't working? I have the Plunker version here.
The way you are setting up your links looks quite odd. Especially the following line doesn't seem to make any sense:
link.data(force.links()).enter().append(".link")
The statement directly preceding this line successfully inserts the lines for the links handling the enter selection. On the suspicious line you are then binding the same data again to the links' selection. Because you did not specify any key function to access your data this will compute a join based on the data's index which will, because you handled this same data before, yield an empty enter selection. Therefore, the method setting style("stroke",...) will never execute the callback. Furthermore, the call to .append(".link") is wrong, because .link is not a valid SVG element which could be appended using this method.
You somehow seem to have messed up this part of your code by, maybe, some copy&paste action. If you get rid of the above mentioned line and delete the directly preceding semicolon your code will work as expected:
var link = svg.selectAll(".link")
.data(force.links())
.enter()
.append("line")
.attr("class", "link")
.attr("marker-end", "url(#end)")
.on("mouseover", linkmouseover)
.on("mouseout", linkmouseout)
.style("stroke", function(d,i){
return ( (data[i]==2) ? "red" : "black" );
});
Have a look at the updated code for a working example.
I'm trying to open a new window on button click and draw a simple circle on it.
I can open a window but circle won't appear on it with this code. Any ideas on how to solve this.0
Here is my code:
function drawBarChart(newWindowRoot){
var sampleSVG = d3.select(newWindowRoot)
.append("svg")
.attr("width", 100)
.attr("height", 100);
sampleSVG.append("circle")
.style("stroke", "gray")
.style("fill", "white")
.attr("r", 40)
.attr("cx", 50)
.attr("cy", 50)
.on("mouseover", function(){d3.select(this).style("fill", "aliceblue");})
.on("mouseout", function(){d3.select(this).style("fill", "white");});
}
function createNewPage(){
var button = content.append("button")
.attr("class", "button")
.attr("id", "myButton")
.text("Visualize");
document.getElementById("myButton").onclick = function () {
var newWindow = window.open('');
newWindowRoot = d3.select(newWindow.document.body)
.attr("width","1060px")
.attr("margin","50px auto");
drawBarChart(newWindowRoot);
}
}
When you call drawBarChart(newWindowRoot), you're passing a d3 selection — the result of d3.select(newWindow.document.body) — into that function. That makes sense.
However, from within drawBarChart, you call var sampleSVG = d3.select(newWindowRoot), essentially making a d3 selection of a d3 selection. That doesn't work (unlike what you might expect from jQuery). You should skip that latter d3.select and that'll make your code begin to work. More specifically, that'll make the button appear in the new window, but not the circle.
To make the circle appear, you need to fix another issue: Your fiddle attempts to add the circle to the button, instead of to an SVG (this is different than your example above which isn't trying to create a button).
Here's a working fiddle
By the way, the reason the jsFiddle you linked to didn't work is because the link you embedded was using secure protocol (https) and the jsfiddle functionality that was trying to load d3 is insecure (http), which resulted in a failure to load d3 and in turn an error executing your code.
I am creating a streamgraph, having used the example code from http://bl.ocks.org/mbostock/4060954 as a template.
I draw the streamgraph:
var svg = d3.select("#graph_area").append("svg")
.attr("width", width)
.attr("height", height)
....
svg.selectAll("path")
.data(layers)
.enter().append("path")
.attr("d", function (d) {return area(d.layer);})
Now it seems that I am not allowed to choose a different name from "path" for the DOM element here. If I do, then streamgraph no longer plots. Why is that?
Then I want to add a legend with bullets, but they don't plot. The elements show up in my web inspector (firebug), but their graphical representation is just not there. I figured it might be a similar problem with the DOM element name, but I don't actually know. Here is the code for my bullets:
//draw all the legend bullets and effects
svg.selectAll("bullets")
.data(layers)
.enter().append("bullets")
.attr("cx", 200)
.attr("cy", function(d, i) { return 20 + i*10; })
.style("fill", function(d) { return d.color; })
.attr("r", 5)
I briefly looked at API's for paths here and here, but I didn't find my answers there.
The element names you can use are defined in the SVG specification. This is a specific set (e.g. path, circle) -- using anything else will work in terms of appending the element to the DOM, but the browser won't know how to interpret and render it.
It's the same way in HTML -- there's a specific set of defined element names that browsers know how to render.
when i do this :
var link = svg.selectAll('.link')
.data(links)
.enter().append('path')
.attr('class', 'link')
.attr('d', diagonal)
There is no node with the .link class. So selectAll returns en empty selection. But i've found that, when you call this for the first time, you can selectAll('whaterverYouWant')
That is because D3 doesn't matter about what you select, as you provide the tag name and the classes later .append('path'), .attr(class ...).
And, if you want to select elements that already exist, i read in the doc that .enter returns a placeholder selection. But if it returns a selection of placeholders (anonymous tags with .link class ?), there is no point to append a path to a path.
When i call .append, it does what i want, i.e. append a path to svg. But i don't understand the logic behind that. (I'm glad it works though, because d3 is powerful)
So, ok i selectAll('anything') and append what i want, regardless of what i selected. But if i try this:
d3.select('#savestring-debug')
.selectAll('div')
.data(debugobjs)
.enter().append('span')
.attr('style', function(d) { return 'background:#'+d.color })
.text(function(d) { return d.aff });
This would create placeholders for divs, but i append spans. Actually spans are created but i'm still looking for my divs ;)
So, what is the principle behind selectAll >> data >> enter >> append ?
thanks
The principle behind selectAll > data > enter > append is explained pretty well by
Mike Bostock here: http://bost.ocks.org/mike/join/ where he explains the concept of the data-join. I can't speak with any authority on the right way to use selectAll, but the way I use it is to select all of the elements I am going to be modifying-appending-removing within the part of the SVG that I need to modify.
So if I'm working with "rects" in a certain area, I'll do something like this:
var svg = d3.select('#graphID')
.append("svg")
.attr("width", 300)
.attr("height", 500);
var graphGroup = self.svg.append("g");
//...Inside a render function
//just want all the "rect" elements in graphGroup
var rects = graphGroup.selectAll("rect")
.data(dataset);
//depending on dataset new rects will need to be appendend
rects.enter()
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 0)
.attr("height", 0)
//all rects are transitioned to new co-ordinates
rects.transition().duration(500)
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d){
return yScale(d);
})
//rects that have no data associated with them are removed
rects.exit()
.transition()
.duration(500)
.attr("x", -xScale.rangeBand())
.remove();
With the idea that I could have other rects in the SVG that do not belong to graphGroup. I just selectAll the rects in a certain area and work on them when needed.
This is a great question and a slightly odd property of D3. If you look carefully how anything is done in D3 you'll notice that everything is added by appending to what is previously created. So the logic behind having the svg.selectAll('whatever class of stuff you're going to add') is that you are kinda making a placeholder for where whatever you are about append to go. It's like the svg is a wall and you're hanging hooks on the upper ridge for you to THEN hang your paintings from. If you don't have the selectAll, I just tried this, you will still append whatever you were gonna make to the page, but it won't be appended to the svg.
The data-->enter-->append is basically saying for each element in the larger data file that you are passing into the data function, make a new element, and append this element to my selection with such and such properties (set when you use the .attr).