Best Jeanist
Best Jeanist

Reputation: 1129

D3 On-Click zoom to a specific node

I have this code below that consist of a D3 Force directed graph with a button that highlights the heaviest node. What i'm currently trying to accomplish is that when i click the button it will zoom into that node. Is there an easy way to accomplish this? Any help would be greatly appreciated thank you!

var graph = {"nodes":[{"id":"Myriel","group":1},{"id":"Napoleon","group":1},{"id":"Mlle.Baptistine","group":1},{"id":"Mme.Magloire","group":1},{"id":"CountessdeLo","group":1},{"id":"Geborand","group":1},{"id":"Champtercier","group":1},{"id":"Cravatte","group":1},{"id":"Count","group":1},{"id":"OldMan","group":1},{"id":"Labarre","group":2},{"id":"Valjean","group":2},{"id":"Marguerite","group":3},{"id":"Mme.deR","group":2},{"id":"Isabeau","group":2},{"id":"Gervais","group":2},{"id":"Tholomyes","group":3},{"id":"Listolier","group":3},{"id":"Fameuil","group":3},{"id":"Blacheville","group":3},{"id":"Favourite","group":3},{"id":"Dahlia","group":3},{"id":"Zephine","group":3},{"id":"Fantine","group":3},{"id":"Mme.Thenardier","group":4},{"id":"Thenardier","group":4},{"id":"Cosette","group":5},{"id":"Javert","group":4},{"id":"Fauchelevent","group":0},{"id":"Bamatabois","group":2},{"id":"Perpetue","group":3},{"id":"Simplice","group":2},{"id":"Scaufflaire","group":2},{"id":"Woman1","group":2},{"id":"Judge","group":2},{"id":"Champmathieu","group":2},{"id":"Brevet","group":2},{"id":"Chenildieu","group":2},{"id":"Cochepaille","group":2},{"id":"Pontmercy","group":4},{"id":"Boulatruelle","group":6},{"id":"Eponine","group":4},{"id":"Anzelma","group":4},{"id":"Woman2","group":5},{"id":"MotherInnocent","group":0},{"id":"Gribier","group":0},{"id":"Jondrette","group":7},{"id":"Mme.Burgon","group":7},{"id":"Gavroche","group":8},{"id":"Gillenormand","group":5},{"id":"Magnon","group":5},{"id":"Mlle.Gillenormand","group":5},{"id":"Mme.Pontmercy","group":5},{"id":"Mlle.Vaubois","group":5},{"id":"Lt.Gillenormand","group":5},{"id":"Marius","group":8},{"id":"BaronessT","group":5},{"id":"Mabeuf","group":8},{"id":"Enjolras","group":8},{"id":"Combeferre","group":8},{"id":"Prouvaire","group":8},{"id":"Feuilly","group":8},{"id":"Courfeyrac","group":8},{"id":"Bahorel","group":8},{"id":"Bossuet","group":8},{"id":"Joly","group":8},{"id":"Grantaire","group":8},{"id":"MotherPlutarch","group":9},{"id":"Gueulemer","group":4},{"id":"Babet","group":4},{"id":"Claquesous","group":4},{"id":"Montparnasse","group":4},{"id":"Toussaint","group":5},{"id":"Child1","group":10},{"id":"Child2","group":10},{"id":"Brujon","group":4},{"id":"Mme.Hucheloup","group":8}],"links":[{"source":"Napoleon","target":"Myriel","value":1,"type":"A"},{"source":"Mlle.Baptistine","target":"Myriel","value":8,"type":"A"},{"source":"Mme.Magloire","target":"Myriel","value":10,"type":"A"},{"source":"Mme.Magloire","target":"Mlle.Baptistine","value":6,"type":"A"},{"source":"CountessdeLo","target":"Myriel","value":1,"type":"A"},{"source":"Geborand","target":"Myriel","value":1,"type":"A"},{"source":"Champtercier","target":"Myriel","value":1,"type":"A"},{"source":"Cravatte","target":"Myriel","value":1,"type":"A"},{"source":"Count","target":"Myriel","value":2,"type":"A"},{"source":"OldMan","target":"Myriel","value":1,"type":"A"},{"source":"Valjean","target":"Labarre","value":1,"type":"A"},{"source":"Valjean","target":"Mme.Magloire","value":3,"type":"A"},{"source":"Valjean","target":"Mlle.Baptistine","value":3,"type":"A"},{"source":"Valjean","target":"Myriel","value":5,"type":"A"},{"source":"Marguerite","target":"Valjean","value":1,"type":"A"},{"source":"Mme.deR","target":"Valjean","value":1,"type":"A"},{"source":"Isabeau","target":"Valjean","value":1,"type":"A"},{"source":"Gervais","target":"Valjean","value":1,"type":"A"},{"source":"Listolier","target":"Tholomyes","value":4,"type":"A"},{"source":"Fameuil","target":"Tholomyes","value":4,"type":"A"},{"source":"Fameuil","target":"Listolier","value":4,"type":"A"},{"source":"Blacheville","target":"Tholomyes","value":4,"type":"A"},{"source":"Blacheville","target":"Listolier","value":4,"type":"A"},{"source":"Blacheville","target":"Fameuil","value":4,"type":"A"},{"source":"Favourite","target":"Tholomyes","value":3,"type":"A"},{"source":"Favourite","target":"Listolier","value":3,"type":"A"},{"source":"Favourite","target":"Fameuil","value":3,"type":"A"},{"source":"Favourite","target":"Blacheville","value":4,"type":"A"},{"source":"Dahlia","target":"Tholomyes","value":3,"type":"A"},{"source":"Dahlia","target":"Listolier","value":3,"type":"A"},{"source":"Dahlia","target":"Fameuil","value":3,"type":"A"},{"source":"Dahlia","target":"Blacheville","value":3,"type":"A"},{"source":"Dahlia","target":"Favourite","value":5,"type":"A"},{"source":"Zephine","target":"Tholomyes","value":3,"type":"A"},{"source":"Zephine","target":"Listolier","value":3,"type":"A"},{"source":"Zephine","target":"Fameuil","value":3,"type":"A"},{"source":"Zephine","target":"Blacheville","value":3,"type":"A"},{"source":"Zephine","target":"Favourite","value":4,"type":"A"},{"source":"Zephine","target":"Dahlia","value":4,"type":"A"},{"source":"Fantine","target":"Tholomyes","value":3,"type":"A"},{"source":"Fantine","target":"Listolier","value":3,"type":"A"},{"source":"Fantine","target":"Fameuil","value":3,"type":"A"},{"source":"Fantine","target":"Blacheville","value":3,"type":"A"},{"source":"Fantine","target":"Favourite","value":4,"type":"A"},{"source":"Fantine","target":"Dahlia","value":4,"type":"A"},{"source":"Fantine","target":"Zephine","value":4,"type":"A"},{"source":"Fantine","target":"Marguerite","value":2,"type":"A"},{"source":"Fantine","target":"Valjean","value":9,"type":"A"},{"source":"Mme.Thenardier","target":"Fantine","value":2,"type":"A"},{"source":"Mme.Thenardier","target":"Valjean","value":7,"type":"A"},{"source":"Thenardier","target":"Mme.Thenardier","value":13,"type":"A"},{"source":"Thenardier","target":"Fantine","value":1,"type":"A"},{"source":"Thenardier","target":"Valjean","value":12,"type":"A"},{"source":"Cosette","target":"Mme.Thenardier","value":4,"type":"A"},{"source":"Cosette","target":"Valjean","value":31,"type":"A"},{"source":"Cosette","target":"Tholomyes","value":1,"type":"A"},{"source":"Cosette","target":"Thenardier","value":1,"type":"A"},{"source":"Javert","target":"Valjean","value":17,"type":"A"},{"source":"Javert","target":"Fantine","value":5,"type":"A"},{"source":"Javert","target":"Thenardier","value":5,"type":"A"},{"source":"Javert","target":"Mme.Thenardier","value":1,"type":"A"},{"source":"Javert","target":"Cosette","value":1,"type":"A"},{"source":"Fauchelevent","target":"Valjean","value":8,"type":"A"},{"source":"Fauchelevent","target":"Javert","value":1,"type":"A"},{"source":"Bamatabois","target":"Fantine","value":1,"type":"A"},{"source":"Bamatabois","target":"Javert","value":1,"type":"A"},{"source":"Bamatabois","target":"Valjean","value":2,"type":"A"},{"source":"Perpetue","target":"Fantine","value":1,"type":"A"},{"source":"Simplice","target":"Perpetue","value":2,"type":"A"},{"source":"Simplice","target":"Valjean","value":3,"type":"A"},{"source":"Simplice","target":"Fantine","value":2,"type":"A"},{"source":"Simplice","target":"Javert","value":1,"type":"A"},{"source":"Scaufflaire","target":"Valjean","value":1,"type":"A"},{"source":"Woman1","target":"Valjean","value":2,"type":"A"},{"source":"Woman1","target":"Javert","value":1,"type":"A"},{"source":"Judge","target":"Valjean","value":3,"type":"A"},{"source":"Judge","target":"Bamatabois","value":2,"type":"A"},{"source":"Champmathieu","target":"Valjean","value":3,"type":"A"},{"source":"Champmathieu","target":"Judge","value":3,"type":"A"},{"source":"Champmathieu","target":"Bamatabois","value":2,"type":"A"},{"source":"Brevet","target":"Judge","value":2,"type":"A"},{"source":"Brevet","target":"Champmathieu","value":2,"type":"A"},{"source":"Brevet","target":"Valjean","value":2,"type":"A"},{"source":"Brevet","target":"Bamatabois","value":1,"type":"A"},{"source":"Chenildieu","target":"Judge","value":2,"type":"A"},{"source":"Chenildieu","target":"Champmathieu","value":2,"type":"A"},{"source":"Chenildieu","target":"Brevet","value":2,"type":"A"},{"source":"Chenildieu","target":"Valjean","value":2,"type":"A"},{"source":"Chenildieu","target":"Bamatabois","value":1,"type":"A"},{"source":"Cochepaille","target":"Judge","value":2,"type":"A"},{"source":"Cochepaille","target":"Champmathieu","value":2,"type":"A"},{"source":"Cochepaille","target":"Brevet","value":2,"type":"A"},{"source":"Cochepaille","target":"Chenildieu","value":2,"type":"A"},{"source":"Cochepaille","target":"Valjean","value":2,"type":"A"},{"source":"Cochepaille","target":"Bamatabois","value":1,"type":"A"},{"source":"Pontmercy","target":"Thenardier","value":1,"type":"A"},{"source":"Boulatruelle","target":"Thenardier","value":1,"type":"A"},{"source":"Eponine","target":"Mme.Thenardier","value":2,"type":"A"},{"source":"Eponine","target":"Thenardier","value":3,"type":"A"},{"source":"Anzelma","target":"Eponine","value":2,"type":"A"},{"source":"Anzelma","target":"Thenardier","value":2,"type":"A"},{"source":"Anzelma","target":"Mme.Thenardier","value":1,"type":"A"},{"source":"Woman2","target":"Valjean","value":3,"type":"A"},{"source":"Woman2","target":"Cosette","value":1,"type":"A"},{"source":"Woman2","target":"Javert","value":1,"type":"A"},{"source":"MotherInnocent","target":"Fauchelevent","value":3,"type":"A"},{"source":"MotherInnocent","target":"Valjean","value":1,"type":"A"},{"source":"Gribier","target":"Fauchelevent","value":2,"type":"A"},{"source":"Mme.Burgon","target":"Jondrette","value":1,"type":"A"},{"source":"Gavroche","target":"Mme.Burgon","value":2,"type":"A"},{"source":"Gavroche","target":"Thenardier","value":1,"type":"A"},{"source":"Gavroche","target":"Javert","value":1,"type":"A"},{"source":"Gavroche","target":"Valjean","value":1,"type":"A"},{"source":"Gillenormand","target":"Cosette","value":3,"type":"A"},{"source":"Gillenormand","target":"Valjean","value":2,"type":"A"},{"source":"Magnon","target":"Gillenormand","value":1,"type":"A"},{"source":"Magnon","target":"Mme.Thenardier","value":1,"type":"A"},{"source":"Mlle.Gillenormand","target":"Gillenormand","value":9,"type":"A"},{"source":"Mlle.Gillenormand","target":"Cosette","value":2,"type":"A"},{"source":"Mlle.Gillenormand","target":"Valjean","value":2,"type":"A"},{"source":"Mme.Pontmercy","target":"Mlle.Gillenormand","value":1,"type":"A"},{"source":"Mme.Pontmercy","target":"Pontmercy","value":1,"type":"A"},{"source":"Mlle.Vaubois","target":"Mlle.Gillenormand","value":1,"type":"A"},{"source":"Lt.Gillenormand","target":"Mlle.Gillenormand","value":2,"type":"A"},{"source":"Lt.Gillenormand","target":"Gillenormand","value":1,"type":"A"},{"source":"Lt.Gillenormand","target":"Cosette","value":1,"type":"A"},{"source":"Marius","target":"Mlle.Gillenormand","value":6,"type":"A"},{"source":"Marius","target":"Gillenormand","value":12,"type":"A"},{"source":"Marius","target":"Pontmercy","value":1,"type":"A"},{"source":"Marius","target":"Lt.Gillenormand","value":1,"type":"A"},{"source":"Marius","target":"Cosette","value":21,"type":"A"},{"source":"Marius","target":"Valjean","value":19,"type":"A"},{"source":"Marius","target":"Tholomyes","value":1,"type":"A"},{"source":"Marius","target":"Thenardier","value":2,"type":"A"},{"source":"Marius","target":"Eponine","value":5,"type":"A"},{"source":"Marius","target":"Gavroche","value":4,"type":"A"},{"source":"BaronessT","target":"Gillenormand","value":1,"type":"A"},{"source":"BaronessT","target":"Marius","value":1,"type":"A"},{"source":"Mabeuf","target":"Marius","value":1,"type":"A"},{"source":"Mabeuf","target":"Eponine","value":1,"type":"A"},{"source":"Mabeuf","target":"Gavroche","value":1,"type":"A"},{"source":"Enjolras","target":"Marius","value":7,"type":"A"},{"source":"Enjolras","target":"Gavroche","value":7,"type":"A"},{"source":"Enjolras","target":"Javert","value":6,"type":"A"},{"source":"Enjolras","target":"Mabeuf","value":1,"type":"A"},{"source":"Enjolras","target":"Valjean","value":4,"type":"A"},{"source":"Combeferre","target":"Enjolras","value":15,"type":"A"},{"source":"Combeferre","target":"Marius","value":5,"type":"A"},{"source":"Combeferre","target":"Gavroche","value":6,"type":"A"},{"source":"Combeferre","target":"Mabeuf","value":2,"type":"A"},{"source":"Prouvaire","target":"Gavroche","value":1,"type":"A"},{"source":"Prouvaire","target":"Enjolras","value":4,"type":"A"},{"source":"Prouvaire","target":"Combeferre","value":2,"type":"A"},{"source":"Feuilly","target":"Gavroche","value":2,"type":"A"},{"source":"Feuilly","target":"Enjolras","value":6,"type":"A"},{"source":"Feuilly","target":"Prouvaire","value":2,"type":"A"},{"source":"Feuilly","target":"Combeferre","value":5,"type":"A"},{"source":"Feuilly","target":"Mabeuf","value":1,"type":"A"},{"source":"Feuilly","target":"Marius","value":1,"type":"A"},{"source":"Courfeyrac","target":"Marius","value":9,"type":"A"},{"source":"Courfeyrac","target":"Enjolras","value":17,"type":"A"},{"source":"Courfeyrac","target":"Combeferre","value":13,"type":"A"},{"source":"Courfeyrac","target":"Gavroche","value":7,"type":"A"},{"source":"Courfeyrac","target":"Mabeuf","value":2,"type":"A"},{"source":"Courfeyrac","target":"Eponine","value":1,"type":"A"},{"source":"Courfeyrac","target":"Feuilly","value":6,"type":"A"},{"source":"Courfeyrac","target":"Prouvaire","value":3,"type":"A"},{"source":"Bahorel","target":"Combeferre","value":5,"type":"A"},{"source":"Bahorel","target":"Gavroche","value":5,"type":"A"},{"source":"Bahorel","target":"Courfeyrac","value":6,"type":"A"},{"source":"Bahorel","target":"Mabeuf","value":2,"type":"A"},{"source":"Bahorel","target":"Enjolras","value":4,"type":"A"},{"source":"Bahorel","target":"Feuilly","value":3,"type":"A"},{"source":"Bahorel","target":"Prouvaire","value":2,"type":"A"},{"source":"Bahorel","target":"Marius","value":1,"type":"A"},{"source":"Bossuet","target":"Marius","value":5,"type":"A"},{"source":"Bossuet","target":"Courfeyrac","value":12,"type":"A"},{"source":"Bossuet","target":"Gavroche","value":5,"type":"A"},{"source":"Bossuet","target":"Bahorel","value":4,"type":"A"},{"source":"Bossuet","target":"Enjolras","value":10,"type":"A"},{"source":"Bossuet","target":"Feuilly","value":6,"type":"A"},{"source":"Bossuet","target":"Prouvaire","value":2,"type":"A"},{"source":"Bossuet","target":"Combeferre","value":9,"type":"A"},{"source":"Bossuet","target":"Mabeuf","value":1,"type":"A"},{"source":"Bossuet","target":"Valjean","value":1,"type":"A"},{"source":"Joly","target":"Bahorel","value":5,"type":"A"},{"source":"Joly","target":"Bossuet","value":7,"type":"A"},{"source":"Joly","target":"Gavroche","value":3,"type":"A"},{"source":"Joly","target":"Courfeyrac","value":5,"type":"A"},{"source":"Joly","target":"Enjolras","value":5,"type":"A"},{"source":"Joly","target":"Feuilly","value":5,"type":"A"},{"source":"Joly","target":"Prouvaire","value":2,"type":"A"},{"source":"Joly","target":"Combeferre","value":5,"type":"A"},{"source":"Joly","target":"Mabeuf","value":1,"type":"A"},{"source":"Joly","target":"Marius","value":2,"type":"A"},{"source":"Grantaire","target":"Bossuet","value":3,"type":"A"},{"source":"Grantaire","target":"Enjolras","value":3,"type":"A"},{"source":"Grantaire","target":"Combeferre","value":1,"type":"A"},{"source":"Grantaire","target":"Courfeyrac","value":2,"type":"A"},{"source":"Grantaire","target":"Joly","value":2,"type":"A"},{"source":"Grantaire","target":"Gavroche","value":1,"type":"A"},{"source":"Grantaire","target":"Bahorel","value":1,"type":"A"},{"source":"Grantaire","target":"Feuilly","value":1,"type":"A"},{"source":"Grantaire","target":"Prouvaire","value":1,"type":"A"},{"source":"MotherPlutarch","target":"Mabeuf","value":3,"type":"A"},{"source":"Gueulemer","target":"Thenardier","value":5,"type":"A"},{"source":"Gueulemer","target":"Valjean","value":1,"type":"A"},{"source":"Gueulemer","target":"Mme.Thenardier","value":1,"type":"A"},{"source":"Gueulemer","target":"Javert","value":1,"type":"A"},{"source":"Gueulemer","target":"Gavroche","value":1,"type":"A"},{"source":"Gueulemer","target":"Eponine","value":1,"type":"A"},{"source":"Babet","target":"Thenardier","value":6,"type":"A"},{"source":"Babet","target":"Gueulemer","value":6,"type":"A"},{"source":"Babet","target":"Valjean","value":1,"type":"A"},{"source":"Babet","target":"Mme.Thenardier","value":1,"type":"A"},{"source":"Babet","target":"Javert","value":2,"type":"A"},{"source":"Babet","target":"Gavroche","value":1,"type":"A"},{"source":"Babet","target":"Eponine","value":1,"type":"A"},{"source":"Claquesous","target":"Thenardier","value":4,"type":"A"},{"source":"Claquesous","target":"Babet","value":4,"type":"A"},{"source":"Claquesous","target":"Gueulemer","value":4,"type":"A"},{"source":"Claquesous","target":"Valjean","value":1,"type":"A"},{"source":"Claquesous","target":"Mme.Thenardier","value":1,"type":"A"},{"source":"Claquesous","target":"Javert","value":1,"type":"A"},{"source":"Claquesous","target":"Eponine","value":1,"type":"A"},{"source":"Claquesous","target":"Enjolras","value":1,"type":"A"},{"source":"Montparnasse","target":"Javert","value":1,"type":"A"},{"source":"Montparnasse","target":"Babet","value":2,"type":"A"},{"source":"Montparnasse","target":"Gueulemer","value":2,"type":"A"},{"source":"Montparnasse","target":"Claquesous","value":2,"type":"A"},{"source":"Montparnasse","target":"Valjean","value":1,"type":"A"},{"source":"Montparnasse","target":"Gavroche","value":1,"type":"A"},{"source":"Montparnasse","target":"Eponine","value":1,"type":"A"},{"source":"Montparnasse","target":"Thenardier","value":1,"type":"A"},{"source":"Toussaint","target":"Cosette","value":2,"type":"A"},{"source":"Toussaint","target":"Javert","value":1,"type":"A"},{"source":"Toussaint","target":"Valjean","value":1,"type":"A"},{"source":"Child1","target":"Gavroche","value":2,"type":"A"},{"source":"Child2","target":"Gavroche","value":2,"type":"A"},{"source":"Child2","target":"Child1","value":3,"type":"A"},{"source":"Brujon","target":"Babet","value":3,"type":"A"},{"source":"Brujon","target":"Gueulemer","value":3,"type":"A"},{"source":"Brujon","target":"Thenardier","value":3,"type":"A"},{"source":"Brujon","target":"Gavroche","value":1,"type":"A"},{"source":"Brujon","target":"Eponine","value":1,"type":"A"},{"source":"Brujon","target":"Claquesous","value":1,"type":"A"},{"source":"Brujon","target":"Montparnasse","value":1,"type":"A"},{"source":"Mme.Hucheloup","target":"Bossuet","value":1,"type":"A"},{"source":"Mme.Hucheloup","target":"Joly","value":1,"type":"A"},{"source":"Mme.Hucheloup","target":"Grantaire","value":1,"type":"A"},{"source":"Mme.Hucheloup","target":"Bahorel","value":1,"type":"A"},{"source":"Mme.Hucheloup","target":"Courfeyrac","value":1,"type":"A"},{"source":"Mme.Hucheloup","target":"Gavroche","value":1,"type":"A"},{"source":"Mme.Hucheloup","target":"Enjolras","value":1,"type":"A"}]};

