2013-08-29 14 views
9

Chciałbym utworzyć funkcję javascript, która może przyjąć ogólny wybór D3 i dołączyć duplikaty do obiektu SVG.D3: Dołącz duplikaty zaznaczenia

Oto przykład minimum pracy:

<!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 powiedział, że to było niemożliwe here ale to było jakiś czas temu.

Czy ktoś ma jakiekolwiek nowe przemyślenia na temat tego, jak można to osiągnąć? Pamiętaj, że wewnątrz funkcji clone_selection nie mamy pojęcia, co element/y svg jest/są w x.

+0

Szukasz czegoś takiego jak ['cloneNode'] (http://www.w3schools.com/jsref/met_node_clonenode.asp)? –

+2

Potrzebujesz prawdziwego klonu, czy też elementy zadziałają? – nrabinowitz

+0

Dzięki za oba komentarze. Jednak żadna z nich nie działa z ogólną selekcją D3. Więcej o tym rozmawia [tutaj] (https://github.com/mbostock/d3/pull/732#issuecomment-7390693), ale przypuszczam, że metoda selection.clone(), o której wspomniał Mike Bostock, nie została jeszcze zaimplementowana. – LondonRob

Odpowiedz

9

Oto kolejna możliwość: robić rzeczy długą drogę. Rozwiązuje to problem z używaniem elementów <use>, w których nie można osobno ustawić atrybutów style lub transform.

Dziwię niesamowite d3js biblioteka nie posiadają coś takiego natywnie, ale tutaj jest mój Hack:

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

Dzięki @nrabinowitz za wskazanie mi elementów <use>.

Oto MWE komplecie z roztworu roboczego:

<!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 .. Właśnie testowałem to i okazuje się, że ta metoda nie jest idealna, ponieważ nie można zastosować atrybutów 'style' lub' transform' do obiektów ''. Oznacza to, że utknąłeś z oryginalnymi kopiami początkowego obiektu. Powrót do deski kreślarskiej ... – LondonRob

+0

Hmm - Jestem prawie pewien, że możesz zastąpić atrybuty typu 'fill',' cx', 'cy', etc na elemencie' use'. Jestem zaskoczony, że nie można użyć 'transform', ale zawsze można zawinąć element w' g' z zastosowanym transformatorem i stylem. – nrabinowitz

+0

To powinna być lepsza odpowiedź :) Wielkie dzięki. –

3

Funkcja ta sprawia, że ​​głębokie kopię selekcji D3 i zwraca wybór kopiowanych elementów:

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

Zobacz demo here.

Ta funkcja działa również wtedy, gdy zaznaczenie opcji Kopiuj zawiera wiele elementów.

Należy jednak pamiętać, że kopiuje on wszystko wraz z klasami, identyfikatorami i innymi atrybutami wszystkich elementów wewnętrznych, które mogą złamać kod, jeśli bezpośrednio odwołujesz się do wewnętrznych elementów w innym miejscu. Więc miej oko na swoje wybory. Posiadanie rodzica, który odróżnia klon od oryginału i wymienienie go w łańcuchu selekcji, zapewni ci bezpieczeństwo.

Zasadną rzeczą do zrobienia (jeśli naprawdę potrzebujesz id tak dużo) jest ustawienie identyfikatora tylko na zewnętrznym elemencie tego, co kopiujesz, gdzie możesz łatwo go zmienić, modyfikując funkcję: d3.select(clone).attr("class", "clone").attr("id", "clone-" + i).

0

Może trochę za późno na odpowiedź, ale gdy opracowałem własne rozwiązanie, znalazłem to pytanie. W ten sposób tworzę duplikaty.

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") 


    } 
}); 

utworzyć div, zrobić .each() na nim i umieścić na pętli do każdej funkcji. Liczba some_number da mi zamierzoną ilość duplikatów.

Wariacje będą możliwe, prawdopodobnie za sekundę by działały itp. Może wersja dla biedaka - nie jestem profesjonalistką. Chciałbym usłyszeć twój kanał zwrotny.