It turns out that I was using an incorrect event, I should have been using 'shown' instead of 'show', since the former is fired after the Bootstrap's CSS animation completes, the latter is fired as soon as the panel is opened i.e. during the collapse animation. I also needed to scroll to the panel heading.
These screenshots demonstrate the problem. I have a number of collapsed panels, when a panel is shown, I dynamically load its content, which can have a variable height, depending on the panel.
If the panel was close to the top of the screen, once the dynamic content loaded, it would be shown incorrectly off-screen.
Whereas, this is what should have been happening...the panel and its heading being shown at the top of the screen instead.
This is how I solved it...the divId calculation is not shown here as that can be implementation specific, I also don't show the URL of the dynamic content I'm loading since that is not relevant to this example.
For the above to work, I nest a div element with and ID inside my panel-body div, like this...I've simplified this slightly, my actual div has a loading animation and the ID is assigned dynamically using PHP.
So now whenever I click a panel heading to expand it, it's scrolled to the top as expected. There is a small delay while loading the dynamic content, but positioning happens before this content is loaded. The video below demonstrates this in action.