Help loading stubborn Highchart scripts and module

Hello,

I am trying to load a Highcharts module (called “dumbbell”) in all the possible ways I can think of, and although I was able to get it to work, it only worked temporarily (strangely enough). The moment I clicked “Enable link sharing”, it broke and I couldn’t get it to work again.

The following scripts need to be loaded:

<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/highcharts-more.js"></script>
<script src="https://code.highcharts.com/modules/dumbbell.js"></script>

I tried to require each, and although the first two “work” (i.e. load without error), the last one complains of a missing k.arearange. Arearange is something added to Highcharts by highcharts-more.js.

The problem is that these scripts really want to be loaded together. So the only way I could think of doing that was:

Highcharts = {
  const HC = await (await fetch('https://code.highcharts.com/highcharts.js')).text();
  const more = await (await fetch('https://code.highcharts.com/highcharts-more.js')).text();
  const dumbbell = await (await fetch('https://code.highcharts.com/modules/dumbbell.js')).text();
  const all = HC + '\n' + more + '\n' + dumbbell;
  const blob = new Blob([all], {type: 'application/javascript'});
  const obj = await require(URL.createObjectURL(blob)).catch(() => window.Highcharts);
  return obj.Highcharts;
}

Believe it or not, this actually worked… for a little while… but then it stopped working. It started to complain that TypeError: obj is undefined. The Javascript console mentions errors like:

  • Firefox: Content Security Policy: The page’s settings blocked the loading of a resource at inline (“script-src”).
  • Safari: Refused to execute a script because its hash, its nonce, or 'unsafe-inline' does not appear in the script-src directive of the Content Security Policy.

… as well as mentions about Unrecognized Content-Security-Policy directive 'prefetch-src' and Unrecognized Content-Security-Policy directive 'worker-src'.

Can anyone please recommend a more reliable way of getting these scripts loaded?

PS: Here’s a sample JSFiddle for what I’m trying to get working in Observable.

Thank you,
Boris

I got it working again. The winning formula (which so far seems to actually work in all circumstances) is:

Highcharts = {
  const HC = await require('https://code.highcharts.com/highcharts.js');
  const Hmore = await require('https://code.highcharts.com/highcharts-more.js');
  Hmore(HC);
  const dumbbell = await require('https://code.highcharts.com/modules/dumbbell.js');
  return dumbbell.Highcharts;
}

I don’t know if this is simplest (best?) way to do this, but seems to work.
The Hmore(HC); line is what extends the Highcharts object with the functionality in highcharts-more.js which is required for the dumbbell module to work.

I’ll now try to re-implement this functionality in D3 :slight_smile:

-Boris

1 Like

Yep, that’s roughly what I was going to suggest. Though I would recommend loading it from npm rather than Highcharts’ servers because on npm it will be versioned, which protects your notebook from breaking if Highcharts ever changes its API.

Here’s a demo:

And the relevant code to load Highcharts:

Highcharts = {
  const Highcharts = await require("highcharts@8");
  await require("highcharts@8/highcharts-more.js");
  await require("highcharts@8/modules/dumbbell.js");
  return Highcharts;
}

I also used a FileAttachment and D3 to parse the CSV file (because that feels better than inlining the data in code).

If you’re looking for a similar D3 example, check out the dot plot:

2 Likes

I love you :heart_eyes: - thank you so much!

In the D3 dumbbell implementation, is it easy to make it so that the labels are listed as in the Highcharts example, as Y-axis labels, right-justified? … and bonus points for adding tooltips to the little circles displaying the actual corresponding percentage value. Pretty please! :slight_smile:

Glad to help. If you want a y-axis, take a look at the code for the horizontal bar chart:

If you want tooltips, here’s that example:

Alternatively, you could label the values directly or use the title attribute.

Can you please point me to an example that uses one of these approaches?

The dot plot example I linked demonstrates labeling the values (the dots) directly. Instead of showing the state name in the label, you’d show the value. Though personally I find it more important to label the names, because with lots of data, it’s difficult to trace a line from the y-axis to the correct dot.

As for using the title attribute, there are lots of examples, but to pick one at random:

In short, you append a title element to the thing you want to have a tooltip, and then set the text of the title element to be the desired tooltip text.

Thank you * 100! I got everything working as you indicated!!
Is there a way I can privately share my notebook with you?

Another question, if you don’t mind (and if I share my notebook you’ll see what I’m talking about):
I added y-axis data labels (like the state names in your example), but my labels are very long and I would need to set a very large left margin for them to fully show up (they’re being cut off at the beginning). I’m wondering whether there is a way to show these labels with an overflow indicator (like the ellipsis “…”) when the label doesn’t fit in a particular margin.

I found this reference about how to generate overflow labels for D3, but it applies to text() nodes, but the text on the yAxis in my notebook isn’t built like that, so not sure if/how this approach would apply.

You can enable link sharing on your notebook and then email me a link to it (or use this forum to send me a private message).

I’m not aware of an “easy” way to get text-overflow: ellipsis working in SVG, but I suspect you could using computed text metrics and binary search to do it manually. Here are a couple examples of text metrics:


You could also use foreignObject or an HTML overlay, but that’s not exactly easy either. I’ll try to put together an ellipsis example in SVG tomorrow.