Como removo todos os elementos filhos de um nó e depois os aplico novamente com cores e tamanhos diferentes?

87

Portanto, tenho o próximo código de gráfico de layout de força para definir nós, links e outros elementos:

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

Agora, as cores e dependências de tamanho mudaram e eu preciso redesenhar os círculos do gráfico (+ todos os elementos anexados) com cores e raios diferentes. Tendo problemas com isso.

Eu posso fazer isso:

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

mas eu tenho todas as imagens que anexei '.circles'ainda estão lá.

De qualquer forma, qualquer ajuda será apreciada, avise-me se a explicação não for clara o suficiente, tentarei consertar.

PS qual é a diferença entre graphData.nodes e d3.selectAll('.nodes')?

HotFrost
fonte

Respostas:

129

Sua resposta funcionará, mas para a posteridade, esses métodos são mais genéricos.

Remova todos os filhos do HTML:

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

Remova todos os filhos de SVG / HTML:

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

A .html("")chamada funciona com meu SVG, mas pode ser um efeito colateral do uso do innerSVG .

Glenn
fonte
3
Infelizmente .html ("") não funciona no Safari. Funciona bem em todos os outros navegadores.
glifo
1
@glyph: consulte stackoverflow.com/a/43661877/1587329 para saber a maneira oficial de fazer isso
serv-inc
8

Meu primeiro conselho é que você deve ler a d3.jsAPI sobre seleções: https://github.com/mbostock/d3/wiki/Selections

Você tem que entender como enter()funciona o comando ( API ). O fato de você ter que usá-lo para lidar com novos nós tem um significado que o ajudará.

Este é o processo básico para lidar com selection.data():

  • primeiro você deseja "anexar" alguns dados à seleção. Então você tem:

    var nodes = visualRoot.selectAll(".node")
        .data(graphData.nodes)
    
  • Em seguida, você pode modificar todos os nós cada vez que os dados são alterados (isso fará exatamente o que você deseja). Se, por exemplo, você alterar o raio dos nós antigos que estão no novo conjunto de dados que você carregou

    nodes.attr("r", function(d){return d.radius})
    
  • Então, você tem que lidar com novos nós, para isso você tem que selecionar os novos nós, é isso que selection.enter()foi feito:

    var nodesEnter = nodes.enter()
        .attr("fill", "red")
        .attr("r", function(d){return d.radius})
    
  • Finalmente, você certamente deseja remover os nós que não deseja mais, para fazer isso, você deve selecioná-los, é para isso que selection.exit()foi feito.

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

Um bom exemplo de todo o processo também pode ser encontrado no wiki da API: https://github.com/mbostock/d3/wiki/Selections#wiki-exit

Christopher Chiche
fonte
Oi Chris, obrigado pelas sugestões e pontos. O fato é que não tenho dados novos .. Os dados continuam os mesmos. É tudo a mesma coisa. Não quero fazer todo o processo de força novamente. Tanto quanto eu entendo (corrija-me se eu estiver errado). Tudo o que tenho a fazer é
HotFrost de
tudo o que tenho que fazer é encontrar elementos dom com a classe '.circle' e seus filhos. Remova eles. Encontre os elementos svg com a classe '.node' e reaplique o processo 'attach' para círculos svg e outros visuais descritos na função 'aplicar elementos visuais', mas desta vez, quando o raio for calculado, ele será calculado de forma diferente.
HotFrost de
de qualquer forma, resolvi isso muito facilmente, visualRoot.selectAll (". circle"). remove (); visualRoot.selectAll (". imagem"). remove ();
HotFrost de
7

desta forma, resolvi muito facilmente,

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

e então eu apenas readicionei os elementos visuais que foram renderizados de maneira diferente porque o código para calcular o raio e a cor tinha propriedades alteradas. Obrigado.

HotFrost
fonte
6

Se você quiser remover o próprio elemento, basta usar element.remove(), como você fez. Caso você queira apenas remover o conteúdo do elemento, mas mantenha o elemento como está, você pode usar f.ex.

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

em vez de .html("")(eu não tinha certeza de quais filhos do elemento você deseja excluir). Isso mantém o próprio elemento, mas limpa todo o conteúdo incluído . É a maneira oficial de fazer isso , então deve funcionar em vários navegadores.

PS: você queria mudar os tamanhos dos círculos. Você tentou

d3.selectAll(".circle").attr("r", newValue);
serv-inc
fonte
html(null)não está funcionando para mim no Internet Explorer 11
Robert
@Robert: "Um valor nulo limpará o conteúdo." Parece um bug. Algo relatado ao console?
serv-inc
Não, sem erros ou avisos. Apenas retorna o objeto selecionado. d3.select($0).html('')da resposta selecionada também não funciona para mim no IE, mas d3.select($0).selectAll('*').remove()funciona.
Robert
@Robert: Você quer denunciar isso ?
serv-inc
3

Para remover todos os elementos de um nó:

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
fonte
Você parece citar algo, qual é a fonte?
Christopher Chiche
Você realmente precisa remover também todos esses nós de seus próprios nós. Certamente, o método recomendado pelo DOM seria suficiente, pois os nós não estariam fora do nó atual e não precisam ser separados também.
Tatarize em
var element = document.getElementById ("top"); while (element.firstChild) {element.removeChild (element.firstChild); }
Tatarize