var svg = d3.select("svg"),
  width = +svg.attr("width"),
  height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var zoom_handler = d3.zoom().on("zoom", zoom_actions);

// zoom_handler(svg);

var simulation = d3.forceSimulation()
  .force("link", d3.forceLink().distance(300).id(function(d) {
    return d.id;
  }))
  .force("charge", d3.forceManyBody().strength(-300))
  .force("center", d3.forceCenter(width / 2, height / 2));

var g = svg.append("g")
  .attr("class", "everything");

svg.call(zoom_handler)
  .call(zoom_handler.transform, d3.zoomIdentity.translate(200, 150).scale(0.2));

var link = g.append("g")
  .attr("class", "links")
  .selectAll("line")
  .data(graph.links)
  .enter().append("line")
  .attr("stroke-width", function(d) {
    return Math.sqrt(d.value);
  });

var node = g.append("g")
  .attr("class", "nodes")
  .selectAll("g")
  .data(graph.nodes)
  .enter().append("g")
  .attr("id", function(d) {
    return d.id
  })


var circles = node.append("circle")
  .attr("r", 8)
  .attr("fill", function(d) {
    return color(d.group);
  })
  .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

var lables = node.append("text") // Labeling for nodes
  .text(function(d) {
    return d.id;
  })
  .attr('x', 6)
  .attr('y', 3);

