Dan Dinh
Dan Dinh

Reputation: 8609

D3: Why does a 'get' action spoil the value

This is a strange case when the datum() function of D3 without param, which is a get function, spoils the value bound to element.

JS Bin link:
https://jsbin.com/jokarapovi/edit?html,js,console

HTML:

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
</head>
<body>
  <svg id="s">
    <g id="g"></g>
  </svg>
</body>
</html>

JavaScript:

var log = console.log;

d3.select("#s").datum(1);
d3.select("#g").datum(2);

var s = d3.select("#s");
var g = d3.select("#g");
log("svg datum:",s.datum());
log("g datum:",g.datum());

// This line is a 'get' but it spoils the value
var temp = s.select("#g").datum();

// Wrong value:
log("g datum:",d3.select("#g").datum());

And this is the spoilt output:

"svg datum:" 1
"g datum:" 2
"g datum:" 1

Why should a get function (D3 datum() without param) spoil the value bound to element?

Upvotes: 1

Views: 36

Answers (1)

Andrew Reid
Andrew Reid

Reputation: 38201

The behavior you see is an outcome of selection.select(), we can remove .datum() from the get line you have and the outcome is the same:

var log = console.log;

d3.select("#s").datum(1);
d3.select("#g").datum(2);

var s = d3.select("#s");
var g = d3.select("#g");
log("svg datum:",s.datum());
log("g datum:",g.datum());

// This get is a 'get' but it spoils the value
var temp = s.select("#g")//.datum();

// Wrong value:
log("g datum:",d3.select("#g").datum());
<!DOCTYPE html>
<html>
<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
</head>
<body>
  <svg id="s">
    <g id="g"></g>
  </svg>
</body>
</html>

This is because selection.select binds data bound to selection to the selected child element (overwriting the previous datum): "If the current element has associated data, this data is propagated to the corresponding selected element." (docs)

This is generally ideal in D3, a one to one parent child relationship generally has the same datum for both parent and child.

If you don't want to pass the parent's datum to the child, the simplest method would be to store a reference to the child or use an identifier that allows it to be selected directly, avoiding the issue:

var log = console.log;

d3.select("#s").datum(1);
d3.select("#g").datum(2);

var s = d3.select("#s");
var g = d3.select("#g");
log("svg datum:",s.datum());
log("g datum:",g.datum());

// This get is a 'get' but it spoils the value
var temp = d3.select("#g").datum();

// Wrong value:
log("g datum:",d3.select("#g").datum());
<!DOCTYPE html>
<html>
<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
</head>
<body>
  <svg id="s">
    <g id="g"></g>
  </svg>
</body>
</html>

Upvotes: 1

Related Questions