0

I am trying to learn D3 with this video, but I got stuck when I try to load an external file. The codes are as below:

index.html

<html>
  <head>
  <meta charset="utf-8">
    <title>D3 Example</title>
  <script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
  <body>

    <script>
    d3.json("mydata.json", function(data){

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

      canvas.selectAll("rect")
        .data(data)
        .enter()
        .append("rect")
          .attr("width", function(d){return d.age*10;})
          .attr("height", 50)
          .attr("fill", "blue")
          .attr("y", function(d,i){return i * 60});

      canvas.select("text")
        .data(data)
        .enter()
        .append("text")
          .attr("fill", "white")
          .attr("y", function(d, i){return i * 60})
          .text(function(d) {return d.name;});
        });
</script>
</body>
</html>

mydata.json

[
  {"name": "Maria", "age": 30},
  {"name": "Fred", "age": 50},
  {"name": "Francis", "age": 12}
]

And I try to run it in a server:

app.js

const http=require('http');
const fs=require('fs');

const hostname='127.0.0.1';
const port=3000;

fs.readFile('index.html', (err, html) => {
  if (err) {
    throw err;
  }

  const server =http.createServer((req, res) => {
    res.statusCode =200;
    res.setHeader('Content-type', 'text/html');
    res.write(html);
    res.end();
  });

  server.listen(port, hostname, () => {
    console.log('Server started on port '+port);
  });
});

I keep getting the error:

Uncaught TypeError: canvas.selectAll(...).data(...).enter is not a function

When I try to use version 3 of D3, I get the following error:

Uncaught TypeError: Cannot read property 'length' of null

When I try to load a .csv file instead, I get the following error:

Error: attribute width: Expected length, "NaN".

altocumulus
  • 21,179
  • 13
  • 61
  • 84
nganlong
  • 61
  • 7

1 Answers1

1

The error is caused by the way you set up your server. No matter what you requested from it, it will always respond by sending the contents of index.html. This equally holds true if you request mydata.json which will also yield index.html's contents.

Because that file does not contain valid JSON, it cannot be parsed by d3.json() causing the callback's data parameter to be null. When passing null to .enter() it will act as a getter, which—at least in your case—yields an empty array:

If data is not specified, this method returns the array of data for the selected elements.

Obviously, this array does not feature a .enter() method, hence the error.

var data = d3.selectAll("div")
  .data(null);

console.log(data);          // []: Empty array
console.log(data.enter());  // "Uncaught TypeError: data.enter is not a function"
<script src="https://d3js.org/d3.v4.js"></script>

The solution to this is to correctly set up your server to return the contents your are after.


Furthermore, to harden your code, you might want to check for errors occurring during loading or parsing, which will be handed over to the callback passed to d3.json():

The callback is invoked with two arguments: the error, if any, and the response value. The response value is undefined if an error occurs.

Although not mandatory, this will make debugging much easier.

d3.json("mydata.json",function(error, data) {
  // Handle error (logging, throwing, whatever)
  if (error) throw error;
}
altocumulus
  • 21,179
  • 13
  • 61
  • 84
  • Thanks for the response, May I ask if there is any way for a quick fix of the server? – nganlong Jan 09 '18 at 12:37
  • @nganlong There are numerous options to address this issue. If you want to stick to your programmatic approach you might want to have a look at this [answer](https://stackoverflow.com/a/29046869/4235784) to [*“ Node.js quick file server (static files over HTTP“*](https://stackoverflow.com/questions/16333790/node-js-quick-file-server-static-files-over-http/16350826). Otherwise, you could just use a simple out-of-the-box HTTP server as proposed by the accepted answer to that question. – altocumulus Jan 09 '18 at 13:57