Как отображать и скрывать ссылки и узлы при нажатии на узел в D3 Javascript

Я пытаюсь перейти по этой ссылке Javascript D3: > http://bl.ocks.org/mbostock/1093130 чтобы понять, как работает событие click. Что я пытаюсь сделать, так это при нажатии на синий узел должны отображаться два других узла и их ссылки. Когда я снова нажимаю на тот же узел, два узла и их ссылки должны скрываться. Если я нажму на один из двух других узлов, ничего не должно произойти.

Вот файл JSON:

{
"nodes": [
    {
        "name": "Node1",
        "group": 2
    },
    {
        "name": "Node2",
        "group": 1
    },
    {
        "name": "Node3",
        "group": 1
    }
],
"links": [
    {
        "source": 0,
        "target": 1,
        "value": 2
    },
    {
        "source": 0,
        "target": 2,
        "value": 2
    }
]
}

Вот мой код:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<style>

.node {
stroke: #fff;
stroke-width: 1.5px;
}

.link {
stroke: #999;
stroke-opacity: .6;
}

</style>
<body>
<p>Are you there!!!</p>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<script>

var width = 960,
height = 500;

d3.json("sample.json", function(error, graph) {

var color = d3.scale.category20();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(30)
    .size([width, height]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
force
.nodes(graph.nodes)
.links(graph.links)
.start();

var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });

var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);

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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
</body>
</html>

Может ли кто-нибудь помочь мне решить эту проблему, пожалуйста. Спасибо.


person user2864315    schedule 27.11.2013    source источник


Ответы (1)


Вы можете разбить шаги, необходимые для включения/отключения видимости соседей/ребер узла, щелкнув этот узел следующим образом:

  1. Вам нужно классифицировать ссылки на основе имен источников/целей и давать идентификаторы узлов на основе их имен. Это делает определение того, какие узлы/ссылки переключать, тривиальным.
  2. Вам нужно сохранить, является ли каждый узел в настоящее время «активным», что вы можете использовать, чтобы определить, следует ли вам скрывать/показывать соседей узла.
  3. Вам нужно добавить on("click", function(){ ... }) к каждому узлу.

Я создал JSFiddle, демонстрирующий этот подход. Основные моменты, на которые стоит обратить внимание, это то, что, во-первых, я создаю словарь для сопоставления имен узлов с именами их соседей, что упрощает определение того, какие ссылки показывать/скрывать:

// Set up dictionary of neighbors
var node2neighbors = {};
for (var i =0; i < json.nodes.length; i++){
    var name = json.nodes[i].name;
    node2neighbors[name] = json.links.filter(function(d){
            return d.source.name == name || d.target.name == name;
        }).map(function(d){
            return d.source.name == name ? d.target.name : d.source.name;
        });
}

Чтобы получить имена соседей узла n, я сначала фильтрую все ссылки, которые включают n, а затем извлекаю имя узла, который не является n, в каждой из этих ссылок.

Далее, вот логика, выполняемая, когда вы нажимаете на узел. Я использую массив узлов, чтобы отслеживать узлы, которые мы хотим включать/выключать (ОП сказал, что это поведение должно применяться только к синему узлу "Node1"):

var clickableNodes = ["Node1"];
nodes.filter(function(n){ return clickableNodes.indexOf(n.name) != -1; })
        // Determine if current node's neighbors and their links are visible
        var active   = n.active ? false : true // toggle whether node is active
        , newOpacity = active ? 0 : 1;

        // Extract node's name and the names of its neighbors
        var name     = n.name
        , neighbors  = node2neighbors[name];

        // Hide the neighbors and their links
        for (var i = 0; i < neighbors.length; i++){
            d3.select("circle#" + neighbors[i]).style("opacity", newOpacity);
            d3.selectAll("line." + neighbors[i]).style("opacity", newOpacity);
        }
        // Update whether or not the node is active
        n.active = active;
    });
person mdml    schedule 27.11.2013
comment
Большое спасибо за ваш ответ. Сейчас это работает. Кажется, мне нужно углубиться в ваши коды и получить больше понимания. Я новичок в Д3. - person user2864315; 28.11.2013
comment
Еще один вопрос, пожалуйста. В чем причина использования фильтра и карты в кодах? Не могли бы вы объяснить мне в нескольких словах? Что я мог видеть, что если нажать на один из других узлов, синий исчезает. Есть идеи, почему? - person user2864315; 28.11.2013
comment
@ user2864315: да, код прямо сейчас будет скрывать соседей и все ссылки соседей, когда вы нажимаете на любой узел. Он очень общий, поэтому, если вам нужно такое поведение только для одного конкретного узла, вы можете легко добавить оператор if, чтобы он применялся только к этому одному узлу. Кроме того, я добавлю несколько предложений, объясняющих map и filter. - person mdml; 28.11.2013
comment
@user2864315: user2864315: я добавил небольшую функцию, которая позволяет вам выбирать узлы, на которые можно щелкнуть, так что в примере можно щелкнуть только синий узел ("Node1"). - person mdml; 28.11.2013
comment
Не могли бы вы прислать мне эту функцию? - person user2864315; 28.11.2013
comment
Большое спасибо за Вашу помощь. - person user2864315; 28.11.2013
comment
Мне очень понравились и идея, и стиль этого ответа. - person VividD; 13.01.2014