NVD3 Charts not rendering correctly in hidden tab
Asked Answered
P

7

19

I am building a page which contains many charts, which are displayed one at a time depending on which tab you are looking at.

The chart in the initially active tab renders correctly. However when I click to another tab, the chart is not rendered properly.

Presumably this is because the hidden field does not have dimensions until it is made visible. In fact if I resize the window the chart will correct it's proportions, and render so that it fills the available width.

I can fix this problem by explicitly defining the chart size via css, but this defeats the responsive aspect of the charts.

Can anyone tell me how to trigger the same NVD3 event which gets activated when the window resizes? That way I can bind it to the selection of a new tab, and hopefully remedy the rendering issue.

Pompano answered 4/2, 2014 at 21:16 Comment(1)
P
12

I figured out how to trigger the resize event I needed. In my case the tabs are driven by bootstrap. So I simply modified my bootstrap show tab event to trigger a page resize event as well. It's a little indirect, but it gets the job done:

jQuery('#myTab a').click(function (e) {
    e.preventDefault()
    jQuery(this).tab('show')
    jQuery(window).trigger('resize'); // Added this line to force NVD3 to redraw the chart
})
Pompano answered 4/2, 2014 at 22:19 Comment(2)
For some reason using jQuery fails for me. I need to use another method (thru creating the event and dispatch it) as mentioned in this post by Slava: #8712536Letha
Works for other bootstrap rendering problems (on hidden element), triggering resize saved me a lot of work. Thank you!Forney
S
16

I had the same issue (charts on multiple tabs), and this is the only thing that I could get to work.

$(function () {
    $(document).on('shown.bs.tab', 'a[data-toggle="tab"]', function (e) {
        window.dispatchEvent(new Event('resize'));
    });
});

I have a feeling, however, that all of the charts are being re-rendered, regardless of whether they are on the active tab (visible) or in the non-selected tabs (hidden).

Does anyone know how to ensure ONLY the active chart gets resized / redrawn?

Sena answered 22/3, 2015 at 4:15 Comment(0)
P
12

I figured out how to trigger the resize event I needed. In my case the tabs are driven by bootstrap. So I simply modified my bootstrap show tab event to trigger a page resize event as well. It's a little indirect, but it gets the job done:

jQuery('#myTab a').click(function (e) {
    e.preventDefault()
    jQuery(this).tab('show')
    jQuery(window).trigger('resize'); // Added this line to force NVD3 to redraw the chart
})
Pompano answered 4/2, 2014 at 22:19 Comment(2)
For some reason using jQuery fails for me. I need to use another method (thru creating the event and dispatch it) as mentioned in this post by Slava: #8712536Letha
Works for other bootstrap rendering problems (on hidden element), triggering resize saved me a lot of work. Thank you!Forney
P
8

Just add this JavaScript:

$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
  window.dispatchEvent(new Event('resize'));
})

hidden.bs.tab is the event that fires after a new tab is shown as per the Bootstrap docs. This code fires a resize event after each tab change.

Peri answered 2/4, 2015 at 5:11 Comment(0)
S
5

Reason For New Answer

Vanilla Javascript is necessary for a lot of people in 2018. As a lot of frameworks and Javascript libraries that exist today do not play well with jQuery. Once upon a time answering all Javascript problems with a jQuery solution was acceptable but it is no longer feasible.

Problem

When loading C3.js or D3.js graphs, if the viewport is not actively in site during page load the graphs do not render correctly.

Example

  1. If you type in the URL then open a new tab and then go back after your page loads.

  2. If you refresh the page that has your graphs on it then minimize the browser and open it back up after the page has loaded.

  3. If you refresh or go to the page with the graphs then swipe away to a new window on your computer. Then go back to the page with the graphs after they have loaded.

In all these cases your C3.js / D3.js graphs will not render correctly. Typically you will see a blank canvas. So if you were expecting a bar chart, you would see a canvas without the bars being drawn.

Background

Although I have seen this question answered I needed an answer that did NOT use jQuery. Now that we have reached the days of everything can not be fixed with jQuery I thought it seemed fit to provide a vanilla Javascript answer to this question.

My team faced the issue that the C3.js / D3.js graphs would not load if you refreshed the page and swiped away or minimized. Basically if you did not stay on the page and keep it in site till it was done loading you would not see the graphs till you resized the page again. I know this is a problem that happens to everyone using C3.js / D3.js but we are specifically using Lightning in Salesforce.

Answer

Our fix was to add this in the controller function that initializes the charts. Anyone can use this in any function they write to initialize their C3.js / D3.js graphs regardless of their stack. This is not Salesforce dependent but it does indeed work if you are facing this issue in Salesforce.

document.addEventListener('visibilitychange', () => {
   console.log(document.visibilityState);
   window.dispatchEvent(new Event('resize'));
});
Stenger answered 1/4, 2018 at 15:58 Comment(0)
C
4

I was facing same issue. I was using ng-show to make div hidden . Once I replaced ng-show with ng-if I am able to see graph drawn in expected behavior. Explanation:

  1. When we use ng-show or ng-hide to make div hidden it only changes it display property but div will be in dom.

  2. When we use ng-if div is removed from dom and again added to dom so internally it performs redraw operation on nvd3 graph too. Hence we see correct graph instead of squished one.

Circumvent answered 19/7, 2016 at 7:21 Comment(0)
A
0

The event that usually triggers a redraw is the window resize event -- NVD3 doesn't use a custom event for this. You can control this yourself though; the usual definition is

nv.utils.windowResize(function() { d3.select('#chart svg').call(chart); });

(where "#chart" is the element that contains the graph). There's nothing stopping you triggering the code on another event or even just running the redraw code explicitly when you change the tab.

Aldas answered 4/2, 2014 at 21:31 Comment(6)
I tried that... Nothing happens, if I look in the console it returns a single cryptic word: 'undefined'Pompano
nv.utils.windowResize(function() { d3.select('#chart-grade-3 svg').call(chart); }); Where #chart-grade-3 is the parent div containing the svg I need to target.Pompano
You would still need to trigger the resize event when you switch tabs.Aldas
I understand that. I tried manually triggering the event via console after switching tabs just to see if it would work. No luck.Pompano
As I've said, you can always just run the code inside the event handler.Aldas
If it doesn't work via console, why would it work any differently in the event handler? The net result would be the same. I think chart is what the 'undefined' message is referring to. But I'm not clear on how to define it.Pompano
O
0

a more efficient approach would be to use the chart.update() method

var chart_x = nv.models.somechart()
var chart_y = nv.models.somechart()

..... show charts

jQuery('#myTab a').click(function (e) {
    e.preventDefault()
    jQuery(this).tab('show')
    if(jQuery(this)...something === '..x..')
      chart_x.update(); //CALL THE UPDATE
    else ...  

})

Odrick answered 27/11, 2017 at 23:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.