Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3 drag error 'Cannot read property x of undefined'

Tags:

d3.js

I'm trying to get drag functionality to work on D3, and have copied the code directly from the developer's example.

However it seems the origin (what is being clicked) is not being passed correctly into the variable d, which leads to the error: 'Cannot read property 'x' of undefined'

The relevant code:

var drag = d3.behavior.drag()
        .on("drag", function(d,i) {
            d.x += d3.event.dx
            d.y += d3.event.dy
            d3.select(this).attr("transform", function(d,i){
                return "translate(" + [ d.x,d.y ] + ")"
            })
        });

var svg = d3.select("body").append("svg")
      .attr("width", 1000)
      .attr("height", 300);

var group = svg.append("svg:g")
    .attr("transform", "translate(10, 10)")
    .attr("id", "group");

var rect1 = group.append("svg:rect")
    .attr("rx", 6)
    .attr("ry", 6)
    .attr("x", 5/2)
    .attr("y", 5/2)
    .attr("id", "rect")
    .attr("width", 250)
    .attr("height", 125)
    .style("fill", 'white')
    .style("stroke", d3.scale.category20c())
    .style('stroke-width', 5)
    .call(drag);
like image 854
jetlej Avatar asked Jul 14 '14 20:07

jetlej


2 Answers

Usually, in D3 you create elements out of some sort of datasets. In your case you have just one (perhaps, one day you'll want more than that). Here's how you can do it:

var data = [{x: 2.5, y: 2.5}], // here's a dataset that has one item in it
    rects = group.selectAll('rect').data(data) // do a data join on 'rect' nodes
        .enter().append('rect') // for all new items append new nodes with the following attributes:
            .attr('x', function (d) { return d.x; })
            .attr('y', function (d) { return d.y; })
            ... // other attributes here to modify 
            .call(drag);

As for the 'drag' event handler:

var drag = d3.behavior.drag()
        .on('drag', function (d) {

            d.x += d3.event.dx;
            d.y += d3.event.dy;

            d3.select(this)
                .attr('transform', 'translate(' + d.x + ',' + d.y + ')');
        });
like image 101
Oleg Avatar answered Oct 30 '22 07:10

Oleg


Oleg's got it, I just wanted to mention one other thing you might do in your case.

Since you only have a single rect, you can bind data directly to it with .datum() and not bother with computing a join or having an enter selection:

var rect1 = svg.append('rect')
  .datum([{x: 2.5, y: 2.5}])
  .attr('x', function (d) { return d.x; })
  .attr('y', function (d) { return d.y; })
  //... other attributes here
  .call(drag);
like image 27
jshanley Avatar answered Oct 30 '22 06:10

jshanley