Comment supprimer tous les éléments enfants d'un nœud, puis les appliquer à nouveau avec une couleur et une taille différentes?

87

J'ai donc le prochain code de graphique de disposition de force pour définir les nœuds, les liens et d'autres éléments:

var setLinks = function ()
{
    link = visualRoot.selectAll("line.link")
        .data(graphData.links)
        .enter().append("svg:line")
        .attr("class", "link")
        .style("stroke-width", function (d) { return nodeStrokeColorDefault; })
        .style("stroke", function (d) { return fill(d); })
        .attr("x1", function (d) { return d.source.x; })
        .attr("y1", function (d) { return d.source.y; })
        .attr("x2", function (d) { return d.target.x; })
        .attr("y2", function (d) { return d.target.y; });

    graphData.links.forEach(function (d)
    {
        linkedByIndex[d.source.index + "," + d.target.index] = 1;
    });
};


var setNodes = function ()
{
    node = visualRoot.selectAll(".node")
        .data(graphData.nodes)
        .enter().append("g")
        .attr("id", function (d) { return d.id; })
        .attr("title", function (d) { return d.name; })
        .attr("class", "node")
        .on("click", function (d, i) { loadAdditionalData(d.userID, this); })
        .call(force.drag)
        .on("mouseover", fadeNode(.1)).on("mouseout", fadeNode(1));
};

//append the visual element to the node
var appendVisualElementsToNodes = function ()
{
    node.append("circle")
        .attr("id", function (d) { return "circleid_" + d.id; })
        .attr("class", "circle")
        .attr("cx", function (d) { return 0; })
        .attr("cy", function (d) { return 0; })
        .attr("r", function (d) { return getNodeSize(d); })
        .style("fill", function (d) { return getNodeColor(d); })
        .style("stroke", function (d) { return nodeStrokeColorDefault; })
        .style("stroke-width", function (d) { return nodeStrokeWidthDefault; });

    //context menu:
    d3.selectAll(".circle").on("contextmenu", function (data, index)
    {
        d3.select('#my_custom_menu')
          .style('position', 'absolute')
          .style('left', d3.event.dx + "px")
          .style('top', d3.event.dy + "px")
          .style('display', 'block');

        d3.event.preventDefault();
    });
    //d3.select("svg").node().oncontextmenu = function(){return false;};

    node.append("image")
        .attr("class", "image")
        .attr("xlink:href", function (d) { return d.profile_image_url; })//"Images/twitterimage_2.png"
        .attr("x", -12)
        .attr("y", -12)
        .attr("width", 24)
        .attr("height", 24);

    node.append("svg:title")
        .text(function (d) { return d.name + "\n" + d.description; });
};

Maintenant, les couleurs et les dépendances de taille ont changé et je dois redessiner les cercles du graphique (+ tous les éléments ajoutés) avec une couleur et un rayon différents. Avoir un problème avec ça.

Je peux le faire:

visualRoot.selectAll(".circle").remove();

mais j'ai toutes les images que j'ai attachées '.circles'toujours là.

De toute façon, toute aide sera appréciée, faites-moi savoir si l'explication n'est pas assez claire, je vais essayer de la réparer.

PS quelle est la difference entre graphData.nodes et d3.selectAll('.nodes')?

HotFrost
la source

Réponses:

129

Votre réponse fonctionnera, mais pour la postérité, ces méthodes sont plus génériques.

Supprimez tous les enfants du HTML:

d3.select("div.parent").html("");

Supprimez tous les enfants de SVG / HTML:

d3.select("g.parent").selectAll("*").remove();

L' .html("")appel fonctionne avec mon SVG, mais cela pourrait être un effet secondaire de l'utilisation de innerSVG .

Glenn
la source
3
Malheureusement .html ("") ne fonctionne pas dans Safari. Fonctionne très bien dans tous les autres navigateurs.
glyphe
1
@glyph: voir stackoverflow.com/a/43661877/1587329 pour la façon officielle de le faire
serv-inc
8

Mon premier conseil est que vous devriez lire l' d3.jsAPI sur les sélections: https://github.com/mbostock/d3/wiki/Selections

Vous devez comprendre comment fonctionne la enter()commande ( API ). Le fait que vous deviez l'utiliser pour gérer de nouveaux nœuds a un sens qui vous aidera.

