Every imported function comes up as "undefined", though the import is clearly being made

Viewed 1231

I've downloaded d3 from here: https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.js and have included it in my /static folder.

/static/graph.js

import * as d3 from "/static/d3.js"
export function graph() {
    d3.select("#classname");
    console.log("Selected");
}

index.html

<html>
    <body>
        <script type="module">
            import {graph} from "/static/graph.js"
            graph()
        </script>
    </body>
</html>

But, I recieve the error d3.select is not a function.

  • The setup of the /static folder is sound; if I change the string referencing either of the .js files, I'm (appropriately) met with a TypeError: Error resolving module specifier: ... That is, with the strings as they are, we can see and serve the files appropriately (further: the response from the network is 304, indicating the cached files match what the server is serving).
  • D3 is being loaded in some way. If I place a debugger; inside of the graph() function, go to the dev. console and type d3, I am able to scroll through an autocomplete list of all functions defined in D3 (including select). But, something like d3.select, returns Undefined. So D3 is being loaded enough to autocomplete, but everything is coming up as undefined. The variable itself is of type Symbol(Symbol.toStringTag): "Module" which is documented here (though I am not sure what to make of said documentation)
  • On the other hand, placing a debugger; statement in index.html shows that d3 seems to work as intended, even though this is not where it's being imported.

Why is d3.select not a function?

1 Answers

I believe the issue here is, you are attempting to load a module, but there are ES6 Modules, and there are UMD Modules. D3.js is a UMD Module.

UMD modules, are "bootstrapped" to hook into the "window" or "global" object. UMD modules, evolved during a time, where exactly how "Modules" would work was in flux, anyways it's a very fascinating topic and I would recommend reading up on it sometime...

So instead of

import * as d3 from "/static/d3.js"

you should do

import "/static/d3.js"

Now you will be able to use D3, as it has been attached to the "window" object.

So with that being said your setup would look like:

// index.html
<html>
    <body>
        <script type="module">
            import {graph} from "/static/graph.js"
            graph()
        </script>
    </body>
</html>

And

// static/graph.js
import "/static/d3.js" // Now loaded as UMD module
export function graph() {
    d3.select("#classname");
    console.log("Selected");
}

That would work.

As a side-note, this is the part of the D3 Module, which does the bootstrapping and attaches D3 to the "window" object.

...
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
        (global = global || self, factory(global.d3 = global.d3 || {})); // <-- the "magic" part.
}(this, function (exports) { 
     /* 
          THE REST OF THE MODULE SOURCE GOES HERE 
          all 18,561 lines of it...
     */ 
}));
Related