Showing loading icon while app loads

I have been trying to resolve this the last few days, but with no success. I want to show a spinner while a bokeh app loads. I can either get a spinner that doesn’t disappear when the charts have loaded, or get no spinner at all. But I can’t get one that shows only while the charts are in the process of loading.

The app has a “templates” folder with index.html, styles.css and main.js. My problem is (I think) I don’t know what code to be using in the js file to show the app has finished loading.

index.html

<head>
        <style>
            body {}
        </style>
        <meta charset="utf-8">
        {{ bokeh_css }}
        {{ bokeh_js }}
        <style>
             {% include 'styles.css' %}
        </style>
        <div class="content">
        </div>
    </head>
    <body>
        <script>
            {% include 'main.js' %}
        </script>
        <div id="loading"></div>
        {{ plot_div|indent(8) }}
        {{ plot_script|indent(8) }}
    </body>
</html>

styles.css

@import url(https://fonts.googleapis.com/css?family=Roboto:100);

body { margin-top: 100px; background-color: #137b85; color: #fff; text-align:center; }

h1 {
  font: 2em 'Roboto', sans-serif;
  margin-bottom: 40px;
}

#loading {
  display: inline-block;
  width: 50px;
  height: 50px;
  border: 3px solid rgba(255,255,255,.3);
  border-radius: 50%;
  border-top-color: #fff;
  animation: spin 1s ease-in-out infinite;
  -webkit-animation: spin 1s ease-in-out infinite;
}

@keyframes spin {
  to { -webkit-transform: rotate(360deg); }
}
@-webkit-keyframes spin {
  to { -webkit-transform: rotate(360deg); }
}

main.js

window.addEventListener("load", () => {
    document.querySelector('#loading').style.display = "none";
  })

I originally posted on SO, but as I didn’t get a response, and had spent some more time working on this since, am posting here instead.

I’ve managed to get the loading spinner icon to appear and disappear, using


def callback(event):
        div_spinner.visible=False

curdoc().add_root(div_spinner)

curdoc().on_event(DocumentReady, callback)

But the the icon appears only just before the charts. There is still a long period of of white space before the icon comes up. I tried adding an app_hooks.py file with this code, but this made no difference.

from bokeh.models.widgets import Div
spinner_text = """
<!-- https://www.w3schools.com/howto/howto_css_loader.asp -->
<div class="loader">
<style scoped>
.loader {
    border: 16px solid #f3f3f3; /* Light grey */
    border-top: 16px solid #3498db; /* Blue */
    border-radius: 50%;
    width: 120px;
    height: 120px;
    animation: spin 2s linear infinite;
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
} 
</style>
</div>
"""
div_spinner = Div(text=spinner_text,width=120,height=120)

def on_server_loaded(server_context):
    pass
    div_spinner.visible=True