Как добавить составной узел в силовой макет D3?

Я добавляю узлы в граф принудительной компоновки следующим образом:

var node = vis.selectAll("circle.node")
    .data(nodes)
    .enter()
    .append("circle")
    .attr("class", "node")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr("r", 5)
    .style("fill", function(d) { return fill(d.group); })
    .call(force.drag);

Есть ли способ добавить составные элементы SVG в качестве узлов? т.е. Я хочу добавить гиперссылку для каждого круга, поэтому мне нужно что-то вроде этого:

<a href="whatever.com"><circle ...></circle></a>


person shinjin    schedule 09.02.2012    source источник


Ответы (2)


Создание «составного» элемента так же просто, как добавление одного или нескольких дочерних элементов к другому элементу. В вашем примере вы хотите связать свои данные с выбором <a> элементов и дать каждому <a> один дочерний элемент <circle>.

Прежде всего, вам нужно выбрать "a.node" вместо "circle.node". Это потому, что ваши гиперссылки будут родительскими элементами. Если нет очевидного родительского элемента, и вы просто хотите добавить несколько элементов для каждого элемента данных, используйте <g>, групповой элемент SVG.

Затем вы хотите добавить один элемент <a> к каждому узлу входящего выбора. Это создает ваши гиперссылки. После установки атрибутов каждой гиперссылки вы хотите дать ей <circle> дочерний элемент. Просто: просто позвоните .append("circle").

var node = vis.selectAll("a.node")
    .data(nodes);

// The entering selection: create the new <a> elements here.
// These elements are automatically part of the update selection in "node".
var nodeEnter = node.enter().append("a")
    .attr("class", "node")
    .attr("xlink:href", "http://whatever.com")
    .call(force.drag);

// Appends a new <circle> element to each element in nodeEnter.
nodeEnter.append("circle")
    .attr("r", 5)
    .style("fill", function(d) { return fill(d.group); })

node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

Помните, что D3 в основном работает с выборками узлов. Таким образом, вызов .append() для входящей выборки означает, что каждый узел в выборке получает новый дочерний элемент. Мощная штука!

И еще: в SVG есть свой собственный элемент <a>, вот что Я имел в виду выше. Это отличается от HTML! Как правило, вы используете только элементы SVG с SVG и HTML с HTML.

Спасибо @mbostock за предложение уточнить имена переменных.

person Jason Davies    schedule 11.02.2012
comment
Хотя я понимаю, почему это работает для создания, не ломается ли это при обновлении? Поскольку append() объединяет выбор ввода и обновления, не будет ли он добавлять новый круг к старым узлам каждый раз, когда вызывается обновление? - person phil_20686; 28.08.2014
comment
Я обновил пример, чтобы сделать его более понятным. selection.append не объединяет какие-либо выборки, но selection.enter().append автоматически добавляет элементы в обновленную выборку. В моем исходном примере было vis.selectAll(…).data(…).enter().append(…). Это только добавляет элементы к входящему выбору, так что проблем нет; ключевым моментом является то, что входящий выбор изначально содержит только заполнители для новых элементов, которые еще не существуют. - person Jason Davies; 29.08.2014

Ответ Джейсону Дэвису (поскольку stackoverflow ограничивает длину комментариев ответа…): Отличный ответ. Однако будьте осторожны с цепочкой методов; обычно вы хотите, чтобы node относился к внешнему элементу привязки, а не к элементу внутреннего круга. Поэтому я бы рекомендовал небольшой вариант:

var node = vis.selectAll("a.node")
    .data(nodes)
  .enter().append("a")
    .attr("class", "node")
    .attr("xlink:href", "http://whatever.com")
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
    .call(force.drag);

node.append("circle")
    .attr("r", 5)
    .style("fill", function(d) { return fill(d.group); });

Я также заменил атрибуты cx и cy круга на преобразование содержащего якоря элемента; любой из них будет работать. Вы можете обрабатывать элементы svg:a как svg:g (оба являются контейнерами), что хорошо, если вы хотите добавить метки позже.

person mbostock    schedule 11.02.2012
comment
Спасибо! Я немного изменил первый пример (убрал var node = , так как он избыточен и может сбивать с толку, как вы указываете). Второй пример с переменными согласуется с вашим, хотя я согласен, что преобразование, вероятно, более полезно. - person Jason Davies; 16.02.2012