2012-02-09 24 views
7

estoy añadiendo nodos de un gráfico trazado fuerza de la siguiente manera:Cómo agregar un nodo compuesto en un diseño de fuerza 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); 

¿Hay una manera de agregar elementos SVG compuestos como nodos? Es decir. Quiero añadir un hipervínculo para cada círculo, por lo que iba a necesitar algo como esto:

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

Respuesta

32

Creación de un elemento de "compuesto" es tan simple como anexar uno o más hijos a otro elemento. En su ejemplo, desea vincular sus datos a una selección de elementos <a>, y otorgar a cada <a> un único hijo <circle>.

En primer lugar, debe seleccionar "a.node" en lugar de "circle.node". Esto se debe a que sus hipervínculos serán los elementos principales. Si no hay un elemento primario obvio, y solo desea agregar elementos múltiples para cada dato, use <g>, elemento de grupo de SVG.

Luego, desea agregar un elemento <a> a cada nodo en la selección entrante. Esto crea tus hipervínculos. Después de configurar los atributos de cada hipervínculo, desea darle un hijo <circle>. Simple: solo llame al .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 Recuerde que opera principalmente en selecciones de nodos. Por lo tanto, al llamar al .append() en la selección entrante , significa que cada nodo en la selección obtiene un nuevo hijo. ¡Poderoso!

Una cosa más: SVG tiene its own <a> element, que es a lo que me refería anteriormente. ¡Esto es diferente del HTML! Normalmente, solo utiliza elementos SVG con SVG y HTML con HTML.

Gracias a @mbostock por sugerir que aclare la nomenclatura variable.

+0

Aunque entiendo por qué esto funciona para la creación, ¿no se rompe en la actualización? Como append() fusiona las selecciones de entrada y actualización, ¿no agrega un nuevo círculo a los nodos antiguos cada vez que se llama a la actualización? –

+0

He actualizado el ejemplo para hacerlo más claro. selection.append no * fusiona * ninguna selección, pero selection.enter(). append agrega automáticamente los elementos a la selección de actualización. Mi ejemplo original tenía vis.selectAll (...) .data (...) .enter(). Append (...). Esto solo agrega elementos a la selección de entrada, por lo que no hay problema; el punto clave es que la selección de entrada inicialmente solo contiene marcadores de posición para * nuevos * elementos que aún no existen. –

10

Respuesta a Jason Davies (ya que stackoverflow limita la duración de los comentarios de respuesta ...): Excelente respuesta. Sin embargo, tenga cuidado con el método de encadenamiento; normalmente desea node para referirse al elemento de anclaje exterior en lugar del elemento de círculo interno. Así que recomiendo una pequeña variación:

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); }); 

También he sustituido cx del círculo y atributos cy con una transformación en el elemento de anclaje que contiene; cualquiera de los dos funcionará. Puede tratar svg: a elements como svg: g (ambos son contenedores), lo que es bueno si quiere agregar etiquetas más adelante.

+0

¡Gracias! He modificado ligeramente el primer ejemplo (se eliminó 'var node =' ya que es redundante y potencialmente confuso como usted señala).El segundo ejemplo con variables es consistente con el tuyo, aunque estoy de acuerdo en que la transformación es probablemente más útil. –

Cuestiones relacionadas