问题
I am working on a simple force directed graph as given in This link. It works well but I want to show tool-tip on edges also as it shows on nodes. Whenever I move the mouse on an edge, it shows some tool-tip on the link (data may be retrieved from JSON file). Is there a builtin method or I have to show the div on mouse-over (in this case how to get the position of mouse, where the div would be shown)
回答1:
- Is there a builtin method?
Ans: Yes. There is a built in way. Most HTML elements support the title attribute. When you move the mouse pointer over that element, a little tool-tip is shown for a certain amount of time or until you leave that element.
Demo:
var w = 500,
h = 200
var vis = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
var graph = {
nodes: [{
name: 'A'
}, {
name: 'B'
}],
links: [{
source: 0,
target: 1
}]
};
var force = d3.layout.force()
.nodes(graph.nodes)
.links(graph.links)
.gravity(.05)
.distance(100)
.charge(-100)
.size([w, h])
.start();
var link = vis.selectAll("line.link")
.data(graph.links)
.enter().append("svg:line")
.attr("class", "link");
link.append("title").text(function(d) {
return d.source.name + " -> " + d.target.name
});
var node = vis.selectAll("g.node")
.data(graph.nodes)
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag);
node.append("circle").attr("r", 5);
node.append("title").text(function(d) {
return d.name
});
force.on("tick", function() {
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;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
line {
stroke: red;
stroke-width: 3;
}
cicrle {
fill: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
- or I have to show the div on mouse-over? How to get the position of mouse? Where the div would be shown?
Ans:
If you would like to show a tooltip with html content, you can show a div on mouse-over. You can get the mouse position using d3.mouse
method.
Demo:
var width = 960;
var height = 500;
var margin = 20;
var pad = margin / 2;
var color = d3.scale.category20();
var graph = {
"nodes": [{
"name": "Myriel",
"group": 1
}, {
"name": "Napoleon",
"group": 1
}, {
"name": "Mlle.Baptistine",
"group": 1
}, {
"name": "Mme.Magloire",
"group": 1
}, {
"name": "CountessdeLo",
"group": 1
}, {
"name": "Geborand",
"group": 1
}, {
"name": "Champtercier",
"group": 1
}, {
"name": "Cravatte",
"group": 1
}, {
"name": "Count",
"group": 1
}, {
"name": "OldMan",
"group": 1
}, {
"name": "Labarre",
"group": 2
}, {
"name": "Valjean",
"group": 2
}, {
"name": "Marguerite",
"group": 3
}, {
"name": "Mme.deR",
"group": 2
}, {
"name": "Isabeau",
"group": 2
}, {
"name": "Gervais",
"group": 2
}, {
"name": "Tholomyes",
"group": 3
}, {
"name": "Listolier",
"group": 3
}, {
"name": "Fameuil",
"group": 3
}, {
"name": "Blacheville",
"group": 3
}, {
"name": "Favourite",
"group": 3
}, {
"name": "Dahlia",
"group": 3
}, {
"name": "Zephine",
"group": 3
}, {
"name": "Fantine",
"group": 3
}, {
"name": "Mme.Thenardier",
"group": 4
}, {
"name": "Thenardier",
"group": 4
}, ],
"links": [{
"source": 1,
"target": 0,
"value": 1
}, {
"source": 2,
"target": 0,
"value": 8
}, {
"source": 3,
"target": 0,
"value": 10
}, {
"source": 3,
"target": 2,
"value": 6
}, {
"source": 4,
"target": 0,
"value": 1
}, {
"source": 5,
"target": 0,
"value": 1
}, {
"source": 6,
"target": 0,
"value": 1
}]
};
drawGraph(graph);
function drawGraph(graph) {
var svg = d3.select("#force").append("svg")
.attr("width", width)
.attr("height", height);
// draw plot background
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "#eeeeee");
// create an area within svg for plotting graph
var plot = svg.append("g")
.attr("id", "plot")
.attr("transform", "translate(" + pad + ", " + pad + ")");
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-force
var layout = d3.layout.force()
.size([width - margin, height - margin])
.charge(-120)
.linkDistance(function(d, i) {
return (d.source.group == d.target.group) ? 50 : 100;
})
.nodes(graph.nodes)
.links(graph.links)
.start();
drawLinks(graph.links);
drawNodes(graph.nodes);
// add ability to drag and update layout
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-drag
d3.selectAll(".node").call(layout.drag);
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-on
layout.on("tick", function() {
d3.selectAll(".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;
});
d3.selectAll(".node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
}
// Draws nodes on plot
function drawNodes(nodes) {
// used to assign nodes color by group
var color = d3.scale.category20();
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-nodes
d3.select("#plot").selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("id", function(d, i) {
return d.name;
})
.attr("cx", function(d, i) {
return d.x;
})
.attr("cy", function(d, i) {
return d.y;
})
.attr("r", function(d, i) {
return 4;
})
.style("fill", function(d, i) {
return color(d.group);
})
.on("mouseover", function(d, i) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
var tooltip = d3.select("#plot")
.append("text")
.text(d.name)
.attr("x", x)
.attr("y", y)
//.attr("dy", -r * 2)
.attr("id", "tooltip");
})
.on("mouseout", function(d, i) {
d3.select("#tooltip").remove();
});
}
// Draws edges between nodes
function drawLinks(links) {
var scale = d3.scale.linear()
.domain(d3.extent(links, function(d, i) {
return d.value;
}))
.range([1, 6]);
// https://github.com/mbostock/d3/wiki/Force-Layout#wiki-links
d3.select("#plot").selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "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;
})
.style("stroke-width", function(d, i) {
return scale(d.value) + "px";
})
.style("stroke-dasharray", function(d, i) {
return (d.value <= 1) ? "2, 2" : "none";
}).on("mouseover", function(d, i) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
var tooltip = d3.select("#plot")
.append("text")
.text(d.source.name + " -> " + d.target.name)
.attr("x", x)
.attr("y", y)
//.attr("dy", -r * 2)
.attr("id", "tooltip");
})
.on("mouseout", function(d, i) {
d3.select("#tooltip").remove();
});
}
body {
font-family: 'Source Sans Pro', sans-serif;
font-weight: 300;
}
b {
font-weight: 900;
}
.outline {
fill: none;
stroke: #888888;
stroke-width: 1px;
}
#tooltip {
font-size: 10pt;
font-weight: 900;
fill: #000000;
stroke: #ffffff;
stroke-width: 0.25px;
}
.node {
stroke: #ffffff;
stroke-weight: 1px;
}
.link {
fill: none;
stroke: #888888;
stroke-weight: 1px;
stroke-opacity: 0.5;
}
.highlight {
stroke: red;
stroke-weight: 4px;
stroke-opacity: 1.0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div align="center" id="force"></div>
回答2:
You can use the <title>
element for line
elements in the same way the linked demo does for circle
elements. For instance,
link.append("title")
.text(function(d) { return "This is my title"; });
would create such tooltips in the demo linked. Note that, according to the spec, you should ensure the title element is the first child of the parent.
Also note that this may not be an effective way to communicate with the user; hovering over a line is rather difficult, especially in a moving graph. One potential solution would be to wrap each line in a larger group with an invisible line of larger width, and add the title as the first child of the group, as the title element also works just fine with <g>
elements.
来源:https://stackoverflow.com/questions/33165265/show-tool-tip-on-links-of-force-directed-graph-in-d3js