admin管理员组文章数量:1026989
I am working with a d3 force based graph, whose node labels are in fact URLs, which when clicked, take the user to the target URL. For usability reasons, is there a way to underline the url? Better yet, can the underline appear and disappear, can the color of the text be changed, as the user hovers over a certain label? This would help users understand that the labels are clickable. Please help.
document.addEventListener('DOMContentLoaded', function () {
drawVisual();
});
var QueuedORG = [];
var tempLIST = [];
function drawVisual()
{
//alert(sessionStorage["queuedArray"]);
/*var getArr = [];
getArr = JSON.parse(localStorage.getItem('storeArray'));
document.getElementById('myMSG').innerHTML = getArr[1].parentURL;*/
QueuedORG.length = 0;
tempLIST.length = 0;
//var w = 1024, h = 768;
var w=window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var h=window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
//var w = 1024, h = 768;
//var vis = d3.select("#tab_5_contents").append("svg:svg").attr("width", w).attr("height", h);
var vis = d3.select("#forcedLayoutGraph").append("svg:svg").attr("width", w).attr("height", h);
//get links from LocalStorage
//QueuedORG = JSON.parse(sessionStorage.getItem("queuedArray"));
//QueuedORG = JSON.parse(localStorage["queuedArray"]);
QueuedORG.push({url: "/", parentURL: "", used:0});
QueuedORG.push({url: "", parentURL: "/", used:0});
QueuedORG.push({url: "", parentURL: "/", used:0});
QueuedORG.push({url: "", parentURL: "/", used:0});
QueuedORG.push({url: "", parentURL: "/", used:0});
var nodes = [];
nodes.length = 0;
var labelAnchors = [];
labelAnchors.length = 0;
var labelAnchorLinks = [];
labelAnchorLinks.length = 0;
var links = [];
links.length = 0;
for(var i = 0; i < QueuedORG.length; i++)
{
var nodeExists = 0;
//check to see if a node for the current url has already been created. If yes, do not create a new node
for(var j = 0; j < nodes.length; j++)
{
if(QueuedORG[i].url == nodes[j].label)
nodeExists = 1;
}
if (nodeExists == 0)
{
var urlLabel = QueuedORG[i].url;
//remove 'http://' part
/*urlLabel = urlLabel.split("http://")[1];
if(urlLabel.match("www"))
urlLabel = urlLabel.split("www.")[1];
var rest = urlLabel.split("\.")[1];
urlLabel = urlLabel.split("\.")[0];*/
var node = {
label : QueuedORG[i].url,
category : QueuedORG[i].category
};
nodes.push(node);
labelAnchors.push({
node : node
});
labelAnchors.push({
node : node
});
}
};
/*for(var i=0;i<nodes.length; i++)
{
console.log("node i:"+i+nodes[i]+"\n");
console.log("labelAnchor i:"+i+labelAnchors[i]+"\n");
}*/
//To create links for connecting nodes
for(var i = 0; i < QueuedORG.length; i++)
{
var srcIndx = 0, tgtIndx = 0;
for(var j = 0; j < nodes.length; j++)
{
if( QueuedORG[i].url == nodes[j].label ) //to find the node number for the current url
{
srcIndx = j;
}
if( QueuedORG[i].parentURL == nodes[j].label ) //to find the node number for the parent url
{
tgtIndx = j;
}
}
//console.log("src:"+srcIndx+" tgt:"+tgtIndx);
//connecting the current url's node to the parent url's node
links.push({
source : srcIndx,
target : tgtIndx,
weight : 1,
});
labelAnchorLinks.push({
source : srcIndx * 2,
target : srcIndx * 2 + 1,
weight : 1
});
};
var force = d3.layout.force().size([w, h]).nodes(nodes).links(links).gravity(1).charge(-10000).linkStrength(function(x) {
return x.weight * 10 // charge is for inter-node repel, link distance is node-node distance
});
force.linkDistance(function(d) {
return d.weight * 100;
});
force.start();
var force2 = d3.layout.force().nodes(labelAnchors).links(labelAnchorLinks).gravity(0).linkStrength(10).charge(-500).size([w, h]); //charge is for inter-label repel, link distance is node-label distance
force2.linkDistance(function(d) {
return d.weight * 10;
});
force2.start();
var link = vis.selectAll("line.link").data(links).enter().append("svg:line").attr("class", "link").style("stroke", "#CCC");
var colors = {"1": "black", "2": "blue", "3": "red"}; // 1=root node 2=blog nodes 3= nodes
var shape = {"1": "diamond", "2": "cross", "3": "circle"};
var node = vis.selectAll("g.node").data(force.nodes()).enter().append("path").attr("class", "node").call(force.drag);
//node.append("circle").attr("r", 5).style("stroke", "#FFF").style("stroke-width", 3).attr("class", function(d) {return "node category"+d.category});
node.attr("d", d3.svg.symbol().type(function(d) {return shape[d.category];})).style("stroke", "#FFF").style("fill", function(d){ return colors[d.category];}).on('click', function(d, i) {
var win = window.open(d.node.label, '_blank);
win.focus();
});
var anchorLink = vis.selectAll("line.anchorLink").data(labelAnchorLinks)//.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#999");
var anchorNode = vis.selectAll("g.anchorNode").data(force2.nodes()).enter().append("svg:g").attr("class", "anchorNode").on('click', function(d, i){
var win = window.open(d.node.label, '_blank);
win.focus();
});
anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF");
anchorNode.append("svg:text").text(function(d, i) {
return i % 2 == 0 ? "" : d.node.label
}).style("fill", "#555").style("font-family", "Arial").style("font-size", 12);
var updateLink = function() {
this.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;
});
}
var updateNode = function() {
this.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
force.on("tick", function() {
force2.start();
node.call(updateNode);
anchorNode.each(function(d, i) {
if(i % 2 == 0) {
d.x = d.node.x;
d.y = d.node.y;
} else {
var b = this.childNodes[1].getBBox();
var diffX = d.x - d.node.x;
var diffY = d.y - d.node.y;
var dist = Math.sqrt(diffX * diffX + diffY * diffY);
var shiftX = b.width * (diffX - dist) / (dist * 2);
shiftX = Math.max(-b.width, Math.min(0, shiftX));
var shiftY = 5;
this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
}
});
anchorNode.call(updateNode);
link.call(updateLink);
anchorLink.call(updateLink);
});
}
I am working with a d3 force based graph, whose node labels are in fact URLs, which when clicked, take the user to the target URL. For usability reasons, is there a way to underline the url? Better yet, can the underline appear and disappear, can the color of the text be changed, as the user hovers over a certain label? This would help users understand that the labels are clickable. Please help.
document.addEventListener('DOMContentLoaded', function () {
drawVisual();
});
var QueuedORG = [];
var tempLIST = [];
function drawVisual()
{
//alert(sessionStorage["queuedArray"]);
/*var getArr = [];
getArr = JSON.parse(localStorage.getItem('storeArray'));
document.getElementById('myMSG').innerHTML = getArr[1].parentURL;*/
QueuedORG.length = 0;
tempLIST.length = 0;
//var w = 1024, h = 768;
var w=window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var h=window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
//var w = 1024, h = 768;
//var vis = d3.select("#tab_5_contents").append("svg:svg").attr("width", w).attr("height", h);
var vis = d3.select("#forcedLayoutGraph").append("svg:svg").attr("width", w).attr("height", h);
//get links from LocalStorage
//QueuedORG = JSON.parse(sessionStorage.getItem("queuedArray"));
//QueuedORG = JSON.parse(localStorage["queuedArray"]);
QueuedORG.push({url: "http://understandblue.blogspot./", parentURL: "http://understandblue.blogspot.", used:0});
QueuedORG.push({url: "http://www.google.", parentURL: "http://understandblue.blogspot./", used:0});
QueuedORG.push({url: "http://paperfriendly.blogspot.", parentURL: "http://understandblue.blogspot./", used:0});
QueuedORG.push({url: "http://4pawsforever", parentURL: "http://understandblue.blogspot./", used:0});
QueuedORG.push({url: "http://en.wikipedia", parentURL: "http://understandblue.blogspot./", used:0});
var nodes = [];
nodes.length = 0;
var labelAnchors = [];
labelAnchors.length = 0;
var labelAnchorLinks = [];
labelAnchorLinks.length = 0;
var links = [];
links.length = 0;
for(var i = 0; i < QueuedORG.length; i++)
{
var nodeExists = 0;
//check to see if a node for the current url has already been created. If yes, do not create a new node
for(var j = 0; j < nodes.length; j++)
{
if(QueuedORG[i].url == nodes[j].label)
nodeExists = 1;
}
if (nodeExists == 0)
{
var urlLabel = QueuedORG[i].url;
//remove 'http://' part
/*urlLabel = urlLabel.split("http://")[1];
if(urlLabel.match("www"))
urlLabel = urlLabel.split("www.")[1];
var rest = urlLabel.split("\.")[1];
urlLabel = urlLabel.split("\.")[0];*/
var node = {
label : QueuedORG[i].url,
category : QueuedORG[i].category
};
nodes.push(node);
labelAnchors.push({
node : node
});
labelAnchors.push({
node : node
});
}
};
/*for(var i=0;i<nodes.length; i++)
{
console.log("node i:"+i+nodes[i]+"\n");
console.log("labelAnchor i:"+i+labelAnchors[i]+"\n");
}*/
//To create links for connecting nodes
for(var i = 0; i < QueuedORG.length; i++)
{
var srcIndx = 0, tgtIndx = 0;
for(var j = 0; j < nodes.length; j++)
{
if( QueuedORG[i].url == nodes[j].label ) //to find the node number for the current url
{
srcIndx = j;
}
if( QueuedORG[i].parentURL == nodes[j].label ) //to find the node number for the parent url
{
tgtIndx = j;
}
}
//console.log("src:"+srcIndx+" tgt:"+tgtIndx);
//connecting the current url's node to the parent url's node
links.push({
source : srcIndx,
target : tgtIndx,
weight : 1,
});
labelAnchorLinks.push({
source : srcIndx * 2,
target : srcIndx * 2 + 1,
weight : 1
});
};
var force = d3.layout.force().size([w, h]).nodes(nodes).links(links).gravity(1).charge(-10000).linkStrength(function(x) {
return x.weight * 10 // charge is for inter-node repel, link distance is node-node distance
});
force.linkDistance(function(d) {
return d.weight * 100;
});
force.start();
var force2 = d3.layout.force().nodes(labelAnchors).links(labelAnchorLinks).gravity(0).linkStrength(10).charge(-500).size([w, h]); //charge is for inter-label repel, link distance is node-label distance
force2.linkDistance(function(d) {
return d.weight * 10;
});
force2.start();
var link = vis.selectAll("line.link").data(links).enter().append("svg:line").attr("class", "link").style("stroke", "#CCC");
var colors = {"1": "black", "2": "blue", "3": "red"}; // 1=root node 2=blog nodes 3= nodes
var shape = {"1": "diamond", "2": "cross", "3": "circle"};
var node = vis.selectAll("g.node").data(force.nodes()).enter().append("path").attr("class", "node").call(force.drag);
//node.append("circle").attr("r", 5).style("stroke", "#FFF").style("stroke-width", 3).attr("class", function(d) {return "node category"+d.category});
node.attr("d", d3.svg.symbol().type(function(d) {return shape[d.category];})).style("stroke", "#FFF").style("fill", function(d){ return colors[d.category];}).on('click', function(d, i) {
var win = window.open(d.node.label, '_blank);
win.focus();
});
var anchorLink = vis.selectAll("line.anchorLink").data(labelAnchorLinks)//.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#999");
var anchorNode = vis.selectAll("g.anchorNode").data(force2.nodes()).enter().append("svg:g").attr("class", "anchorNode").on('click', function(d, i){
var win = window.open(d.node.label, '_blank);
win.focus();
});
anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF");
anchorNode.append("svg:text").text(function(d, i) {
return i % 2 == 0 ? "" : d.node.label
}).style("fill", "#555").style("font-family", "Arial").style("font-size", 12);
var updateLink = function() {
this.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;
});
}
var updateNode = function() {
this.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
force.on("tick", function() {
force2.start();
node.call(updateNode);
anchorNode.each(function(d, i) {
if(i % 2 == 0) {
d.x = d.node.x;
d.y = d.node.y;
} else {
var b = this.childNodes[1].getBBox();
var diffX = d.x - d.node.x;
var diffY = d.y - d.node.y;
var dist = Math.sqrt(diffX * diffX + diffY * diffY);
var shiftX = b.width * (diffX - dist) / (dist * 2);
shiftX = Math.max(-b.width, Math.min(0, shiftX));
var shiftY = 5;
this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
}
});
anchorNode.call(updateNode);
link.call(updateLink);
anchorLink.call(updateLink);
});
}
Share
Improve this question
edited Jun 11, 2014 at 12:34
VividD
10.5k8 gold badges66 silver badges112 bronze badges
asked Jan 22, 2014 at 0:36
Darth CoderDarth Coder
1,8087 gold badges35 silver badges59 bronze badges
2 Answers
Reset to default 3As Matt Walters said, you can change the text colour and add underlining with CSS, including adding hover effects.
However, I would highly remend that you implement your links as actual <a>
element hyperlinks, which are perfectly acceptable in SVG. I've tested it, and you can either wrap the text element in a link, or put the link inside the text element, but surrounding the actual text content. The only plication is that you have to use xlink:href
for the url attribute, instead of simply href
.
Here's a force-directed graph I created for someone else's debugging, adapted to turn all the text labels into links:
http://codepen.io/AmeliaBR/pen/AoFHg
Key code in the update function:
hypertext = hypertext.data(force.nodes());
// hypertext refers to a selection of text elements, joined to the data
// Add labels for new nodes:
hypertext.enter().append("text") //add the text element
//add any unchanging attributes of the <text>:
.attr("text-anchor", "middle")
//add a link element within each text element
.append("a")
//add unchanging attributes of the <a> link
.attr("target", "_blank") //set the link to open in an unnamed tab
.attr("xlink:show", "new");
//show="new" is an XML way to tell the link to open in a new tab/window.
//Either attribute *should* work on its own, but best use both to be safe.
// At this point, each new <text> element contains an <a> element, but the
// variable hypertext still refers to the text elements.
// Remove any outgoing/old text elements for non-existent nodes:
hypertext.exit().remove();
// Compute data-based attributes for entering and updating texts:
hypertext.attr("x", 8) //attributes of the <text> elements
.attr("y", "0.3em")
//Select the existing <a> element within each <text>
.select("a")
//Update the link attributes. Note that the <a> element was never given
//its own data, so it inherits the data from the parent <text> element.
.attr("xlink:href", function (d) {
return "http://example./" + d.name; //create url from data
})
//Set the text within the link, which in this case is the only text
//within the text element.
.text(function (d) {
return d.name; //link text content
});
Using the link element created all the necessary functionality, but it didn't add the default HTML link styles. For that, there's CSS:
text a {
fill: navy;
}
text a:visited {
fill:darkpurple;
}
text a:hover, text a:active {
text-decoration: underline;
fill:darkred;
}
I think you should be able to do all of this in css. Thats part of the beauty of d3.js
Without seeing some code it'll be hard to say exactly what you need, but Ill bet you could do something like this for the underlining and something similar for the other effects you have.
Can you post some code, so we can help you further?
I am working with a d3 force based graph, whose node labels are in fact URLs, which when clicked, take the user to the target URL. For usability reasons, is there a way to underline the url? Better yet, can the underline appear and disappear, can the color of the text be changed, as the user hovers over a certain label? This would help users understand that the labels are clickable. Please help.
document.addEventListener('DOMContentLoaded', function () {
drawVisual();
});
var QueuedORG = [];
var tempLIST = [];
function drawVisual()
{
//alert(sessionStorage["queuedArray"]);
/*var getArr = [];
getArr = JSON.parse(localStorage.getItem('storeArray'));
document.getElementById('myMSG').innerHTML = getArr[1].parentURL;*/
QueuedORG.length = 0;
tempLIST.length = 0;
//var w = 1024, h = 768;
var w=window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var h=window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
//var w = 1024, h = 768;
//var vis = d3.select("#tab_5_contents").append("svg:svg").attr("width", w).attr("height", h);
var vis = d3.select("#forcedLayoutGraph").append("svg:svg").attr("width", w).attr("height", h);
//get links from LocalStorage
//QueuedORG = JSON.parse(sessionStorage.getItem("queuedArray"));
//QueuedORG = JSON.parse(localStorage["queuedArray"]);
QueuedORG.push({url: "/", parentURL: "", used:0});
QueuedORG.push({url: "", parentURL: "/", used:0});
QueuedORG.push({url: "", parentURL: "/", used:0});
QueuedORG.push({url: "", parentURL: "/", used:0});
QueuedORG.push({url: "", parentURL: "/", used:0});
var nodes = [];
nodes.length = 0;
var labelAnchors = [];
labelAnchors.length = 0;
var labelAnchorLinks = [];
labelAnchorLinks.length = 0;
var links = [];
links.length = 0;
for(var i = 0; i < QueuedORG.length; i++)
{
var nodeExists = 0;
//check to see if a node for the current url has already been created. If yes, do not create a new node
for(var j = 0; j < nodes.length; j++)
{
if(QueuedORG[i].url == nodes[j].label)
nodeExists = 1;
}
if (nodeExists == 0)
{
var urlLabel = QueuedORG[i].url;
//remove 'http://' part
/*urlLabel = urlLabel.split("http://")[1];
if(urlLabel.match("www"))
urlLabel = urlLabel.split("www.")[1];
var rest = urlLabel.split("\.")[1];
urlLabel = urlLabel.split("\.")[0];*/
var node = {
label : QueuedORG[i].url,
category : QueuedORG[i].category
};
nodes.push(node);
labelAnchors.push({
node : node
});
labelAnchors.push({
node : node
});
}
};
/*for(var i=0;i<nodes.length; i++)
{
console.log("node i:"+i+nodes[i]+"\n");
console.log("labelAnchor i:"+i+labelAnchors[i]+"\n");
}*/
//To create links for connecting nodes
for(var i = 0; i < QueuedORG.length; i++)
{
var srcIndx = 0, tgtIndx = 0;
for(var j = 0; j < nodes.length; j++)
{
if( QueuedORG[i].url == nodes[j].label ) //to find the node number for the current url
{
srcIndx = j;
}
if( QueuedORG[i].parentURL == nodes[j].label ) //to find the node number for the parent url
{
tgtIndx = j;
}
}
//console.log("src:"+srcIndx+" tgt:"+tgtIndx);
//connecting the current url's node to the parent url's node
links.push({
source : srcIndx,
target : tgtIndx,
weight : 1,
});
labelAnchorLinks.push({
source : srcIndx * 2,
target : srcIndx * 2 + 1,
weight : 1
});
};
var force = d3.layout.force().size([w, h]).nodes(nodes).links(links).gravity(1).charge(-10000).linkStrength(function(x) {
return x.weight * 10 // charge is for inter-node repel, link distance is node-node distance
});
force.linkDistance(function(d) {
return d.weight * 100;
});
force.start();
var force2 = d3.layout.force().nodes(labelAnchors).links(labelAnchorLinks).gravity(0).linkStrength(10).charge(-500).size([w, h]); //charge is for inter-label repel, link distance is node-label distance
force2.linkDistance(function(d) {
return d.weight * 10;
});
force2.start();
var link = vis.selectAll("line.link").data(links).enter().append("svg:line").attr("class", "link").style("stroke", "#CCC");
var colors = {"1": "black", "2": "blue", "3": "red"}; // 1=root node 2=blog nodes 3= nodes
var shape = {"1": "diamond", "2": "cross", "3": "circle"};
var node = vis.selectAll("g.node").data(force.nodes()).enter().append("path").attr("class", "node").call(force.drag);
//node.append("circle").attr("r", 5).style("stroke", "#FFF").style("stroke-width", 3).attr("class", function(d) {return "node category"+d.category});
node.attr("d", d3.svg.symbol().type(function(d) {return shape[d.category];})).style("stroke", "#FFF").style("fill", function(d){ return colors[d.category];}).on('click', function(d, i) {
var win = window.open(d.node.label, '_blank);
win.focus();
});
var anchorLink = vis.selectAll("line.anchorLink").data(labelAnchorLinks)//.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#999");
var anchorNode = vis.selectAll("g.anchorNode").data(force2.nodes()).enter().append("svg:g").attr("class", "anchorNode").on('click', function(d, i){
var win = window.open(d.node.label, '_blank);
win.focus();
});
anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF");
anchorNode.append("svg:text").text(function(d, i) {
return i % 2 == 0 ? "" : d.node.label
}).style("fill", "#555").style("font-family", "Arial").style("font-size", 12);
var updateLink = function() {
this.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;
});
}
var updateNode = function() {
this.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
force.on("tick", function() {
force2.start();
node.call(updateNode);
anchorNode.each(function(d, i) {
if(i % 2 == 0) {
d.x = d.node.x;
d.y = d.node.y;
} else {
var b = this.childNodes[1].getBBox();
var diffX = d.x - d.node.x;
var diffY = d.y - d.node.y;
var dist = Math.sqrt(diffX * diffX + diffY * diffY);
var shiftX = b.width * (diffX - dist) / (dist * 2);
shiftX = Math.max(-b.width, Math.min(0, shiftX));
var shiftY = 5;
this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
}
});
anchorNode.call(updateNode);
link.call(updateLink);
anchorLink.call(updateLink);
});
}
I am working with a d3 force based graph, whose node labels are in fact URLs, which when clicked, take the user to the target URL. For usability reasons, is there a way to underline the url? Better yet, can the underline appear and disappear, can the color of the text be changed, as the user hovers over a certain label? This would help users understand that the labels are clickable. Please help.
document.addEventListener('DOMContentLoaded', function () {
drawVisual();
});
var QueuedORG = [];
var tempLIST = [];
function drawVisual()
{
//alert(sessionStorage["queuedArray"]);
/*var getArr = [];
getArr = JSON.parse(localStorage.getItem('storeArray'));
document.getElementById('myMSG').innerHTML = getArr[1].parentURL;*/
QueuedORG.length = 0;
tempLIST.length = 0;
//var w = 1024, h = 768;
var w=window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;
var h=window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;
//var w = 1024, h = 768;
//var vis = d3.select("#tab_5_contents").append("svg:svg").attr("width", w).attr("height", h);
var vis = d3.select("#forcedLayoutGraph").append("svg:svg").attr("width", w).attr("height", h);
//get links from LocalStorage
//QueuedORG = JSON.parse(sessionStorage.getItem("queuedArray"));
//QueuedORG = JSON.parse(localStorage["queuedArray"]);
QueuedORG.push({url: "http://understandblue.blogspot./", parentURL: "http://understandblue.blogspot.", used:0});
QueuedORG.push({url: "http://www.google.", parentURL: "http://understandblue.blogspot./", used:0});
QueuedORG.push({url: "http://paperfriendly.blogspot.", parentURL: "http://understandblue.blogspot./", used:0});
QueuedORG.push({url: "http://4pawsforever", parentURL: "http://understandblue.blogspot./", used:0});
QueuedORG.push({url: "http://en.wikipedia", parentURL: "http://understandblue.blogspot./", used:0});
var nodes = [];
nodes.length = 0;
var labelAnchors = [];
labelAnchors.length = 0;
var labelAnchorLinks = [];
labelAnchorLinks.length = 0;
var links = [];
links.length = 0;
for(var i = 0; i < QueuedORG.length; i++)
{
var nodeExists = 0;
//check to see if a node for the current url has already been created. If yes, do not create a new node
for(var j = 0; j < nodes.length; j++)
{
if(QueuedORG[i].url == nodes[j].label)
nodeExists = 1;
}
if (nodeExists == 0)
{
var urlLabel = QueuedORG[i].url;
//remove 'http://' part
/*urlLabel = urlLabel.split("http://")[1];
if(urlLabel.match("www"))
urlLabel = urlLabel.split("www.")[1];
var rest = urlLabel.split("\.")[1];
urlLabel = urlLabel.split("\.")[0];*/
var node = {
label : QueuedORG[i].url,
category : QueuedORG[i].category
};
nodes.push(node);
labelAnchors.push({
node : node
});
labelAnchors.push({
node : node
});
}
};
/*for(var i=0;i<nodes.length; i++)
{
console.log("node i:"+i+nodes[i]+"\n");
console.log("labelAnchor i:"+i+labelAnchors[i]+"\n");
}*/
//To create links for connecting nodes
for(var i = 0; i < QueuedORG.length; i++)
{
var srcIndx = 0, tgtIndx = 0;
for(var j = 0; j < nodes.length; j++)
{
if( QueuedORG[i].url == nodes[j].label ) //to find the node number for the current url
{
srcIndx = j;
}
if( QueuedORG[i].parentURL == nodes[j].label ) //to find the node number for the parent url
{
tgtIndx = j;
}
}
//console.log("src:"+srcIndx+" tgt:"+tgtIndx);
//connecting the current url's node to the parent url's node
links.push({
source : srcIndx,
target : tgtIndx,
weight : 1,
});
labelAnchorLinks.push({
source : srcIndx * 2,
target : srcIndx * 2 + 1,
weight : 1
});
};
var force = d3.layout.force().size([w, h]).nodes(nodes).links(links).gravity(1).charge(-10000).linkStrength(function(x) {
return x.weight * 10 // charge is for inter-node repel, link distance is node-node distance
});
force.linkDistance(function(d) {
return d.weight * 100;
});
force.start();
var force2 = d3.layout.force().nodes(labelAnchors).links(labelAnchorLinks).gravity(0).linkStrength(10).charge(-500).size([w, h]); //charge is for inter-label repel, link distance is node-label distance
force2.linkDistance(function(d) {
return d.weight * 10;
});
force2.start();
var link = vis.selectAll("line.link").data(links).enter().append("svg:line").attr("class", "link").style("stroke", "#CCC");
var colors = {"1": "black", "2": "blue", "3": "red"}; // 1=root node 2=blog nodes 3= nodes
var shape = {"1": "diamond", "2": "cross", "3": "circle"};
var node = vis.selectAll("g.node").data(force.nodes()).enter().append("path").attr("class", "node").call(force.drag);
//node.append("circle").attr("r", 5).style("stroke", "#FFF").style("stroke-width", 3).attr("class", function(d) {return "node category"+d.category});
node.attr("d", d3.svg.symbol().type(function(d) {return shape[d.category];})).style("stroke", "#FFF").style("fill", function(d){ return colors[d.category];}).on('click', function(d, i) {
var win = window.open(d.node.label, '_blank);
win.focus();
});
var anchorLink = vis.selectAll("line.anchorLink").data(labelAnchorLinks)//.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#999");
var anchorNode = vis.selectAll("g.anchorNode").data(force2.nodes()).enter().append("svg:g").attr("class", "anchorNode").on('click', function(d, i){
var win = window.open(d.node.label, '_blank);
win.focus();
});
anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF");
anchorNode.append("svg:text").text(function(d, i) {
return i % 2 == 0 ? "" : d.node.label
}).style("fill", "#555").style("font-family", "Arial").style("font-size", 12);
var updateLink = function() {
this.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;
});
}
var updateNode = function() {
this.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
force.on("tick", function() {
force2.start();
node.call(updateNode);
anchorNode.each(function(d, i) {
if(i % 2 == 0) {
d.x = d.node.x;
d.y = d.node.y;
} else {
var b = this.childNodes[1].getBBox();
var diffX = d.x - d.node.x;
var diffY = d.y - d.node.y;
var dist = Math.sqrt(diffX * diffX + diffY * diffY);
var shiftX = b.width * (diffX - dist) / (dist * 2);
shiftX = Math.max(-b.width, Math.min(0, shiftX));
var shiftY = 5;
this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
}
});
anchorNode.call(updateNode);
link.call(updateLink);
anchorLink.call(updateLink);
});
}
Share
Improve this question
edited Jun 11, 2014 at 12:34
VividD
10.5k8 gold badges66 silver badges112 bronze badges
asked Jan 22, 2014 at 0:36
Darth CoderDarth Coder
1,8087 gold badges35 silver badges59 bronze badges
2 Answers
Reset to default 3As Matt Walters said, you can change the text colour and add underlining with CSS, including adding hover effects.
However, I would highly remend that you implement your links as actual <a>
element hyperlinks, which are perfectly acceptable in SVG. I've tested it, and you can either wrap the text element in a link, or put the link inside the text element, but surrounding the actual text content. The only plication is that you have to use xlink:href
for the url attribute, instead of simply href
.
Here's a force-directed graph I created for someone else's debugging, adapted to turn all the text labels into links:
http://codepen.io/AmeliaBR/pen/AoFHg
Key code in the update function:
hypertext = hypertext.data(force.nodes());
// hypertext refers to a selection of text elements, joined to the data
// Add labels for new nodes:
hypertext.enter().append("text") //add the text element
//add any unchanging attributes of the <text>:
.attr("text-anchor", "middle")
//add a link element within each text element
.append("a")
//add unchanging attributes of the <a> link
.attr("target", "_blank") //set the link to open in an unnamed tab
.attr("xlink:show", "new");
//show="new" is an XML way to tell the link to open in a new tab/window.
//Either attribute *should* work on its own, but best use both to be safe.
// At this point, each new <text> element contains an <a> element, but the
// variable hypertext still refers to the text elements.
// Remove any outgoing/old text elements for non-existent nodes:
hypertext.exit().remove();
// Compute data-based attributes for entering and updating texts:
hypertext.attr("x", 8) //attributes of the <text> elements
.attr("y", "0.3em")
//Select the existing <a> element within each <text>
.select("a")
//Update the link attributes. Note that the <a> element was never given
//its own data, so it inherits the data from the parent <text> element.
.attr("xlink:href", function (d) {
return "http://example./" + d.name; //create url from data
})
//Set the text within the link, which in this case is the only text
//within the text element.
.text(function (d) {
return d.name; //link text content
});
Using the link element created all the necessary functionality, but it didn't add the default HTML link styles. For that, there's CSS:
text a {
fill: navy;
}
text a:visited {
fill:darkpurple;
}
text a:hover, text a:active {
text-decoration: underline;
fill:darkred;
}
I think you should be able to do all of this in css. Thats part of the beauty of d3.js
Without seeing some code it'll be hard to say exactly what you need, but Ill bet you could do something like this for the underlining and something similar for the other effects you have.
Can you post some code, so we can help you further?
本文标签: javascriptHow to underline text within a node39s label in d3 force based layoutsStack Overflow
版权声明:本文标题:javascript - How to underline text within a node's label in d3 force based layouts? - Stack Overflow 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://it.en369.cn/questions/1745666959a2162231.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论