Porting Mike's Bar Chart Race from Observable to HTML/JS

As we know, there are a number of differences between Observable and Javascript. I’ve been wrestling with errors porting this working fork of @mbostock 's Bar Chart race to static HTML.

I finally got it so the ported HTML/JS doesn’t produce any errors (link to codepen here… code is toward bottom).

Sadly, it also doesn’t produce the visualization :frowning:

I believe I’m missing a way of attaching the actual visualization to the DOM but I can’t quite figure out how to do that (I thought the async generator that produces the chart would do this but it doesn’t seem to execute for some reason). Any guidance appreciated :slight_smile:

1 Like

I am not the best at this, and I am very unfamiliar with CodePen… but at the end you commented out some lines that assign the SVG to a div element. Hopefully I am not way off base here (and hopefully someone more knowledgeable steps in to help), but each time I try to transcribe something to HTML I tend to get caught on this. In Observable, you can just “return()” an element (like I think you’re doing with chart() ) and that will render, but in HTML you have have to attach it to a DOM element.

Not sure this helps (or that it’s even correct). If I get more time I’ll see if I can solve it.

1 Like

Hey Aaron, thank you so much for your comment! You are correct, I do have that line which is commented out. That appends an arbitrary blank svg element. It’s commented because I’m not sure how to append the chart itself in an SVG element.

My intention with chart() is to do exactly as you say - to return the chart element… but my tests show that chart() isn’t actually executing. I think because of something about it being an async generator. It could very well be that this chart() piece shouldn’t be in an async generator but that was the only combination of JS code I tried that didn’t produce an error due to the yield statement and later await statement that Mike had written.

I will experiment more

1 Like

Seems like you’re on the right track! Sorry that I haven’t managed a solution yet. Not much time this evening.

This is probably too much of an oversimplification for your purposes, but I wanted to share this notebook, which I found helpful / inspirational in my learning process this evening for very simply showing basic DOM manipulations:

I really expect that all you need to solve is the last bit of rendering your SVG into something…

In the linked notebook, the different compared to your codes is that this one shows D3.js selecting a given <div> element and appending to it a newly-created HTML element (in this case p) with some text.

In your code, I am seeing first all these functions that define what to do with an SVG. I then see the SVG get ‘created’ later down, and from there your pass it through the functions you define above. All of these looks like it should be yielded out at the bottom, e.g. via chart(), but as you indicate the final ‘glue’ isn’t there yet. I’ll keep trying…

1 Like

Just in case you’re looking for the shortest path to victory :wink:

-also-

-also-

In these various examples, the SVG looks to be defined early on as a selection of some DOM element, e.g. (const svg = d3.select("#chart")). I am not sure the extent to which this matters in terms of your code organization. I’ll have another crack at adjusting your code if I get a chance this weekend (though I am pretty swamped now and also really clumsy at all this coding stuff, but it’s fun to learn and good practice). You are likely to solve this before me.

… On a bit of an aside, I wonder if in CodePen you can separate your data from your visualization code? It’s a lot to scroll through (though I suppose you’re trying how to get this all working in one HTML file, which I totally appreciate).

Hope this helps!

1 Like

Hi. I just had a quick look at your codepen, and I think one of the problems is that you should use ‘const’ in defining the variables when you copy them over to javascript. In observable, they are cell names, but in JavaScript, you need to make them constants.

And then also, add this to your html (right at the end, after the JavaScript block:
<div id="chart"></div>
and then in your chart() function, do something like:
const svg = d3.select("#chart").attr("viewBox", [-150, 0, width + currency_x_shift + 150, height]);
as Aaron suggested also.

2 Likes

Thank you so much for those pointers @aaronkyle and @Cobus ! :smile:

I implemented those recommendations but still wasn’t seeing chart() execute. I remember this was because it is a generator… which means it is run by calling its .next() method.

I added the following lines:

chart()
const generatorObject = chart();
generatorObject.next()

This finally got chart() to execute. However, the canvas is still blank :frowning: Given how generators work - by suspending execution at any yield statement - the bottom half of chart() doesn’t execute.

image

A hackish solution to this is to call generatorObject.next() twice in succession… doing this definitely runs the second half of chart() though still resulting in a blank canvas.

So the real take away here is that Mike is a magician :mage: :slight_smile:

1 Like

Thank you for this amazing message and great humor! I should admit: this is very far over my head. I just try to encourage and read things conceptually…

1 Like

Haha, thank you Aaron! Your posts have been very helpful! I think this is still a touch over my head as well. Will just keep chugging along on this until I find a way. Thanks again :slight_smile:

1 Like

I thought I would try a different approach and instead of porting @mbostock 's Bar Chart race to HTML, I thought I’d try achieving the same result by embedding the notebook cell using the Runtime with JavaScript option. As mentioned in the documentation, this method allows me to redefine the chart’s data:

I’ve successfully used that technique to override the data from Observable’s simple bar chart example in the below CodePen

Using this technique on the Bar Chart Race unexpectedly fails.

Above I try to override the data variable as I successfully did in the bar chart example, but in this case it fails to produce the bar chart race. Specifically, if I comment out the line main.redefine("data", newData); then the bar chart race plays as expected. Otherwise, nothing appears.

Any ideas why that may be?

1 Like

Hi @zeluspudding , I guess the issue is your newData array is not coming with the correct Date type. On the original notebook data is a parsed csv data and it’s using d3.autoType.

One fix for your redefine is simple, just force the date do be a new Date`

main.redefine("data", newData.map(d=> Object.assign(d, {date: new Date(d.date)})));
3 Likes

Yaaaaay! It works now! Strange because I had compared those dates and they looked the same to me :thinking: Thank you sooo much!!

1 Like

@zeluspudding cool! Maybe what you’ve compared was the string formatted output date.toISOString()? Which is the same as the string values you have on the newData array. However, the ISO string can be easily converted to a javascript Date object, as shown above.

1 Like