2013-08-29 9 views
9

Mi piacerebbe creare una funzione javascript che può richiedere una selezione D3 generica e aggiungerne i duplicati a un oggetto SVG.D3: Aggiungi duplicati di una selezione

Ecco un esempio di lavoro minima:

<!DOCTYPE html> 
<meta charset="utf-8"> 
<body> 
<script src="http://d3js.org/d3.v3.min.js"></script> 
<script> 

svg = d3.select("body").append("svg") 
         .attr("width", 300) 
         .attr("height", 300); 

circle = svg.append("circle") 
       .attr("cx", 100) 
       .attr("cy", 100) 
       .attr("r", 20) 

function clone_selection(x, i) { 
    for (j = 0; j < i; j++) { 
    // Pseudo code: 
    // svg.append(an exact copy of x, with all the attributes) 
    } 
} 

clone_selection(circle, 5); 
</script> 

Mike Bostock ha detto che questo era impossibile here, ma che era un po 'indietro.

Qualcuno ha qualche idea nuova su come questo potrebbe essere raggiunto? Ricorda, all'interno della funzione clone_selection non abbiamo idea di quale elemento/i di svg sia/siano in x.

+0

Stai cercando qualcosa come ['cloneNode'] (http://www.w3schools.com/jsref/met_node_clonenode.asp)? –

+2

Hai bisogno di un vero clone o gli elementi funzionano? – nrabinowitz

+0

Grazie per entrambi i commenti. Tuttavia, nessuno di questi funziona con una selezione D3 generica. C'è più conversazione su questo [qui] (https://github.com/mbostock/d3/pull/732#issuecomment-7390693) ma suppongo che il metodo selection.clone() menzionato da Mike Bostock non sia stato ancora implementato. – LondonRob

risposta

9

Ecco un'altra possibilità: fare le cose nel lungo cammino. Ciò risolve il problema con l'utilizzo degli elementi <use> in cui non è possibile impostare separatamente gli attributi style o transform.

Mi sorprende l'incredibile d3js biblioteca non dispone di qualcosa come questo in modo nativo, ma qui è il mio trucco:

function clone_d3_selection(selection, i) { 
      // Assume the selection contains only one object, or just work 
      // on the first object. 'i' is an index to add to the id of the 
      // newly cloned DOM element. 
    var attr = selection.node().attributes; 
    var length = attr.length; 
    var node_name = selection.property("nodeName"); 
    var parent = d3.select(selection.node().parentNode); 
    var cloned = parent.append(node_name) 
       .attr("id", selection.attr("id") + i); 
    for (var j = 0; j < length; j++) { // Iterate on attributes and skip on "id" 
     if (attr[j].nodeName == "id") continue; 
     cloned.attr(attr[j].name,attr[j].value); 
    } 
    return cloned; 
} 
4

Grazie a @nrabinowitz per avermi indirizzato agli elementi <use>.

Ecco il MWE completo di soluzione di lavoro:

<!DOCTYPE html> 
<meta charset="utf-8"> 
<body> 
<script src="http://d3js.org/d3.v3.min.js"></script> 
<script> 

svg = d3.select("body").append("svg") 
      .attr("width", 300) 
      .attr("height", 300); 

circle = svg.append("circle") 
      .attr("id", "circleToClone") 
      .attr("cx", 100) 
      .attr("cy", 100) 
      .attr("r", 20) 

function clone_selection(object, i) { 
    for (j = 0; j < i; j++) { 
    // Here's the solution: 
    cloned_obj = svg.append("use") 
       .attr("xlink:href","#" + object.attr("id")); 
    } 
} 

clone_selection(circle, 5); 
</script> 
+0

Hmm .. Ho appena provato questo e si scopre che questo metodo non è l'ideale, perché non è possibile applicare gli attributi 'style' o' transform' agli oggetti ''. Ciò significa che sei bloccato con copie originali dell'oggetto iniziale. Torna al tavolo da disegno ... – LondonRob

+0

Hmm - Sono abbastanza sicuro che puoi sovrascrivere attributi come 'fill',' cx', 'cy', ecc. Sull'elemento' use'. Sono sorpreso che tu non possa usare 'transform', ma puoi sempre avvolgere l'elemento in un' g' con una trasformazione e uno stile applicato. – nrabinowitz

+0

Questa dovrebbe essere la risposta migliore :) Grazie mille. –

3

Questa funzione fa una copia completa di selezione d3 e restituisce la selezione di elementi copiati:

function cloneSelection(appendTo, toCopy, times) { 
    toCopy.each(function() { 
    for (var i = 0; i < times; i++) { 
     var clone = svg.node().appendChild(this.cloneNode(true)); 
     d3.select(clone).attr("class", "clone"); 
    } 
    }); 
    return appendTo.selectAll('.clone'); 
} 

Vedere la demo here.

Questa funzione funziona anche se la selezione di toCopy contiene più elementi.

Ma attenzione, copia tutto, insieme a classi, ID e altri attributi di tutti gli elementi interni, che potrebbero infrangere il codice, se si fa riferimento direttamente ad elementi interni da qualche altra parte. Quindi tieni d'occhio le tue selezioni. Avere un genitore, che distingue il clone dall'originale, e menzionarlo nella catena di selezione ti manterrà al sicuro.

Una cosa ragionevole da fare (se hai davvero bisogno di id così tanto) è avere l'id impostato solo sull'elemento esterno di ciò che stai copiando, dove puoi facilmente modificarlo modificando la funzione: d3.select(clone).attr("class", "clone").attr("id", "clone-" + i).

0

Forse un po 'in ritardo per rispondere, ma mentre ho sviluppato la mia soluzione ho trovato questa domanda. Questo è il modo in cui creo i duplicati.

d3.select("#some_id") 
     .append("div") 
     .attr("class","some_other_id") 
     .each(function(d) { 

    for (var i = 1; i < number_duplicate; i++) { 
     d3.select("#some_other_id") 
      .append("button") 
      .attr("type","button") 
      .attr("class","btn-btn") 
      .attr("id","id_here") 
      .append("div") 
      .attr("class","label") 
      .text("text_here") 


    } 
}); 

creo un div, fare .each() su di esso e mettere un ciclo for in ogni funzione. Il numero some_number mi darà la quantità prevista di duplicati.

Le variazioni saranno possibili, probabilmente un secondo per funzionerebbe ecc. Forse la versione di un uomo povero - Io non sono un professionista. Mi piacerebbe sentire la tua risposta.

Problemi correlati