node.append("title")
  .text(function(d) {
    return d.id;
  });

simulation
  .nodes(graph.nodes)
  .on("tick", ticked);

simulation.force("link")
  .links(graph.links);


function ticked() {
  link
    .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;
    });
  node
    .attr("transform", function(d) {
      return "translate(" + d.x + "," + d.y + ")";
    })

};


d3.select("#Zooming").on("click", Zooming);

function Zooming() {
  var largest = null,
    weight = 0;
  node.each(function(d) {
    var connections = link.filter(function(l) {
      return l.source.index == d.index || l.target.index == d.index
    });
    d.weight = connections.size();
    if (d.weight > weight) {
      largest = {
        node: this,
        links: connections
      };
      weight = d.weight;

    }
  });
  if (largest) {
    d3.select(largest.node).select("circle")
      .style("fill", "red")
      .style("stroke-width", 3);
    largest.links.each(function() {
      d3.select(this).style("stroke", "red").style("stroke-width", 3);;
    })
  }
}

function zoom_actions() {
  g.attr("transform", d3.event.transform)
}

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}
.links line {
  stroke: #999;
  stroke-opacity: 0.6;
}

.nodes circle {
  stroke: #000;
  stroke-width: 1.5px;
}

text {
  font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
  font-size: 10px;
}
<!DOCTYPE html>
<meta charset="utf-8">
<link rel="shortcut icon" href="//#" />

