d3js 搜索节点及其子节点
d3js search nodes and its child
我的项目是做一个带有文本框的力导向图来搜索任何节点。当用户搜索某些内容时,我想突出显示该节点及其相邻节点,就像您将鼠标悬停在节点上时 here 所做的那样。
我的source code:
var width = 900,
height = 590;
// var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom");
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append('g');
d3.json("https://raw.githubusercontent.com/khalidal-walid/fyp/master/countries.json", function(data) {
// Extract the nodes and links from the data.
var nodes = data["nodes"];
var links = data["links"];
//CONNECTIONS
var hash_lookup = [];
nodes.forEach(function(d, i) {
hash_lookup[d.country] = d;
});
links.forEach(function(d, i) {
d.source = hash_lookup[d.source];
d.target = hash_lookup[d.target];
});
// Now we create a force layout object and define its properties.
// Those include the dimensions of the visualization and the arrays of nodes and links.
var force = d3.layout.force()
.size([width, height])
.nodes(d3.values(nodes))
.links(links)
.on('tick', tick)
.linkDistance(100)
.gravity(.15)
.friction(.8)
.linkStrength(1)
.charge(-425)
.chargeDistance(600)
.start();
//LINKS
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
//NODES
var node = svg.selectAll('.node')
.data(force.nodes())
.enter().append('circle')
.attr('class', 'node')
.attr('r', width * 0.01)
//LABELS
var text_center = false;
var nominal_text_size = 12;
var max_text_size = 22;
var nominal_base_node_size = 8;
var max_base_node_size = 36;
var size = d3.scale.pow().exponent(1)
.domain([1, 100])
.range([8, 24]);
var text = svg.selectAll(".text")
.data(nodes)
.enter().append("text")
.attr("dy", ".35em")
.style("font-size", nominal_text_size + "px")
if (text_center)
text.text(function(d) {
return d.code;
})
.style("text-anchor", "middle");
else
text.attr("dx", function(d) {
return (size(d.size) || nominal_base_node_size);
})
.text(function(d) {
return '\u2002' + d.code;
});
//ZOOM AND PAN
// function redraw() {
// svg.attr("transform",
// "translate(" + d3.event.translate + ")"
// + " scale(" + d3.event.scale + ")");
// }
// var drag = force.drag()
// .on("dragstart", function(d) {
// d3.event.sourceEvent.stopPropagation();
// });
//NODES IN SPACE
function tick(e) {
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
})
.call(force.drag);
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;
});
};
//AUTOCOMPLETE SEARCH
var optArray = [];
for (var i = 0; i < nodes.length - 1; i++) {
optArray.push(nodes[i].code);
}
optArray = optArray.sort();
window.searchNode = searchNode;
function searchNode() {
var selectedVal = document.getElementById('search').value;
if (selectedVal == 'none') {} else {
var selected = node.filter(function(d, i) {
return d.code != selectedVal;
});
var selectedText = text.filter(function(d, i) {
return d.code != selectedVal;
});
selected.style("opacity", "0");
selectedText.style("opacity", "0");
var link = svg.selectAll(".link")
link.style("opacity", "0");
d3.selectAll(".node, .link, .text").transition()
.duration(3000)
.style("opacity", '1');
var selectedNode = node
.filter(function(d, i) {
return d.code == selectedVal;
})
.datum();
var scale = zoom.scale();
var desiredPosition = {
x: 200,
y: 200
}; // constants, set to svg center point
zoom.translate([desiredPosition.x - selectedNode.x * scale, desiredPosition.y - selectedNode.y * scale]);
zoom.event(svg);
}
}
})
.node {
stroke: #aaa;
stroke-width: 2px;
}
.link {
stroke: #aaa;
stroke-width: 2px;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script>
<div class="ui-widget">
<input id="search" , size='54' , autocomplete="off">
<button type="button" onclick="searchNode()">Search</button>
</div>
我想下面就是你想要的。我首先检查是否有任何节点与搜索词匹配,如果是,我找到所有不涉及匹配节点的链接,并将它们隐藏。同时,我填充 highlightCodes
,一个包含匹配代码和所有直接邻居代码的数组。然后我隐藏所有与 highlightCodes
数组不匹配的节点。
var width = 900,
height = 590;
// var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom");
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append('g');
d3.json("https://raw.githubusercontent.com/khalidal-walid/fyp/master/countries.json", function(data) {
// Extract the nodes and links from the data.
var nodes = data["nodes"];
var links = data["links"];
//CONNECTIONS
var hash_lookup = [];
nodes.forEach(function(d, i) {
hash_lookup[d.country] = d;
});
links.forEach(function(d, i) {
d.source = hash_lookup[d.source];
d.target = hash_lookup[d.target];
});
// Now we create a force layout object and define its properties.
// Those include the dimensions of the visualization and the arrays of nodes and links.
var force = d3.layout.force()
.size([width, height])
.nodes(d3.values(nodes))
.links(links)
.on('tick', tick)
.linkDistance(100)
.gravity(.15)
.friction(.8)
.linkStrength(1)
.charge(-425)
.chargeDistance(600)
.start();
//LINKS
var link = svg.selectAll('.link')
.data(links)
.enter()
.append('line')
.attr('class', 'link');
//NODES
var node = svg.selectAll('.node')
.data(force.nodes())
.enter()
.append('circle')
.attr('class', 'node')
.attr('r', width * 0.01);
//LABELS
var text_center = false;
var nominal_text_size = 12;
var max_text_size = 22;
var nominal_base_node_size = 8;
var max_base_node_size = 36;
var size = d3.scale.pow()
.exponent(1)
.domain([1, 100])
.range([8, 24]);
var text = svg.selectAll(".text")
.data(nodes)
.enter()
.append("text")
.attr("dy", ".35em")
.style("font-size", nominal_text_size + "px");
if(text_center) {
text.text(function(d) {
return d.code;
})
.style("text-anchor", "middle");
} else {
text.attr("dx", function(d) {
return (size(d.size) || nominal_base_node_size);
})
.text(function(d) {
return '\u2002' + d.code;
});
}
//ZOOM AND PAN
// function redraw() {
// svg.attr("transform",
// "translate(" + d3.event.translate + ")"
// + " scale(" + d3.event.scale + ")");
// }
// var drag = force.drag()
// .on("dragstart", function(d) {
// d3.event.sourceEvent.stopPropagation();
// });
//NODES IN SPACE
function tick(e) {
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
})
.call(force.drag);
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;
});
};
//AUTOCOMPLETE SEARCH
window.searchNode = searchNode;
function resetSelection() {
node.style('opacity', null);
link.style('opacity', null);
text.style('opacity', null);
}
function searchNode() {
var selectedVal = document.getElementById('search').value;
if(selectedVal == 'none') {
return resetSelection();
}
var matchingNode = nodes.find(function(d) {
return d.code === selectedVal;
});
if(matchingNode === undefined) {
return resetSelection();
}
var matchingCode = matchingNode.code;
var highlightCodes = [];
link
.filter(function(d) {
if(d.source.code === matchingCode ||
d.target.code === matchingCode) {
highlightCodes.push(d.source.code, d.target.code);
return false;
}
return true;
})
.transition()
.duration(500)
.style("opacity", 0.3);
d3.selectAll(".node, .text")
.filter(function(d) {
return highlightCodes.indexOf(d.code) === -1;
})
.transition()
.duration(500)
.style("opacity", 0.3);
}
});
.node {
stroke: #aaa;
stroke-width: 2px;
}
.link {
stroke: #aaa;
stroke-width: 2px;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script>
<div class="ui-widget">
<input id="search" size='54' autocomplete="off">
<button type="button" onclick="searchNode()">Search</button>
</div>
我的项目是做一个带有文本框的力导向图来搜索任何节点。当用户搜索某些内容时,我想突出显示该节点及其相邻节点,就像您将鼠标悬停在节点上时 here 所做的那样。
我的source code:
var width = 900,
height = 590;
// var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom");
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append('g');
d3.json("https://raw.githubusercontent.com/khalidal-walid/fyp/master/countries.json", function(data) {
// Extract the nodes and links from the data.
var nodes = data["nodes"];
var links = data["links"];
//CONNECTIONS
var hash_lookup = [];
nodes.forEach(function(d, i) {
hash_lookup[d.country] = d;
});
links.forEach(function(d, i) {
d.source = hash_lookup[d.source];
d.target = hash_lookup[d.target];
});
// Now we create a force layout object and define its properties.
// Those include the dimensions of the visualization and the arrays of nodes and links.
var force = d3.layout.force()
.size([width, height])
.nodes(d3.values(nodes))
.links(links)
.on('tick', tick)
.linkDistance(100)
.gravity(.15)
.friction(.8)
.linkStrength(1)
.charge(-425)
.chargeDistance(600)
.start();
//LINKS
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
//NODES
var node = svg.selectAll('.node')
.data(force.nodes())
.enter().append('circle')
.attr('class', 'node')
.attr('r', width * 0.01)
//LABELS
var text_center = false;
var nominal_text_size = 12;
var max_text_size = 22;
var nominal_base_node_size = 8;
var max_base_node_size = 36;
var size = d3.scale.pow().exponent(1)
.domain([1, 100])
.range([8, 24]);
var text = svg.selectAll(".text")
.data(nodes)
.enter().append("text")
.attr("dy", ".35em")
.style("font-size", nominal_text_size + "px")
if (text_center)
text.text(function(d) {
return d.code;
})
.style("text-anchor", "middle");
else
text.attr("dx", function(d) {
return (size(d.size) || nominal_base_node_size);
})
.text(function(d) {
return '\u2002' + d.code;
});
//ZOOM AND PAN
// function redraw() {
// svg.attr("transform",
// "translate(" + d3.event.translate + ")"
// + " scale(" + d3.event.scale + ")");
// }
// var drag = force.drag()
// .on("dragstart", function(d) {
// d3.event.sourceEvent.stopPropagation();
// });
//NODES IN SPACE
function tick(e) {
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
})
.call(force.drag);
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;
});
};
//AUTOCOMPLETE SEARCH
var optArray = [];
for (var i = 0; i < nodes.length - 1; i++) {
optArray.push(nodes[i].code);
}
optArray = optArray.sort();
window.searchNode = searchNode;
function searchNode() {
var selectedVal = document.getElementById('search').value;
if (selectedVal == 'none') {} else {
var selected = node.filter(function(d, i) {
return d.code != selectedVal;
});
var selectedText = text.filter(function(d, i) {
return d.code != selectedVal;
});
selected.style("opacity", "0");
selectedText.style("opacity", "0");
var link = svg.selectAll(".link")
link.style("opacity", "0");
d3.selectAll(".node, .link, .text").transition()
.duration(3000)
.style("opacity", '1');
var selectedNode = node
.filter(function(d, i) {
return d.code == selectedVal;
})
.datum();
var scale = zoom.scale();
var desiredPosition = {
x: 200,
y: 200
}; // constants, set to svg center point
zoom.translate([desiredPosition.x - selectedNode.x * scale, desiredPosition.y - selectedNode.y * scale]);
zoom.event(svg);
}
}
})
.node {
stroke: #aaa;
stroke-width: 2px;
}
.link {
stroke: #aaa;
stroke-width: 2px;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script>
<div class="ui-widget">
<input id="search" , size='54' , autocomplete="off">
<button type="button" onclick="searchNode()">Search</button>
</div>
我想下面就是你想要的。我首先检查是否有任何节点与搜索词匹配,如果是,我找到所有不涉及匹配节点的链接,并将它们隐藏。同时,我填充 highlightCodes
,一个包含匹配代码和所有直接邻居代码的数组。然后我隐藏所有与 highlightCodes
数组不匹配的节点。
var width = 900,
height = 590;
// var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom");
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append('g');
d3.json("https://raw.githubusercontent.com/khalidal-walid/fyp/master/countries.json", function(data) {
// Extract the nodes and links from the data.
var nodes = data["nodes"];
var links = data["links"];
//CONNECTIONS
var hash_lookup = [];
nodes.forEach(function(d, i) {
hash_lookup[d.country] = d;
});
links.forEach(function(d, i) {
d.source = hash_lookup[d.source];
d.target = hash_lookup[d.target];
});
// Now we create a force layout object and define its properties.
// Those include the dimensions of the visualization and the arrays of nodes and links.
var force = d3.layout.force()
.size([width, height])
.nodes(d3.values(nodes))
.links(links)
.on('tick', tick)
.linkDistance(100)
.gravity(.15)
.friction(.8)
.linkStrength(1)
.charge(-425)
.chargeDistance(600)
.start();
//LINKS
var link = svg.selectAll('.link')
.data(links)
.enter()
.append('line')
.attr('class', 'link');
//NODES
var node = svg.selectAll('.node')
.data(force.nodes())
.enter()
.append('circle')
.attr('class', 'node')
.attr('r', width * 0.01);
//LABELS
var text_center = false;
var nominal_text_size = 12;
var max_text_size = 22;
var nominal_base_node_size = 8;
var max_base_node_size = 36;
var size = d3.scale.pow()
.exponent(1)
.domain([1, 100])
.range([8, 24]);
var text = svg.selectAll(".text")
.data(nodes)
.enter()
.append("text")
.attr("dy", ".35em")
.style("font-size", nominal_text_size + "px");
if(text_center) {
text.text(function(d) {
return d.code;
})
.style("text-anchor", "middle");
} else {
text.attr("dx", function(d) {
return (size(d.size) || nominal_base_node_size);
})
.text(function(d) {
return '\u2002' + d.code;
});
}
//ZOOM AND PAN
// function redraw() {
// svg.attr("transform",
// "translate(" + d3.event.translate + ")"
// + " scale(" + d3.event.scale + ")");
// }
// var drag = force.drag()
// .on("dragstart", function(d) {
// d3.event.sourceEvent.stopPropagation();
// });
//NODES IN SPACE
function tick(e) {
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
})
.call(force.drag);
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;
});
};
//AUTOCOMPLETE SEARCH
window.searchNode = searchNode;
function resetSelection() {
node.style('opacity', null);
link.style('opacity', null);
text.style('opacity', null);
}
function searchNode() {
var selectedVal = document.getElementById('search').value;
if(selectedVal == 'none') {
return resetSelection();
}
var matchingNode = nodes.find(function(d) {
return d.code === selectedVal;
});
if(matchingNode === undefined) {
return resetSelection();
}
var matchingCode = matchingNode.code;
var highlightCodes = [];
link
.filter(function(d) {
if(d.source.code === matchingCode ||
d.target.code === matchingCode) {
highlightCodes.push(d.source.code, d.target.code);
return false;
}
return true;
})
.transition()
.duration(500)
.style("opacity", 0.3);
d3.selectAll(".node, .text")
.filter(function(d) {
return highlightCodes.indexOf(d.code) === -1;
})
.transition()
.duration(500)
.style("opacity", 0.3);
}
});
.node {
stroke: #aaa;
stroke-width: 2px;
}
.link {
stroke: #aaa;
stroke-width: 2px;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script>
<div class="ui-widget">
<input id="search" size='54' autocomplete="off">
<button type="button" onclick="searchNode()">Search</button>
</div>