title: D3 force layout zoom and pan: Clicking node also pans
body: <p>I have the following d3.js force layout code below which mostly works. It uses anchor labels so that labels will be more visible in a crowded graph. I also found a pan and zoom example and implemented it. Panning and zooming mostly works, however when clicking on a node it pans as well as moves the node, which means it doesn't really move the node. I just want it to move the node and not pan when clicking on the node. I created a jsfiddle available <a href="http://jsfiddle.net/Hs5NC/" rel="nofollow">here</a>.</p>
  
  <p>Code:</p>
  
  <pre><code>var links = [ 
      { source: "a", target: "b", indicator_type: "x"},
      { source: "a", target: "c", indicator_type: "x"},
      { source: "e", target: "d", indicator_type: "x"},
      { source: "e", target: "d", indicator_type: "x"},
      { source: "e", target: "b", indicator_type: "y"},
      { source: "e", target: "c", indicator_type: "y"},
  ];
  
  var w = 850,
      h = 500;
  
  var fill = d3.scale.category20();
  
  var labelDistance = 0;
  
  var vis = d3.select("#relationship_graph")
      .append("svg:svg")
      .attr("width", w)
      .attr("height", h)
      .attr("pointer-events", "all")
      .append('svg:g')
      .call(d3.behavior.zoom().on("zoom", redraw))
      .append('svg:g');
  
  vis.append("rect")
      .attr("width", w)
      .attr("height", h)
      .attr("opacity", 0);
  
  function redraw() {
      vis.attr("transform",
          "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
  }
  
  var nodes = {};
  var labelAnchors = [];
  var labelAnchorLinks = [];
  
  // Compute the distinct nodes from the links.
  links.forEach(function (link) {
      link.source = nodes[link.source] || (nodes[link.source] = {
          label: link.source,
          type: "notice",
          indicator_type: "notice"
      });
      link.target = nodes[link.target] || (nodes[link.target] = {
          label: link.target,
          type: "indicator",
          indicator_type: link.indicator_type
      });
      link.weight = 1;
  });
  
  for (var node in nodes) {
  
      var a = labelAnchors.push({
          node: nodes[node]
      });
      var b = labelAnchors.push({
          node: nodes[node]
      });
      labelAnchorLinks.push({
          source: (a - 1),
          target: (b - 1),
          weight: 1
      });
  };
  
  var force = d3.layout.force()
      .size([w,h])
      .nodes(d3.values(nodes))
      .links(links)
      .gravity(1)
      .linkDistance(60)
      .charge(-3000)
      .linkStrength(function (x) {
              return x.weight * 5
      });
  
  force.start();
  
  var force2 = d3.layout.force()
      .nodes(labelAnchors)
      .links(labelAnchorLinks)
      .gravity(0)
      .linkDistance(0)
      .linkStrength(8)
      .charge(-300)
      .size([w, h]);
  
  force2.start();
  
  var link = vis.selectAll("line.link")
      .data(links)
      .enter()
      .append("svg:line")
      .attr("class", "link")
      .style("stroke", "#CCC");
  
  var node = vis.selectAll("g.node")
      .data(force.nodes())
      .enter()
      .append("svg:g")
      .attr("class", "node");
  
  node.append("svg:circle")
      .attr("r", function (d) {
          return d.type == "notice" ? 10 : 7
      })
      .style({
          fill: function (d) {
              return fill(d.indicator_type)
          },
          stroke: "#CCC",
      });
  
  node.call(force.drag);
  
  var anchorLink = vis.selectAll("line.anchorLink")
      .data(labelAnchorLinks)
      //.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#999");
  
  var anchorNode = vis.selectAll("g.anchorNode")
      .data(force2.nodes())
      .enter()
      .append("svg:g")
      .attr("class", "anchorNode");
  
  anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF");
  anchorNode.append("svg:text").text(function (d, i) {
      return i % 2 == 0 ? "" : d.node.label;
  })
  .style("fill", "#555")
  .style("font-family", "Arial")
  .style("font-size", 10);
  
  var updateLink = function () {
      this.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;
      });
  }
  
  var updateNode = function () {
      this.attr({
          "transform": function (d) {
              return "translate(" + d.x + "," + d.y + ")";
          }
      });
  }
  
  force.on("tick", function () {
  
      force2.start();
  
      node.call(updateNode);
  
      anchorNode.each(function (d, i) {
          if (i % 2 == 0) {
              d.x = d.node.x;
              d.y = d.node.y;
          } else {
              var b = this.childNodes[1].getBBox();
  
              var diffX = d.x - d.node.x;
              var diffY = d.y - d.node.y;
  
              var dist = Math.sqrt(diffX * diffX + diffY * diffY);
  
              var shiftX = b.width * (diffX - dist) / (dist * 2);
              shiftX = Math.max(-b.width, Math.min(0, shiftX));
              var shiftY = 5;
              this.childNodes[1]
                  .setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
          }
      });
  
      anchorNode.call(updateNode);
  
      link.call(updateLink);
      anchorLink.call(updateLink);
  
  });
  </code></pre>