title: Drag behavior for nested g elements
body: <p>This question is closely related to the following question:</p>
  
  <ul>
  <li><a href="https://stackoverflow.com/questions/10095979/d3-click-and-drag-event-nesting">d3 click and drag event nesting</a></li>
  </ul>
  
  <p>In that question, there was a single g element with a bunch of svg elements in the group. The solution was to use d3.event.sourceEvent.stopPropagation on dragstart.</p>
  
  <p>In my case, I have a single g element with a bunch of svg elements including other g elements. Here is a simple example I created that illustrates my problem: <a href="http://jsfiddle.net/eforgy/x1cwur41/" rel="nofollow noreferrer">http://jsfiddle.net/eforgy/x1cwur41/</a> (code copied below). </p>
  
  <p>The fiddle creates three nested g elements and each g element contains just a single rect. If you open up a console and click the innermost red rect, despite having a stopPropagation on dragstart, you can see it appears to be propagating from the top red rect to the bottom blue rect and when you start dragging, it is using the drag from the blue rect, i.e. all three rects move. The behavior I am trying to produce is the following:</p>
  
  <ul>
  <li>If you drag on the blue rect, all three rects move together</li>
  <li>If you drag on the green rect, the blue rect does not move, but the green and red rects move together</li>
  <li>If you drag on the red rect, only the red rect moves.</li>
  </ul>
  
  <p>Any help would be appreciated.</p>
  
  <p>PS: Here is the code:</p>
  
  <pre><code>var child = {index: 0};
  var w = 800,
      h = 600;
  var svg = d3.select("body").append("svg")
              .attr("width", w)
              .attr("height", h);
  
  var cinit = function() {
      return svg;
  };
  cinit.x = 0;
  cinit.y = 0;
  cinit.width = w;
  cinit.height = h;
  
  var c0 = new Component(cinit); c0.fill = "blue";
  var c1 = new Component(c0); c1.fill = "green";
  var c2 = new Component(c1); c2.fill = "red";
  
  function Component(parent) {
      var id = child.index;
      child.index += 1;
      console.log("Created component "+id);
      var x = 1.1*parent.x;
      var y = 1.1*parent.y;
      var width = .5*parent.width;
      var height = .5*parent.height;
      var fill = "blue";
      var stroke = "black";
  
      var drag = d3.behavior.drag()
      .origin(function() {
          var t = d3.transform(group.attr("transform")).translate;
          return {
              x: t[0],
              y: t[1]
          };
      })
     .on("drag", function() {
          console.log("drag: "+id);
          var p = component.position;
          p[0] = d3.event.x;
          p[1] = d3.event.y;
          component.position = p;
      })
      .on("dragstart", function() {
          console.log("dragstart: "+id);
          d3.event.sourceEvent.stopPropagation;
      });
  
      var group = parent().append("g")
          .attr("transform", "translate("+x+","+y+")")
          .call(drag);
  
      var rect = group.append("rect")
          .attr("width", width)
          .attr("height", height)
          .attr("fill", fill)
          .attr("stroke", stroke)
          .on("click", function() {console.log("Clicked "+id);});
  
      function component() {
          return group;
      };
  
      Object.defineProperty(component,"position",{
          get: function() {return [x, y, width, height];},
          set: function(_) {
              x = _[0];
              y = _[1];
              width = _[2];
              height = _[3];
              rect.attr("width", width).attr("height", height);
              group.attr("transform", "translate("+x+","+y+")");
              return component;
          }
      });
  
      Object.defineProperty(component,"x",{
          get: function() {return x;},
          set: function(_) {
              x = _;
              group.attr("transform", "translate("+x+","+y+")");
              return component;
          }
      });
  
      Object.defineProperty(component,"y",{
          get: function() {return y;},
          set: function(_) {
              y = _;
              group.attr("transform", "translate("+x+","+y+")");
              return component;
          }
      });
  
      Object.defineProperty(component,"width",{
          get: function() {return width;},
          set: function(_) {
              width = _;
              rect.attr("width", width).attr("height", height);
              return component;
          }
      });
  
      Object.defineProperty(component,"height",{
          get: function() {return height;},
          set: function(_) {
              height = _;
              rect.attr("width", width).attr("height", height);
              return component;
          }
      });
  
      Object.defineProperty(component,"fill",{
          get: function() {return fill;},
          set: function(_) {
              fill = _;
              rect.attr("fill", fill);
              return component;
          }
      });
  
      return component;    
  }
  </code></pre>