<html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">

<input type="button" value="Zoom" id="Zooming" />

</html>
</div>

<svg width="798" height="400"></svg>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>

Upvotes: 1

Views: 1608

Answers (1)

rioV8
rioV8

Reputation: 28633

To zoom to the position of the node you must update the zoom transform based on the position of the largest node and a chosen zoom factor (I found a factor of 2 a nice zoom factor). The read about the math see Panning and Zooming with D3v4 - Empty Pipes

To prevent the "ghosting" effect while dragging you should organize the nodes and the text differently and also modify the ticked function. The reason is you are changing the transformation of the object you are dragging.

Add the texts as separate tags below the .nodes group and update the text position when you update the node position. The text tags get a unique id based on the id of the node (prefix 't_').

Try to run your HTML through the W3 validator. Almost all tags are positioned incorrect.

var nodeGroup = g.append("g")
  .attr("class", "nodes");

var node = nodeGroup.selectAll("circle")
  .data(graph.nodes)
  .enter().append("circle")
  .attr("r", 8)
  .attr("fill", function(d) { return color(d.group); })
  .attr("id", function(d) { return d.id })
  .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));

function cleanId(id) { return id.replace(/\./g, '_'); }

node.append("title")
  .text(function(d) { return d.id; });

node.each(d => {
  nodeGroup.append("text") // Labelling for nodes
    .attr("id", 't_' + cleanId(d.id) )
    .text(d.id); });