Voici le processus de base lorsque vous traitez selection.data():

  • vous voulez d'abord "attacher" des données à la sélection. Donc vous avez:

    var nodes = visualRoot.selectAll(".node")
        .data(graphData.nodes)
    
  • Ensuite, vous pouvez modifier tous les nœuds à chaque fois que les données sont modifiées (cela fera exactement ce que vous voulez). Si, par exemple, vous modifiez le rayon des anciens nœuds qui se trouvent dans le nouveau jeu de données que vous avez chargé

    nodes.attr("r", function(d){return d.radius})
    
  • Ensuite, il faut gérer de nouveaux nœuds, pour cela il faut sélectionner les nouveaux nœuds, voici ce à quoi il selection.enter()est fait:

    var nodesEnter = nodes.enter()
        .attr("fill", "red")
        .attr("r", function(d){return d.radius})
    
  • Enfin vous avez certainement envie de supprimer les nœuds dont vous ne voulez plus, pour cela, il faut les sélectionner, c'est ce qui selection.exit()est fait.

    var nodesRemove = nodes.exit().remove()
    

Un bon exemple de l'ensemble du processus peut également être trouvé sur le wiki de l'API: https://github.com/mbostock/d3/wiki/Selections#wiki-exit

Christophe Chiche
la source
HI Chris, merci pour les suggestions et les points. Le fait est que je n'ai pas de nouvelles données .. Les données sont toujours les mêmes. Tout est pareil. Je ne veux pas refaire tout le processus de force. Autant que je comprends (corrigez-moi si je me trompe). Tout ce que j'ai à faire est de
HotFrost
tout ce que j'ai à faire est de trouver des éléments dom avec la classe «.circle» et leurs enfants. Retirez-les. Trouvez les éléments svg avec la classe '.node' et réappliquez le processus 'attach' pour les cercles svg et autres visuels décrits dans la fonction 'applyvisualelements', mais cette fois, lorsque le rayon sera calculé, il sera calculé différemment.
HotFrost
de toute façon, je l'ai résolu très facilement, visualRoot.selectAll (". circle"). remove (); visualRoot.selectAll (". image"). remove ();
HotFrost
7

de cette façon, je l'ai résolu très facilement,

visualRoot.selectAll(".circle").remove();
visualRoot.selectAll(".image").remove();

puis je viens de rajouter des éléments visuels qui ont été rendus différemment parce que le code de calcul du rayon et de la couleur avait changé de propriétés. Merci.

HotFrost
la source
6

Si vous souhaitez supprimer l'élément lui-même, utilisez simplement element.remove(), comme vous l'avez fait. Si vous souhaitez simplement supprimer le contenu de l'élément, mais conserver l'élément tel quel, vous pouvez utiliser f.ex.

visualRoot.selectAll(".circle").html(null);
visualRoot.selectAll(".image").html(null);

au lieu de .html("")(je n'étais pas sûr de quel élément vous voulez supprimer les enfants). Cela conserve l'élément lui-même, mais nettoie tout le contenu inclus . C'est la façon officielle de le faire , donc devrait fonctionner avec plusieurs navigateurs.

PS: vous vouliez changer la taille des cercles. As-tu essayé

d3.selectAll(".circle").attr("r", newValue);
serv-inc
la source
html(null)ne fonctionne pas pour moi dans Internet Explorer 11
Robert
@Robert: "Une valeur nulle effacera le contenu." Cela ressemble à un bug. Quelque chose a été rapporté à la console?
serv-inc
Non, pas d'erreurs ni d'avertissements. Renvoie simplement l'objet sélectionné. d3.select($0).html('')à partir de la réponse sélectionnée ne fonctionne pas non plus pour moi dans IE, mais d3.select($0).selectAll('*').remove()fonctionne.
Robert
@Robert: Voulez-vous signaler cela ?
serv-inc
3

Pour supprimer tous les éléments d'un nœud:

var siblings = element.parentNode.childNodes;
for (var i = 0; i < siblings.length; i++) {
    for (var j = 0; j < siblings.length; j++) {
        siblings[i].parentElement.removeChild(siblings[j]);
    }
}`
jedd.ahyoung
la source
Vous parlez de citer quelque chose, quelle est la source?
Christopher Chiche
Avez-vous vraiment besoin de supprimer également tous ces nœuds de leurs propres nœuds? La méthode recommandée par DOM suffirait sûrement car les nœuds ne seraient pas hors du nœud actuel et n'ont pas besoin d'être déchirés.
Tatarize
var element = document.getElementById ("top"); while (element.firstChild) {element.removeChild (element.firstChild); }
Tatarize