web-dev-qa-db-fra.com

Ajout de plusieurs éléments non imbriqués pour chaque membre de données avec D3.js

Je voudrais créer plusieurs éléments non imbriqués en utilisant d3 pour créer une structure comme celle-ci:

    <div id="parent">
        <p> from data[0] </p>
        <p> from data[0] </p>

        <p> from data[1] </p>
        <p> from data[1] </p>

        <p> from data[2] </p>
        <p> from data[2] </p>
    </div>

la création de structures imbriquées irait quelque chose comme

    d3.select('#parent').selectAll('p').data(data).enter().
           append('p')...append('p')

mais j'aimerais conserver la sélection d'origine même après l'ajout, pour pouvoir continuer à ajouter à l'élément parent. Je vous remercie!

42
user4815162342

La manière idomatique de faire est avec imbrication:

var divs = d3.select('#parent').selectAll('p').data(data).enter().append('div');

divs.append('p')
divs.append('p')

Ce qui crée:

<div id="parent">
  <div>
    <p> from data[0] </p>
    <p> from data[0] </p>
  </div>

  <div>
    <p> from data[1] </p>
    <p> from data[1] </p>
  </div>

  <div>
    <p> from data[2] </p>
    <p> from data[2] </p>
  </div>
</div>

Si cela ne fonctionne pas, enregistrez votre sélection et ajoutez à plusieurs reprises:

var enterSelection = d3.select('#parent').selectAll('p').data(data).enter();

enterSelection.append('p')
enterSelection.append('p')

puis triez ce que vous avez ajouté:

d3.select('#parent').selectAll('p').sort(function(a, b){ return a.index - b.index; })

Vous devez ajouter une propriété index à chaque élément de data qui décrit l'ordre de tri. La i normale n'est définie que dans le contexte d'une sélection particulière, qui est perdue lors de la nouvelle sélection. 

55
Adam Pearce

Utilisez append() pour le premier élément et insert() pour le second. Ceci élimine le besoin de trier après (merci au commentaire de @ scuerda pour l'avoir signalé) ( JSFiddle ):

data = [{}, {}, {}];
var enterSelection = d3.select('#parent').selectAll('p').data(data).enter()

enterSelection.append('p').text(function(d, i) {return 'from data[' + i + ']'})
enterSelection.insert('p').text(function(d, i) {return 'from data[' + i + ']'})

Cela vous donnera la structure exacte demandée:

<p>from data[0]</p>

<p>from data[0]</p>

<p>from data[1]</p>

<p>from data[1]</p>

<p>from data[2]</p>

<p>from data[2]</p>
14
Caleb Clark

Vous pouvez également le faire en un seul cycle de sélection/entrée, comme suit

d3.select('#parent').selectAll('p').data(data).enter().
append('p').text(function(d) {return 'from data[0]')}).
select(function() { return this.parentNode; }).
append('p').text(function(d) {return 'from data[0]')});
12
Karl Koster

Au lieu d'un .append(),

Vous pouvez également encapsuler une fonction qui crée un nouveau contenu dans un .html()

d3.select('#parent')
  .selectAll('div')
    .data(data)
  .enter()
    .append('div')
    .html(function(d) {return "<p>" + from data[0] + "<p>" etc..... ;});
7
Nate Daubert

Semblable à ce qui précède, mais dans un autre langage. Ceci est plus vrai de l’approche de sélections imbriquées et élimine le besoin de trier ou d’insérer:

var divs = d3.select('#parent');

var ps = divs.selectAll('#parent > div')
    .data(d3.range(3)).enter().append('div');

ps.append('p').html(function(d,i) { return 'from data[' + i + ']'; });
ps.append('p').html(function(d,i) { return 'from data[' + i + ']'; });

Plus élégamment (et probablement plus rapide):

var divs = d3.select('#parent');

  var ps = divs.selectAll('#parent > div')
    .data(d3.range(3)).enter().append('div');

    ps.append('p').html(function(d,i) { return 'from data[' + i + ']'; })
    .select(function() { return this.parentNode.appendChild(this.cloneNode(true)); });