46

I've been trying to invoke D3 within Node.js. I tried firstly to import d3.v2.js from D3's website with the script tag, but then read this thread:

I want to run d3 from a Cakefile

Where D3's author advises one should 'npm install d3'...I did this, and I can successfully invoke it in node console:

dpc@ananda:$ node
> var d3 = require("d3");
undefined
> d3.version;
'2.8.1' 

However, when trying to invoke it from app.js with 'node app.js', I get:

node.js:201
    throw e; // process.nextTick error, or 'error' event on first tick
          ^
TypeError: Cannot read property 'BSON' of undefined
at     /Users/dpc/Dropbox/sync/Business/MindfulSound/Development/nad.am/nadam/node_modules/mongoose/node_modules/mongodb/lib/mongodb/index.js:45:44

I realise that elsewhere, D3's author has clearly specified that one should require the canvas:

https://github.com/mbostock/d3/blob/master/examples/node-canvas/us-counties.js

as:

var Canvas = require("canvas");

but even then, (and even if specifically requiring index.js instead of d3.v2.js in a require statement in app.js), I can't get the below to work within a Jade template:

- script('/javascripts/d3.v2.js')
h1 Dashboard
  section.css-table
    section.two-column
      section.cell
        hr.grey
        h2 Statistics
        #mainGraph
            script(type="text/javascript") 
              var Canvas = require("canvas");
              var w = 400,
                  h = 400,
                  r = Math.min(w, h) / 2,
                  data = d3.range(10).map(Math.random).sort(d3.descending),
                  color = d3.scale.category20(),
                  arc = d3.svg.arc().outerRadius(r),
                  donut = d3.layout.pie();
              var vis = d3.select("body").append("svg")
                  .data([data])
                  .attr("width", w)
                  .attr("height", h);
              var arcs = vis.selectAll("g.arc")
                  .data(donut)
                  .enter().append("g")
                  .attr("class", "arc")
                  .attr("transform", "translate(" + r + "," + r + ")");
              var paths = arcs.append("path")
                  .attr("fill", function(d, i) { return color(i); });
              paths.transition()
                  .ease("bounce")
                  .duration(2000)
                  .attrTween("d", tweenPie);
              paths.transition()
                  .ease("elastic")
                  .delay(function(d, i) { return 2000 + i * 50; })
                  .duration(750)
                  .attrTween("d", tweenDonut);

              function tweenPie(b) {
                b.innerRadius = 0;
                var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
                return function(t) {
                  return arc(i(t));
                };
              }

              function tweenDonut(b) {
                b.innerRadius = r * .6;
                var i = d3.interpolate({innerRadius: 0}, b);
                return function(t) {
                  return arc(i(t));
                };

      section.cell
        hr.grey
        h2 Achievements
ZachB
  • 13,051
  • 4
  • 61
  • 89
pland
  • 858
  • 1
  • 9
  • 17

3 Answers3

78

The correct way to use D3 within Node is to use NPM to install d3 and then to require it. You can either npm install d3 or use a package.json file, followed by npm install:

{
  "name": "my-awesome-package",
  "version": "0.0.1",
  "dependencies": {
    "d3": "3"
  }
}

Once you have d3 in your node_modules directory, load it via require:

var d3 = require("d3");

And that's it.

Regarding your other issues: Canvas is not required to use D3. The node-canvas example you linked requires canvas because it renders to a canvas. The TypeError (Cannot read property 'BSON' of undefined) appears to be related to your use of mongoose / monogdb, not D3.

mbostock
  • 51,423
  • 13
  • 175
  • 129
  • Thanks for your reply! It's rather strange: d3 is indeed in node_modules as I installed it with 'npm install d3'...but a 'var d3 = require("d3");' in app.js gives me that BSON error...remove that line, BSON error gone. And this is with a simpler example of d3 use, from the SVG circle drawing here: http://christopheviau.com/d3_tutorial/ – pland Mar 30 '12 at 22:44
  • 1
    For the record, figured out that there were issues in the use of globals between mongoose and d3, so that calling d3 first with var d3 = require("d3"); before requiring mongoose solved the BSON error problem. – pland Apr 02 '12 at 09:08
  • This has been fixed in BSON v0.1.5 (and mongodb v2.0.0, which depends on the correct BSON version). – Jason Davies Nov 13 '12 at 21:00
  • 2
    Now that i have d3 in node.js as a module, how do I select element with it? I want to create a svg on server and return it to the client. can d3.select() take a html snippet like '
    ' ?
    – arunkjn Sep 19 '13 at 13:51
  • 3
    This is an easily confused topic so let me try to help clarify - `npm install d3` loads the d3 node module, which allows you to build data driven documents within memory on the server so they can be passed to the browser as pre-built(rendered) html/svg/etc. Compare this to traditional d3 usage, which is to include the library in the html and render the elements in the dom within the browser. There's a subtle but significant difference in rendering on client-side versus server-side. Also see: http://mango-is.com/blog/engineering/pre-render-d3-js-charts-at-server-side.html – Brad Hein Feb 14 '16 at 14:47
  • 1
    My understanding is that server-side d3 will require a pseudo-dom to select(). Take a look at JSDOM. I found this post very enlightening http://mango-is.com/blog/engineering/pre-render-d3-js-charts-at-server-side.html – Colin Mar 18 '16 at 17:48
16

To use with ES6's import instead of require:

import * as d3 from 'd3';

This is perhaps obvious to any experienced babel/ES6-user, and I know this is an old question, but I came here in an attempt to figure this out. Hope this is helpful for someone.

More on import vs. require is found here.

Community
  • 1
  • 1
Thomas Fauskanger
  • 2,536
  • 1
  • 27
  • 42
4

You need to install jsdom using yarn or npm. Node.js does not support dom by default, thus the need to use jsdom for creating dom elements. Use d3 for all other manipulations except fetch operations. i.e d3.xml,d3.tsv

import jsdom from 'jsdom';
import * as d3 from 'd3';
const { JSDOM } = jsdom;
JSDOM.fromURL(
    'your resource url',
  ).then((dom) => {
    const doc = dom.window.document;
    const states = d3
      .select(doc)
      .select('path')
      .attr(':fme:ID');
    console.log(states);
  });

creating dom from file

JSDOM.fromURL(
    'your resource url',
  ).then((dom) => {
    const doc = dom.window.document;
}

dom from html string

const dom = new JSDOM(
  `<p>Hello
    <img src="foo.jpg">
  </p>`,
  { includeNodeLocations: true }
);

Joma sim
  • 173
  • 1
  • 5