function ticked() {
  link
    .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; });
  node
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .each(d => { d3.select('#t_' + cleanId(d.id)).attr('x', d.x + 10).attr('y', d.y + 3); });
};

simulation
  .nodes(graph.nodes)
  .on("tick", ticked);

simulation.force("link")
  .links(graph.links);

d3.select("#Zooming").on("click", Zooming);

function Zooming() {
  var largest = null,
    weight = 0;
  node.each(function(d) {
    var connections = link.filter(function(l) {
      return l.source.index == d.index || l.target.index == d.index
    });
    d.weight = connections.size();
    if (d.weight > weight) {
      largest = {
        node: this,
        links: connections
      };
      weight = d.weight;
    }
  });
  if (largest) {
    d3.select(largest.node)
      .style("fill", "red")
      .style("stroke-width", 3);
    largest.links.each(function() {
      d3.select(this).style("stroke", "red").style("stroke-width", 3);
    });
    var scaleZoom = 2;
    var nodeDatum = d3.select(largest.node).datum();
    svg.transition()
       .duration(750)
       .call(zoom_handler.transform,
             d3.zoomIdentity
               .translate(width*0.5-scaleZoom*nodeDatum.x,
                          height*0.5-scaleZoom*nodeDatum.y)
               .scale(scaleZoom));
  }
}

Edit

Modified the code a bit to sanitize the ids for the text tags to replace the . with a '_'.

Upvotes: 1

Related Questions