我正在尝试显示多个SVG节点的工具提示,这些节点具有基于按钮单击的相关属性。
我一直在关注创建工具提示的Scott Murrays一书,但这似乎适用于在事件悬停后显示工具提示。我的程序在某种意义上是不同的,我想按一个按钮[查找相关的标题],并让它显示具有相同标题属性的节点的工具提示。
这是我为工具提示创建的div
<div id="tooltip" class="hidden">
<p><strong>Important Label heading</strong></p>
<p><span id="value">100</span>%</p>
</div>
这是样式表:
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
这是我创建每个节点的方式:
var node = svg.selectAll(".node")
.data(graph.nodes).enter()
.append("circle")
.attr("class", function (d) { console.log("Represents: " + d.properties.represents); return "node "+ d.type.toString() })
.attr("r", radius)
.style("fill", function(d) {return d.colr; })
.call(force.drag);
// html title attribute
node.append("title")
.text(function (d) { return d.name; })
现在,当单击某个节点时,程序会生成用于查找具有相同属性的节点的按钮。 这里我们只是尝试设置按下findTitlesBtn时的工具提示。
node.on("click", function(n)
{
/// Several more lines of code ///
getTitle = n.properties.title;
//Dynamically create button for finding related Titles
if (getTitle !== undefined) {
//Create the button element
var findTitlesBtn = document.createElement("BUTTON");
//Create the button label, and add it to the button
var title = document.createTextNode("Find Related Titles");
findTitlesBtn.appendChild(title);
//Call findTitle function when button is clicked
findTitlesBtn.onclick = findTitle;
//Add button to the 'displayOptions' div inside the console
document.getElementById("displayOptions").appendChild(findTitlesBtn);
}
最后,单击按钮时调用的函数。我相信它应该在这个功能中。在我筛选节点后(基于它们是否具有相同的title属性),每个节点都会调用一个使其工具提示可见的函数。 过滤的节点是我想要显示工具提示的节点。
//Function used to find nodes with related 'Title' properties
function findTitle() {
//Return color of nodes back to normal
svg.selectAll(".node").style("fill", function(d) { return d.colr; });
//Filter through all nodes to find matching titles, color them yellow
svg.selectAll(".node")
.filter(function(d) { return d.properties.title == getTitle; })
.style('fill', 'yellow')
.onload = function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x"));
var yPosition = parseFloat(d3.select(this).attr("y"));
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#value")
.text(d);
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
}
}
然而,这不起作用。我无法在网上找到与此特定问题或文档相关的任何内容,以便我尝试做什么。我有很少的Javascript经验,这是我第一次使用SVG / D3。关于我当前实施的错误的任何建议将不胜感激!虽然我已经添加了上述代码的所有相关部分,但为了完整起见,我已附上完整的脚本。
以下是完整的源代码:
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript" src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="console">
<h2>Find all matches:</h3>
<div id="displayOptions"></div>
<div id="metainfo"></div>
</div>
<div id="graph">
</div>
<div id="tooltip" class="hidden">
<p><strong>Important Label heading</strong></p>
<p><span id="value">100</span>%</p>
</div>
<style type="text/css">
.node { stroke: #222; stroke-width: 1.5px; }
.node.Column { fill: #777; }
.node.Table { fill: #BBB; }
.node.JoinTable { fill: #999}
.node.Dataset { fill: #333}
.link { stroke: #999; stroke-width: 7px; }
div {height: 100%;}
html {height: 100%;}
body {height: 100%;}
</style>
<script type="text/javascript">
var width = 1200, height = 800, radius = 20;
var force = d3.layout.force()
.charge(-1000).linkDistance(150).size([width, height]);
var svg = d3.select("#graph").append("svg")
.attr("width", "100%").attr("height", "100%")
.attr("pointer-events", "all");
//var currRepresents = "null";
d3.json("/Justin/graph", function(error, graph) {
if (error) return;
force.nodes(graph.nodes).links(graph.links).start();
var link = svg.selectAll(".link")
.data(graph.links).enter()
.append("line").attr("class", "link");
var node = svg.selectAll(".node")
.data(graph.nodes).enter()
.append("circle")
.attr("class", function (d) { console.log("Represents: " + d.properties.represents); return "node "+ d.type.toString() })
.attr("r", radius)
.style("fill", function(d) {return d.colr; })
.call(force.drag);
// html title attribute
node.append("title")
.text(function (d) { return d.name; })
var state = false;
var last = null;
var current = null;
node.on("click", function(n)
{
//Return color of nodes back to normal
svg.selectAll(".node").style("fill", function(d) { return d.colr; });
//Remove buttons from previous mouse click
var getOptionsDiv = document.getElementById("displayOptions");
while (getOptionsDiv.hasChildNodes()) {
getOptionsDiv.removeChild(getOptionsDiv.lastChild);
}
//Get Represents property from currently selected node
currRepresents = n.properties.represents;
//Add data to meta info div
var metainf = "";
metainf = metainf.concat("Title: ", n.name, "<br/>Label: ", n.type, "<br/>Represents: ", n.properties.represents,
"<br/>Column Type: ", n.properties.columntype, "<br/>Semantic Relation: ", n.properties.semanticrelation);
console.log(metainf);
d3.select("#metainfo")
.html(metainf);
last = current;
current = d3.select(this);
current.style('fill', 'red');
last.style('fill', function(d) { return d.colr; });
getTitle = n.properties.title;
getRepresents = n.properties.represents;
getColumnType = n.properties.columntype;
getSemanticRelation = n.properties.semanticrelation;
//Dynamically create button for finding related Titles
if (getTitle !== undefined) {
//Create the button element
var findTitlesBtn = document.createElement("BUTTON");
//Create the button label, and add it to the button
var title = document.createTextNode("Find Related Titles");
findTitlesBtn.appendChild(title);
//Call findTitle function when button is clicked
findTitlesBtn.onclick = findTitle;
//Add button to the 'displayOptions' div inside the console
document.getElementById("displayOptions").appendChild(findTitlesBtn);
}
//Dynamically create button for finding related Represents
if (getRepresents !== undefined) {
//Create the button element
var findRepresentsBtn = document.createElement("BUTTON");
//Create the button label, and add it to the button
var title = document.createTextNode("Find Related Represents");
findRepresentsBtn.appendChild(title);
//Call findRepresents function when button is clicked
findRepresentsBtn.onclick = findRep;
//Add button to the 'displayOptions' div inside the console
document.getElementById("displayOptions").appendChild(findRepresentsBtn);
}
//Dynamically create button for finding related Column Types
if (getColumnType !== undefined) {
//Create the button element
var findColumnTypeBtn = document.createElement("BUTTON");
//Create the button label, and it to the button
var title = document.createTextNode("Find Related Column Types");
findColumnTypeBtn.appendChild(title);
//Call findColType function when button is clicked
findColumnTypeBtn.onclick = findColType;
//Add button to the 'displayOptions' div inside the console
document.getElementById("displayOptions").appendChild(findColumnTypeBtn);
}
//Dynamically create button for finding related Semantic Relations
if (getSemanticRelation !== undefined) {
//Create the button element
var findSemanticRelationsBtn = document.createElement("BUTTON");
//Create the button label, and it to the button
var title = document.createTextNode("Find Related Semantic Relations");
findSemanticRelationsBtn.appendChild(title);
//Call findSemRel function when button is clicked
findSemanticRelationsBtn.onclick = findSemRel;
//Add button to the 'displayOptions' div inside the console
document.getElementById("displayOptions").appendChild(findSemanticRelationsBtn);
}
});
// force feed algo ticks
force.on("tick", function() {
node.attr("cx", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.attr("cy", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
function findRepresents() {
if (currRepresents == "null") {
console.log("No node is clicked on currently!");
} else {
console.log(currRepresents);
}
}
//Function used to find nodes with related 'Title' properties
function findTitle() {
//Return color of nodes back to normal
svg.selectAll(".node").style("fill", function(d) { return d.colr; });
//Filter through all nodes to find matching titles, color them yellow
svg.selectAll(".node")
.filter(function(d) { return d.properties.title == getTitle; })
.style('fill', 'yellow')
.onload = function(d) {
//Get this bar's x/y values, then augment for the tooltip
var xPosition = parseFloat(d3.select(this).attr("x"));
var yPosition = parseFloat(d3.select(this).attr("y"));
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#value")
.text(d);
//Show the tooltip
d3.select("#tooltip").classed("hidden", false);
}
}
//Function used to find nodes with related 'Represents' properties
function findRep() {
//Return color of nodes back to normal
svg.selectAll(".node").style("fill", function(d) { return d.colr; });
//Filter through all nodes to find matching represents, color them blue
svg.selectAll(".node")
.filter(function(d) { return d.properties.represents == getRepresents; })
.style('fill', 'blue');
}
//Function used to find nodes with related 'Column Type' properties
function findColType() {
//Return color of nodes back to normal
svg.selectAll(".node").style("fill", function(d) { return d.colr; });
//Filter through all nodes to find matching column types, color them green
svg.selectAll(".node")
.filter(function(d) { return d.properties.columntype == getColumnType; })
.style('fill', 'green');
}
//Function used to find nodes with related 'Semantic Relation' properties
function findSemRel() {
//Return color of nodes back to normal
svg.selectAll(".node").style("fill", function(d) { return d.colr; });
//Filter through all nodes to find matching semantic relations, color them orange
svg.selectAll(".node")
.filter(function(d) { return d.properties.semanticrelation == getSemanticRelation; })
.style('fill', 'orange');
}
</script>
</body>
</html>
修改 以下是创建JSON的Java代码:
@SuppressWarnings("unchecked")
public Map<String, Object> graph(int limit) {
Iterator<Map<String,Object>> result = cypher.query(
"match (c)-[:BELONGS_TO]->(p) " +
"return c.title as childName, labels(c) as childType, ID(c) as childId, c as child, p.title as parentName, labels(p) as parentType, ID(p) as parentId, p as parent ",
map("1",limit));
List nodes = new ArrayList();
List rels= new ArrayList();
int i = 0;
//Iterate through each row of the resulting cypher query
while (result.hasNext())
{
//Row has a dataset, a table, and a collection of columns
Map<String, Object> row = result.next();
//Add the child node if it is not already there
Map<String, Object> childNode = map("id", row.get("childId"), "name", row.get("childName"), "type", row.get("childType"), "properties", row.get("child"));
int source = nodes.indexOf(childNode);
if (source == -1)
{
nodes.add(childNode);
source = i++;
}
//Add the parent node if it is not already there
Map<String, Object> parentNode = map("id", row.get("parentId"), "name", row.get("parentName"), "type", row.get("parentType"), "properties", row.get("parent"));
int target = nodes.indexOf(parentNode);
if (target == -1)
{
nodes.add(parentNode);
target = i++;
}
rels.add(map("source", source, "target", target));
}
return map("nodes", nodes, "links", rels);
}