I've found the reason for both the initial behaviour and the workaround - and it's all amCharts specific (nothing to do with SVG per se) so I'm rephrasing the title accordingly.
What happens is that when amCharts creates the SVG, it needs to (or at least, decides to) define the width and height in absolute terms. These are based on the size of the target div, obtained via the offsetWidth
and offsetHeight
properties.
The inactive tab has the display: none
property set, and as a result this part of the DOM is not even rendered, so returns zero for both size properties. This ultimately leads to amCharts creating a 0x0 SVG chart when chart.write
is called for the hidden div.
Resizing fixes things because each chart registers a listener to the onresize
window event, which calls the chart's handleResize
method. This forces a recalculation of the width and height based on the div's new (current) dimensions.
So in conclusion I think there are two alternative ways to handle this:
- Call
chart.write
for a chart when and only when its tab becomes visible.
- Call each chart's
handleResize
method when the tabs change.
(The first option avoids the initial hit of rendering an invisible chart, but then does a full redraw every time the tabs are changed. The latter takes a hit up-front but is likely quicker thereafter. For bonus marks, the best solution would be to render each chart exactly once between each resize, the first time it becomes visible, but that's a lot more complex as it would involve interfering with the default event listeners, amongst other things.)
Update: There's further complications with rendering an invisible chart; in particular, I found issues with the height calculations not taking into account the space required by the domain axis and so stretching the chart out of its div. This wasn't fixed by calling handleResize
- calling measureMargins
beforehand looked like it should work but didn't. (There's probably another method one could call after this to make it work such as resetMargins
but at this point it started to feel very flaky...)
As such I don't think it's practical to render a chart for the first time on a non-visible div, so I went with some combination of the bullets above. I listen for when a chart's tab becomes visible for the first time and then call chart.write
for the appropriate chart object - and whenever the tabs change, all previously-rendered charts are told to handle the resize.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…