Why do I get JavaScript undefined property in Observable but not in HTML?

Hi Community!

I am new to coding / JavaScript / data visualization, and I am very thankful to the Observable team for making this resource (and making it OpenSource)!

I am trying to understand how code sections need to be separated in order to work in observable. Toward this aim, I tried to covert a visualization from standard HTML so that it would render in Observable. I can’t get it to work, however – getting TypeError: Cannot read property 'timeFormat' of undefined in reference to the line of code that draws the chart, namely: d3.select('#events').data([repositoriesData]).call(chart);

My progress is visible at:

Is there anyone who can help me understand what I am doing wrong (and how to fix it)?

Thanks for your time!

Hi Aaron,

Sure! Here’s a fork of that notebook that works: https://beta.observablehq.com/@tmcw/learning-javascript-and-observable-by-converting-a-fiddle

The changes are:

  • The event-drops module expects d3 to be just ‘hanging around’ on the window object. This isn’t ideal: modules should really declare their dependencies and load them with AMD, but anyway - it’s not a dealbreaker. I added a cell that sets window.d3 = d3 and that makes event-drops happy. This was the issue that complained about timeFormat - it expected d3.timeFormat to just be there.
  • I created a cell for the output, and now reference that in d3.select(events) instead of d3.select('#events'). See the little observer for one explanation of why: cells run the order that they need to depending on each other, so it’s best to connect things like d3.select to elements on the page based on referencing variables, rather than using strings like ‘#events’ to select elements on the page.
  • The stylesheet can just be added with the html template literal - no need to append it to the <head> of the document manually.
2 Likes

Here’s my take:

The main change I would make is to combine the cell that creates the DIV element with the cell that renders the chart. In general, you want to avoid side-effects in Observable, like reaching into a DOM element created in another cell and modifying its contents; instead, try to combine the definition into a single cell.

The slightly tricky thing here is that EventDrops requires that the DIV into which you want to render the chart be already in the DOM, whereas cells in Observable are designed to create detached DOM elements and then return then. But you can solve this readily by first yielding the DIV element (so that Observable adds it to the DOM) and then rendering the chart with EventDrops. Tom used a similar technique here for maps:

2 Likes

Wow! Thanks to you both! I appreciate not only that you made the chart work, but also that your explanations are clear and informative. I also feel honored that two of Observable’s founders and developers replied to my newbie question – thanks again!

1 Like