The way I wrote webapplications in the past was referencing single javascript libraries and plugins and call their methods either in inline scripts or with a final javascript file. This way you could add scripts per page, like a large chart library or maps and you only load what you need.

This is all possible because there is a full reload for every webpage which fetches all the resources like additional javascript or css.

html
    head
        link(href="page.css")
    body
        div.row
            div.col-6#map
            div.col-6#content
        script(src="https://cdn.com/jquery.js")
        script(src="https://cdn.com/leaflet.js")
        script(src="/app.js")

With the need of websites behaving app-like and the wish for softer and slicker page transitions, single-page apps (SPA) became popular and there was no hard reload anymore.

It doesn't matter if you use Vue, Turbolinks, Intercooler oder anything that doesn't break the javascript lifecycle with a hard server reload – you have to rethink how to write javascript.

The Problem

In my example I used the pretty neat library Unpoly, that is helping to enhance classic html websites with smooth page transitions by swapping out server rendered html via ajax, without reloading the page.

As long as you load all the used javascript on your first pageload, you are fine. But if you are used to add site specific javascript inline or load it on demand, you are out of luck, since unpoly doesn't parse any script tags in the returned html fragment1.

Imagine this scenario by navigate from page Index...

html
    head
        title "Index"
        link(href="page.css")
    body
        div.row
            div.col-6#map
            div.col-6#content
                a(href="/map", up-target="body")
        script(src="https://cdn.com/unpoly.js")

...to page Map with Unpoly. The <body> gets swapped out with the following and you assume the new script tags are going to be executed.

html
    head
        title "Map"
        link(href="page.css")
    body
        div.row
            div.col-6#map
            div.col-6#content
        script(src="https://cdn.com/unpoly.js")
        script(src="https://cdn.com/leaflet.js")
        script.
            var map = L.map('map').setView([50.493054,12.8190702], 16);

But that is not the case.

With a full page reload I get Unpoly and the Leaflet library and then I initialize the map afterwards.

But if I navigate to this page with Unpoly, the script tags are getting ignored and even if I use Unpoly compilers leaflet.js is not available1

The solution to this is

  1. move the inline javascript into it's own file and wrap it in up.compiler()
  2. load the additional assets like the Leaflet library and its css via javascript

The Solution

Load script and css files dynamically. I use these functions for loadScript and loadCSS but anything else, like jQuery's $.getScript()should work. Just make sure, that script tags are added only once (both linked script take care of that).

html
    head
        title "Map"
        link(href="page.css")
    body
        div.row
            div.col-6#map
            div.col-6#content
        script(src="https://cdn.com/unpoly.js")
        // load app.js in your initial page call.
        script(src="/app.js")
// app.js
// if you need the promise polyfill https://polyfill.io/v3/polyfill.min.js?flags=gated&features=Promise
up.compiler('#map', function(e) {

    // include loadCSS function in your script
    // @link https://gist.github.com/marcus-at-localhost/514ce85df6fb3ab10719a7cacce50770
    loadCSS('https://cdn.jsdelivr.net/npm/leaflet@1.5.1/dist/leaflet.min.css');

    // include the loadScript function
    // @link https://gist.github.com/marcus-at-localhost/da0cc3da4bfa70399963a4bd32b9e5bf
    loadScript('https://cdn.jsdelivr.net/npm/leaflet@1.5.1')
    .then( function () {
        var map = L.map('map').setView([50.493054,12.8190702], 16);
    });

    return function(){
        map.remove();
    };
});

You can look at the code of this page https://schmetterlinge-im-luchsbachtal.de/entdecken to see it work.

Update: just found a complete article from the Unpoly devs, oh well :).