I'm trying to append HTML elements within a <circle> and then use CSS-Sprite to fill them, but so far I can't see them!
Here's what im doing:
//Create the node in order to use the Force Layout
var node = svg.selectAll(".container")
.data(data)
.append("circle")
.attr("class", "dot")
.attr("r", radius)
.attr("cx", function(d) { return x(d[xVar]); }) //xAxis position
.attr("cy", function(d) { return y(d[yVar]); }) //yAxis position
.append("xhtml:i")
.attr('class', function(d) {
return 'sprite '+ d['css-code'];
});
And my css:
.sprite{
position: absolute;
background:url(sprite.png) no-repeat;
}
.ad{background-position:0 -704px;}
.ae{background-position:0 -736px;}
.etc
They are being generated and I can see them on my browser inspector with the correct css atributes but they just won't appear.
How do I use CSS-Sprites with D3's Force Layout?
You're inserting the <i> elements directly within the SVG <circle> elements, which is not legal SVG markup. There are at least a couple of alternatives that you could use.
Make the nodes <g> elements which would contain both the <circle> elements and a different element for the sprite.
Insert the HTML sprites directly in the HTML container for the graph and absolutely position them to correspond with the node positions.
Related
I am trying to add a link to the text elements of this D3 visualisation:
http://bl.ocks.org/1093025
I would like to be able to click on "flare", "analytics" and navigate to another page or click in the rectangle and perform the normal action, which is expanding the sub-trees.
I tried a few things that didn't work:
on event
I tried to add an on event to the svg:text element:
nodeEnter.append("svg:text")
.attr("dy", 3.5)
.attr("dx", 5.5)
.text(function(d) { return d.name; })
.on("click",function(d,i) { alert("Clicked on the text");});
foreignObject element
I tried to add the foreignObject element like this:
nodeEnter.append("svg:foreignObject")
.style("float","right")
.attr("height", 100)
.attr("width", 100)
.append("div")
.html("<a href='#'>link</a>")
Even though it creates the link, it is an extra link, though (not the text element in the rectangle).
link with xlink:href attribute
Finally, I also tried the following (in some combinations):
<a xlink:href="/svg/index.html">
<text x="10" y="20">/svg/index.html</text>
</a>
But it didn't work either.
Any suggestions?
I agree with Matt's answer re: pointer-events... change this to pointer-events: all in the css.
And this is how I made the link in the force directed graph:
svg = d3.select("body").append("svg");
svg.selectAll(".node")
.append("svg:a").attr("xlink:href", function(d){ return "generic.php?url=" + d.url })
.append("svg:text")
.text(function(d) { return d.name; })
.attr("dy", 3.5)
.attr("dx", 5.5)
.attr("text-anchor", "middle");
//The End ;-)
.on('click', function () {}) should work as long as you
have selected the right nodes
haven't disabled pointer events on text elements with css
The pointer events seems to be catching people out as quite a lot of the examples have pointer-events: none for the text nodes.
i have been trying to insert an icon using d3.js.but i am not getting any icon when i am running it .here is my code snippet.
node = svg.selectAll('g.node')
.data(svg_nodes, function (d) {
return (d && d.svg_id) || d3.select(this).attr("id");
})
node.select('polygon:nth-of-type(8)')
.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", -8)
.attr("y", -8)
.attr("width", 16)
.attr("height", 16);
It appears you are appending an <image> to a <polygon>. An image is not a valid child of an SVG polygon, only animation elements or descriptive elements are valid children (MDN).
Permissible children include:
<animate>, <animateColor>, <animateMotion>, <animateTransform>, <discard>, <mpath>, <set>, <desc>, <metadata>, <title>
As you can't add an image to a polygon, you'll need to add it to a different parent, this also means positioning it separately.
Generally speaking, a useful approach here is often to use a parent g to position each polygon and image pair - this might remove the need to position image and polygon separately, but also keeps these elements paired as siblings of a common parent.
As it appears you already have a parent g, we only need to access it in order to append to it:
node.select('polygon:nth-of-type(8)')
.each(function() {
d3.select(this.parentNode)
.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", -8)
.attr("y", -8)
.attr("width", 16)
.attr("height", 16);
})
Without seeing more of your code it is not possible to say how this will position the image relative to what you want, but should at least append the image
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.
How do I place a header image above the center text in a node?
Can I give each node a different header image? HOW?
Will the header image beable to scale according to node size?
The nodes/text are defined by size of node category/power and are scaled by:
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(d.packageName); })
.attr("transform", function(d) { return "translate(" + d + ")"; });;
node.append("text")
.attr("dy", ".2em")
.style("text-anchor", "middle")
.text(function(d) { return d.className; })
.style("font-size", "1px")
.each(getSize)
.style("font-size", function(d) { return d.scale + "px"; });
If I need to create individual background images to fill the circles; What would be my options for distributing the individual images to the appropriate nodes?
If more information is needed please notify me below...
1. How do I place a header image above the center text in a node?
Exactly like you did it with the text. You need to insert an image at the correct place (you will need to calculate the offset)
2. Can I give each node a different header image? HOW?
I would suggest adding an imageUrl property to your source data and then bind to it in the same way that you bind to d.packageName in your code snippet above.
3. Will the header image be able to scale according to node size?
Yes you can scale the image according to node size, have a look at svg:transform https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform. Specifically the "scale" attribute.
An alternative and perhaps simpler approach would be to always draw the nodes the same size but wrap each node in a "g" (group). Then you only need to scale the whole group once.
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).