Black Lives Matter. Please consider donating to Black Girls Code today.

Responsive plotly.js was working, now it is not

About a week ago, I had some responsive plotly.js plots working and now they are not - I’m not sure if I changed something or if plotly.js was updated. I did read the post about including the d3 var explicitly, which I’ve now added to my app. However, it’s still not responsive.

Here’s the relevant code (assume data and layout are filled in):

var d3 = Plotly.d3;
var WIDTH_IN_PERCENT_OF_PARENT = 80,
    HEIGHT_IN_PERCENT_OF_PARENT = 50;
var gd3 = d3.select("div[id='Green Line E_reveal']")
    .style({
        width: WIDTH_IN_PERCENT_OF_PARENT + '%',
        'margin-left': (100 - WIDTH_IN_PERCENT_OF_PARENT) / 2 + '%',
        height: HEIGHT_IN_PERCENT_OF_PARENT + 'vh',
        'margin-top': (100 - HEIGHT_IN_PERCENT_OF_PARENT) / 2 + 'vh'
    });
var Green_Line_E = gd3.node();
Plotly.plot(Green_Line_E, data, layout, {showLink: false});
window.onresize = function() { Plotly.Plots.resize( Green_Line_E ); };

Am I missing something? This looks to me like it mimics the example at https://plot.ly/javascript/responsive-fluid-layout/.

The graphs show successfully and the Javascript console shows no runtime errors - it’s strictly the resizing where nothing occurs. I suspect it’s something with the last line of code (specifically, Plotly.Plots.resize()). I also tried using the recent solution of relayout to no avail (after wrapping it on a window.onresize function). You can view the full source code here.

Any help would be hugely appreciated. Thanks a lot.

@alexpetralia Thanks for writing in.

Your code is not missing anything, but rather has too many conflicting things.

Setting window.resize multiple times does not add as many on-resize handlers, but instead it overrides them, keeping only the last handler set active. In your case the last window.onresize statement resizes the blue line graph, which does behave responsively as intended.

To add multiple handlers to the same event, you’ll have to use to addEventListener method instead. For example,

window.addEventListener('resize', function() { Plotly.Plots.resize('blue_line'); });
window.addEventListener('resize', function() { Plotly.Plots.resize('red_line'); });

// ... and so on
4 Likes

Etienne, thank you so much, it worked! I hope this post can help someone else from making the same silly mistake in the future. Thank you for helping out so quickly.

No problem.

Your app looks slick. Looking forward to seeing the final version.

In case anyone else needs some clarification on this, you’ll need to use window.addEventListener instead of window.onresize. Took me a couple tries to get this working. I was able to get both ‘resize’ and ‘relayout’ working within the EventListener for multiple plots on a single page.

Anyone’s got this to work in a react component?
I tried this code in componentDidMount() but didn’t make any difference.

For now I am using this terrible hack

if(window.innerWidth < 800){
			layout.width = 470
		} else if(window.innerWidth < 1000){
			layout.width = 750
		} else layout.width = 900

It is not responsive but on initial load the chart figures out the correct width.
I would love to see a proper solution

I finally got this working in react.
I understand this is not totally relevant to plotlyjs but still posting the answer here.
This is quite to difficult to figure out for someone relatively new to javascript. Hopefully, it will save someone else a lot of time. This is what worked for me.

In componentDidMount()

const el = document.getElementById('aiplot')
PlotlyCore.newPlot(el, traces, layout, defaultPlotlyConfiguration)
window.addEventListener('resize', function() { PlotlyCore.relayout(el, {width: el.getBoundingClientRect().width, height: el.getBoundingClientRect().height}) })

And then in componentWillUnmount()

componentWillUnmount() {
    const el = document.getElementById('aiplot')
   window.removeEventListener('resize', function() { PlotlyCore.relayout(el, {width: el.getBoundingClientRect().width, height: el.getBoundingClientRect().height}) })
}

That’s it! None of d3 selectors and IIFEs necessary.
It’s working like a charm but if anyone spots a bug, please let me know.
Cheers!

1 Like

So, what’s a bottom line? Is it responsive? What if I dont want to specify a percentage of the parent? I just want my chart taking all available space any time I resize.

Thanks

I made an account just to thank you for this.
Indeed it’s not trivial, but with your comment it worked within minutes for my Angular app:

The HTML:

<div #chart></div>

The element ref:

@ViewChild(‘chart’) el:ElementRef;

The Resize Listener:

  @HostListener('window:resize', ['$eent'])
  onResize(event) {
  Plotly.Plots.resize(
      this.el.nativeElement,
      {
          width: this.el.nativeElement.getBoundingClientRect().width,
          height: this.el.nativeElement.getBoundingClientRect().height
      });

}

1 Like

Hi, etienne, I am trying to resize multiple plots on the same page. I think this post about “addEventListener” will do the work.

However, I don’t understand what is the ‘blue_line’ in the resize function is. Is it a gd3.node() or the id of the <div> in html?

Thanks.

Thanks kausti, your solution got me on the right track.

For React in 2020, we can use React.CreateRef() instead of looking up the dom element by ID. We don’t have to import PlotlyCore or call PlotlyCore.relayout at all.

Here’s how I did it in a component:

https://pbnqj.csb.app