隐藏附加的子节点 onClick()
Hide attached child nodes onClick()
我正在处理一个小的 D3.js 图,如果单击较大的父节点,我想隐藏连接的较小节点。到目前为止,我尝试了几种方法,首先过滤链接以在之后接收源节点可能是我最好的尝试。
不幸的是我收到newLinks.map is not a function error
,要么我完全误解了地图功能,要么只是错过了最后的和平。
function onClick(event, d) {
const newLinks = link.filter(link => link.target.id === d.id);
console.log(newLinks)
const newNodes = newLinks.map(link => data.nodes.find(newNode => newNode.id === link.source.id))
console.log(newNodes)
}
问题:
我怎样才能实现我所追求的目标?如果单击父节点,我想隐藏较小的附加节点。
var svg = d3.select("body").append("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight)
var data = {
"nodes": [{
"id": "A",
"type": "parent"
},
{
"id": "B",
"type": "parent"
},
{
"id": "C",
"type": "child"
},
{
"id": "D",
"type": "child"
},
{
"id": "E",
"type": "child"
},
],
"links": [{
"source": "A",
"target": "B",
"distance": 125
},
{
"source": "C",
"target": "A",
"distance": 20
},
{
"source": "D",
"target": "A",
"distance": 20
},
{
"source": "E",
"target": "B",
"distance": 20
},
]
}
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id
}).distance(function(d) {
return d.distance
}))
.force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
.force("charge", d3.forceManyBody().strength(-1000))
.force("collision", d3.forceCollide().radius(setSize))
var linksContainer = svg.append("g").attr("class", "linksContainer")
var nodesContainer = svg.append("g").attr("class", "nodesContainer")
initialize()
function initialize() {
link = linksContainer.selectAll("line")
.data(data.links)
.join("line")
.attr("class", "line")
node = nodesContainer.selectAll(".node")
.data(data.nodes, d => d.id)
.join("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
node.selectAll("circle")
.data(d => [d])
.join("circle")
.attr("r", setSize)
.on("click", onClick)
force
.nodes(data.nodes)
.on("tick", ticked)
force
.force("link")
.links(data.links)
}
function setSize(d) {
switch (d.type) {
case "parent":
return 40
case "child":
return 20
default:
return 40
}
}
function onClick(event, d) {
const newLinks = link.filter(link => link.target.id === d.id);
console.log(newLinks)
const newNodes = newLinks.map(link => data.nodes.find(newNode => newNode.id === link.source.id))
console.log(newNodes)
}
function ticked() {
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 + ")";
});
}
function dragStarted(event, d) {
if (!event.active) force.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragEnded(event, d) {
if (!event.active) force.alphaTarget(0);
d.fx = undefined;
d.fy = undefined;
}
body {
overflow: hidden;
background: #e6e7ee;
margin: 0;
}
circle {
fill: whitesmoke;
stroke: black;
}
line {
stroke: black;
}
<script src="https://d3js.org/d3.v7.js"></script>
您需要了解数据数组和 d3 选择之间的区别。在您的代码中,link
和 node
是代表圆圈和直线的 d3 选择。它们不是基础数据的表示。
但是,它们确实提供了一些在此上下文中有用的函数。例如,您可以使用 .each(...)
而不是 forEach
来遍历元素,或者 .data()
来获取 d3 选择表示为数组的对象,因此 link.data().map
是绝对是一个有效的功能。
我通过向每个节点添加 属性 isVisible
或确定可见性的 link 来实现您想要的。然而,这绝对不是最好的或唯一的方法,所以请随意探索替代方案。
var svg = d3.select("body").append("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight)
var data = {
"nodes": [{
"id": "A",
"type": "parent"
},
{
"id": "B",
"type": "parent"
},
{
"id": "C",
"type": "child"
},
{
"id": "D",
"type": "child"
},
{
"id": "E",
"type": "child"
},
],
"links": [{
"source": "A",
"target": "B",
"distance": 125
},
{
"source": "C",
"target": "A",
"distance": 20
},
{
"source": "D",
"target": "A",
"distance": 20
},
{
"source": "E",
"target": "B",
"distance": 20
},
]
}
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id
}).distance(function(d) {
return d.distance
}))
.force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
.force("charge", d3.forceManyBody().strength(-1000))
.force("collision", d3.forceCollide().radius(setSize))
var linksContainer = svg.append("g").attr("class", "linksContainer")
var nodesContainer = svg.append("g").attr("class", "nodesContainer")
initialize()
function initialize() {
const links = data.links
.filter(link => link.isVisible !== false);
const nodes = data.nodes.filter(node =>
node.isVisible !== false);
link = linksContainer.selectAll("line")
.data(links)
.join("line")
.attr("class", "line")
node = nodesContainer.selectAll(".node")
.data(nodes, d => d.id)
.join("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
node.selectAll("circle")
.data(d => [d])
.join("circle")
.attr("r", setSize)
.on("click", onClick)
force
.nodes(nodes)
.on("tick", ticked)
force
.force("link")
.links(links)
}
function setSize(d) {
switch (d.type) {
case "parent":
return 40
case "child":
return 20
default:
return 40
}
}
function onClick(event, d) {
link.data()
.forEach(link => {
link.isVisible = link.target.id === d.id;
});
const visibleNodeIds = [
d.id,
...link.data()
.filter(l => l.isVisible)
.map(l => l.source.id)
];
node.data()
.forEach(node => {
node.isVisible = visibleNodeIds.includes(node.id);
});
initialize();
}
function ticked() {
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 + ")";
});
}
function dragStarted(event, d) {
if (!event.active) force.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragEnded(event, d) {
if (!event.active) force.alphaTarget(0);
d.fx = undefined;
d.fy = undefined;
}
body {
overflow: hidden;
background: #e6e7ee;
margin: 0;
}
circle {
fill: whitesmoke;
stroke: black;
}
line {
stroke: black;
}
<script src="https://d3js.org/d3.v7.js"></script>
我正在处理一个小的 D3.js 图,如果单击较大的父节点,我想隐藏连接的较小节点。到目前为止,我尝试了几种方法,首先过滤链接以在之后接收源节点可能是我最好的尝试。
不幸的是我收到newLinks.map is not a function error
,要么我完全误解了地图功能,要么只是错过了最后的和平。
function onClick(event, d) {
const newLinks = link.filter(link => link.target.id === d.id);
console.log(newLinks)
const newNodes = newLinks.map(link => data.nodes.find(newNode => newNode.id === link.source.id))
console.log(newNodes)
}
问题: 我怎样才能实现我所追求的目标?如果单击父节点,我想隐藏较小的附加节点。
var svg = d3.select("body").append("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight)
var data = {
"nodes": [{
"id": "A",
"type": "parent"
},
{
"id": "B",
"type": "parent"
},
{
"id": "C",
"type": "child"
},
{
"id": "D",
"type": "child"
},
{
"id": "E",
"type": "child"
},
],
"links": [{
"source": "A",
"target": "B",
"distance": 125
},
{
"source": "C",
"target": "A",
"distance": 20
},
{
"source": "D",
"target": "A",
"distance": 20
},
{
"source": "E",
"target": "B",
"distance": 20
},
]
}
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id
}).distance(function(d) {
return d.distance
}))
.force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
.force("charge", d3.forceManyBody().strength(-1000))
.force("collision", d3.forceCollide().radius(setSize))
var linksContainer = svg.append("g").attr("class", "linksContainer")
var nodesContainer = svg.append("g").attr("class", "nodesContainer")
initialize()
function initialize() {
link = linksContainer.selectAll("line")
.data(data.links)
.join("line")
.attr("class", "line")
node = nodesContainer.selectAll(".node")
.data(data.nodes, d => d.id)
.join("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
node.selectAll("circle")
.data(d => [d])
.join("circle")
.attr("r", setSize)
.on("click", onClick)
force
.nodes(data.nodes)
.on("tick", ticked)
force
.force("link")
.links(data.links)
}
function setSize(d) {
switch (d.type) {
case "parent":
return 40
case "child":
return 20
default:
return 40
}
}
function onClick(event, d) {
const newLinks = link.filter(link => link.target.id === d.id);
console.log(newLinks)
const newNodes = newLinks.map(link => data.nodes.find(newNode => newNode.id === link.source.id))
console.log(newNodes)
}
function ticked() {
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 + ")";
});
}
function dragStarted(event, d) {
if (!event.active) force.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragEnded(event, d) {
if (!event.active) force.alphaTarget(0);
d.fx = undefined;
d.fy = undefined;
}
body {
overflow: hidden;
background: #e6e7ee;
margin: 0;
}
circle {
fill: whitesmoke;
stroke: black;
}
line {
stroke: black;
}
<script src="https://d3js.org/d3.v7.js"></script>
您需要了解数据数组和 d3 选择之间的区别。在您的代码中,link
和 node
是代表圆圈和直线的 d3 选择。它们不是基础数据的表示。
但是,它们确实提供了一些在此上下文中有用的函数。例如,您可以使用 .each(...)
而不是 forEach
来遍历元素,或者 .data()
来获取 d3 选择表示为数组的对象,因此 link.data().map
是绝对是一个有效的功能。
我通过向每个节点添加 属性 isVisible
或确定可见性的 link 来实现您想要的。然而,这绝对不是最好的或唯一的方法,所以请随意探索替代方案。
var svg = d3.select("body").append("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight)
var data = {
"nodes": [{
"id": "A",
"type": "parent"
},
{
"id": "B",
"type": "parent"
},
{
"id": "C",
"type": "child"
},
{
"id": "D",
"type": "child"
},
{
"id": "E",
"type": "child"
},
],
"links": [{
"source": "A",
"target": "B",
"distance": 125
},
{
"source": "C",
"target": "A",
"distance": 20
},
{
"source": "D",
"target": "A",
"distance": 20
},
{
"source": "E",
"target": "B",
"distance": 20
},
]
}
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id
}).distance(function(d) {
return d.distance
}))
.force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
.force("charge", d3.forceManyBody().strength(-1000))
.force("collision", d3.forceCollide().radius(setSize))
var linksContainer = svg.append("g").attr("class", "linksContainer")
var nodesContainer = svg.append("g").attr("class", "nodesContainer")
initialize()
function initialize() {
const links = data.links
.filter(link => link.isVisible !== false);
const nodes = data.nodes.filter(node =>
node.isVisible !== false);
link = linksContainer.selectAll("line")
.data(links)
.join("line")
.attr("class", "line")
node = nodesContainer.selectAll(".node")
.data(nodes, d => d.id)
.join("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
node.selectAll("circle")
.data(d => [d])
.join("circle")
.attr("r", setSize)
.on("click", onClick)
force
.nodes(nodes)
.on("tick", ticked)
force
.force("link")
.links(links)
}
function setSize(d) {
switch (d.type) {
case "parent":
return 40
case "child":
return 20
default:
return 40
}
}
function onClick(event, d) {
link.data()
.forEach(link => {
link.isVisible = link.target.id === d.id;
});
const visibleNodeIds = [
d.id,
...link.data()
.filter(l => l.isVisible)
.map(l => l.source.id)
];
node.data()
.forEach(node => {
node.isVisible = visibleNodeIds.includes(node.id);
});
initialize();
}
function ticked() {
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 + ")";
});
}
function dragStarted(event, d) {
if (!event.active) force.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragEnded(event, d) {
if (!event.active) force.alphaTarget(0);
d.fx = undefined;
d.fy = undefined;
}
body {
overflow: hidden;
background: #e6e7ee;
margin: 0;
}
circle {
fill: whitesmoke;
stroke: black;
}
line {
stroke: black;
}
<script src="https://d3js.org/d3.v7.js"></script>