Does SVG foreignObject work only with static HTML? - d3.js

As described on http://bl.ocks.org/mbostock/1424037, I have seen the foreignObject feature working only with statically inputted text. What happens if I try something like this:
svg.append("foreignObject")
.attr("width", 400)
.attr("height", 200)
.append("xhtml:body")
.style("font", "16px 'Helvetica Neue'")
.html(function(d) {
return d.name;
})
Does the foreignObject feature work with data returned by functions?

Well, that does work. However one thing that needs to be taken care of is to replace .html with .text.
node.append("foreignObject")
.attr("class", "innerNode")
.text(function (d) {
return d.name; })

Related

d3.js trying to call data method twice on same selector produces strange results

I'm new to D3 and am trying to build a table like structure out of rectangles. I would like the header to be a different color than the rest of the rectangles. I've written the following code:
table = svgContainer.selectAll('rect')
.data([managedObj])
.enter()
.append('rect')
.attr("width", 120)
.attr("height", 20)
.attr("fill", "blue")
.text(function(d) {
return d.name;
});
// create table body
table.selectAll('rect')
.data(managedObj.data)
.enter()
.append('rect')
.attr("y", function() {
shift += 20;
return shift;
})
.attr("width", 120)
.attr("height", 20)
.attr("fill", "red")
.text(function(d) {
return d.name;
});
This is producing the following results:
This is almost what I intended except it is nesting the second group of rectangles inside the first rectangle. This causes only the first blue rectangle to be visible. I'm assuming this has something to do with calling the data method twice. How can I fix this issue?
I think I understand the intended result, so I'll give it a go:
This line :
table.selectAll('rect')
is selecting the rectangle just created here:
table = svgContainer.selectAll('rect')....append('rect')....
You don't want to append rectangles to that rectangle (or any rectangle for that matter) because this won't work, but you do want to append them to the SVG itself.
So instead of table.selectAll you should be using svgContainer.selectAll, but there are two other issues:
if you use svgContainer.selectAll('rect') you will be selecting the rect you have already appended, when you actually want an empty selection. See the answer here.
you cannot place text in a rect (See answer here), instead you could append g elements and then append text and rect elements to those. And, for ease of positioning, you could translate the g elements so that positioning the rectangles and text is more straight forward.
So, your code could look like:
var data = ["test1","test2","test3","test4"];
var svgContainer = d3.select('body').append('svg').attr('width',900).attr('height',400);
var header = svgContainer.selectAll('g')
.data([data])
.enter()
.append('g')
.attr('transform','translate(0,0)');
header.append('rect')
.attr("width", 120)
.attr("height", 20)
.attr("fill", "blue");
header.append('text')
.attr('y',15)
.attr('x',5)
.text(function(d) {
return "header";
});
// create table body
var boxes = svgContainer.selectAll('.box')
.data(data)
.enter()
.append('g')
.attr('class','box')
.attr('transform',function(d,i) { return 'translate(0,'+((i+1)*20)+')'; });
boxes.append('rect').attr("width", 120)
.attr("height", 20)
.attr("fill", "red");
boxes.append('text')
.attr('y',15)
.attr('x',5)
.text(function(d) {
return d;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

internal link for using lightbox (fancyBox) in a d3 script

I'm quite new to web programming and its more or less like searching for a funcion and trying until stuff works. So you have an idea of my skill ;)
I'm trying to build a d3 visualization (after Mike Bostocks dendrogramm: http://bl.ocks.org/mbostock/4063570).
Within the tree I want to use internal links to a hidden div (with id='test') that uses a lightbox script (fancyBox: http://fancyapps.com/fancybox/).
So far, my d3 script produces following code for the link (copied from firebug):
<a class="fancybox" x="10" href="#test"><text class="nodeText" dy=".35em" text-anchor="start" style="fill-opacity: 1;" x="10"test</text>
When I'm using this code anywhere else (even in the d3-div) it works fine opening the lightbox div. But when I click on it within the d3 canvas it doesn't work.
The code for setting up the nodes and their properties (to create the code above) is as following:
nodeEnter.append("svg:a")
.attr("x", function(d) {
return d.children || d._children ? -10 : 10;
})
.attr("xlink:href", function(d){return d.url;}) // <-- reading the new "url" property
.attr('class', 'fancybox')
.append("text")
.attr("dy", ".35em")
.attr('class', 'nodeText')
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.name;
})
.style("fill-opacity", 0);
Furthermore, when I set an external url this works also fine. But when url is '#test' it doesn't.
I cannot find any articles for such an issue (maybe I have to spcial desires? O.o). Is there another way to setup internal links or did I missed any sign? I'm wondering why this works outside d3 but not within.
Thanks for help!
I solved this problem, building my own lightbox after Jim Nielsons tutorial (https://webdesign.tutsplus.com/articles/super-simple-lightbox-with-css-and-jquery--webdesign-3528). Watch out for the comments on recent versions of jquery.
The solution after having a working lightbox as desired is, to include the code into the d3 script somewhere (I put it to the end). Et voila!
It's not good to have blackboxes in your own code :)
Done.
nodeEnter.append("image")
.attr("xlink:href", function(d) { return d.icon; })
.on("click", function(d){
var link=document.createElement("a");
link.id="lightLink";
link.href= d.url;
link.rel="lightbox";
this.appendChild(link);
document.getElementById("lightLink").click();
})
.attr("x", "-12px")
.attr("y", "-12px")
.attr("width", "24px")
.attr("height", "24px");

How to add hypertext link to Cluster Force Layout circle

I have a cluster force layout which works fine, and I can add text to the circles. However, when I try to add a hypertext link, nothing is displayed.
What is the problem with my code?
The code for the text is
node.append("a")
.text(function(d){
return d.name;
})
.attr("href", function(d){
return '/profile/'+d.name;
})
.attr("dx", -10)
.text(function(d){
return d.name;
})
.style("stroke", "white");
In SVGs you are not allowed to put textual content into the <a> itself. For an overview of what is actually allowed in an <a> element have a look at the Content model section of the spec.
You need to wrap another text element around your link labels:
node.append("a")
.attr("xlink:href", function(d){
return '/profile/'+d.name;
})
.append("text") // <-- Wrap <text> element around link label
.text(function(d){
return d.name;
})
.attr("dx", -10)
.style("stroke", "white");

Link inside the node to expand it

I am trying to create a collapsible tree having a link to expand the current node (in stead of clicking the node using on.("click",clicked) method). Here is the jsfiddle
nodeEnter.append("foreignObject")
.attr("width", rectW)
.attr("height", rectH)
.append("xhtml:body")
.style("font", "14px 'Helvetica Neue'")
.html(function (d) { return "<a href='#' onclick=\x22clicked(" + source + ")\x22>"+d.name+"</a>"});
Problem is that your source variable is converted to string, so you don't actually pass object to clicked function;
You can rewrite this to append link like this:
nodeEnter.append("foreignObject")
.attr("width", rectW)
.attr("height", rectH)
.append("xhtml:body")
.style("font", "14px 'Helvetica Neue'")
.append("a").html(function(d){return d.name;})
.attr('href', '#')
.on('click', function(){
clicked(source);
});
see updated fiddle
To make it work for clicked node (instead of always parent node) you can replace source by clicked function argument
.on('click', function(d){
clicked(d);
});
another updated fiddle

Mouseover in a scatterplot of a second svg in D3

I am trying to create a page with a scatterplot, a network diagram, and a table. I was able to get the mousehandling to work on the network diagram and the table (with the help of #Superboggly at Link D3 force layout network diagram with table on mouseover). Now I am trying to get the mousehandling to work on a second svg with a scatterplot, and I think I'm messing up the referencing.
var mapit = svg2.selectAll("maprect")
.data(graph.nodes)
.enter().append("rect")
.attr("x", function(d) { return xScale(d.long); })
.attr("y", function(d) { return yScale(d.lat); })
.attr("height", 20)
.attr("width", 20)
.attr("fill", "cyan")
// This mouseover doesn't work, what am I missing?
.on("mouseover", function(d) {
d3.select(this).select("rect").style("fill", "orange");
})
.on("mouseout", function(d) {
d3.select(this).select("rect").style("fill", "cyan");
});
I'm new to D3 and to JavaScript, and am confused by the collection of ... mapit, svg2, maprect, graph.nodes, rect, ... that I pieced together from other folks' examples. Any suggestions?
The example is posted as a jsFiddle.
You were so close! Just remove the .select("rect") in the functions:
.on("mouseover", function(d) {
d3.select(this).style("fill", "orange");
})
.on("mouseout", function(d) {
d3.select(this).style("fill", "cyan");
});
updated fiddle.

Resources