Loading a javascript library in a shortcode

29 views
Skip to first unread message

Paul Moore

unread,
Apr 16, 2023, 10:30:09 AM4/16/23
to nikola-discuss
This is probably more of a Javascript question than a Nikola one, but I want to create a template shortcode that relies on a Javascript library. The dirt simple approach is to just have a `<script src=xxx>` element in the shortcode output, but that means that if I use the shortcode twice, I'll be loading the library twice. And any initialisation I do will happen twice, etc.

Is there a good way to put some sort of "do this once per page" block into a shortcode template, so that the actual shortcode logic can rely on it? The alternative, I guess, is to write a shortcode plugin which adds stuff to `site.template_hooks["extra_head"]`, but that seems like overkill here...

Paul

Chris Warrick

unread,
Apr 16, 2023, 2:10:26 PM4/16/23
to nikola-...@googlegroups.com
You can’t easily do something like that in shortcodes, as they cannot
modify the context used to render pages that contain the post. You
could solve this with JS in two ways:

1. The shortcode embeds the full library JS, which checks if a global
variable was defined. If it’s defined, it exits without doing
anything; if it’s not defined, it defines it and then proceeds to do
its thing. This may or may not be good, depending on what the JS
library does (if it changes the HTML/DOM when included and not when
the document is ready, it will not work if the shortcode appears
twice).
2. The shortcode adds a single line of JS that sets a global variable,
and then another piece of JS in BODY_END checks the variable and runs
the library only if needed. This may be wasteful if you use the
shortcode very rarely in your content.

--
Chris Warrick <https://chriswarrick.com/>
PGP: 5EAAEA16

Paul Moore

unread,
Apr 16, 2023, 3:55:08 PM4/16/23
to nikola-discuss
On Sunday, 16 April 2023 at 19:10:26 UTC+1 kwpo...@gmail.com wrote:
On Sun, 16 Apr 2023 at 16:30, Paul Moore <p.f....@gmail.com> wrote:
>
> This is probably more of a Javascript question than a Nikola one, but I want to create a template shortcode that relies on a Javascript library. The dirt simple approach is to just have a `<script src=xxx>` element in the shortcode output, but that means that if I use the shortcode twice, I'll be loading the library twice. And any initialisation I do will happen twice, etc.
>
> Is there a good way to put some sort of "do this once per page" block into a shortcode template, so that the actual shortcode logic can rely on it? The alternative, I guess, is to write a shortcode plugin which adds stuff to `site.template_hooks["extra_head"]`, but that seems like overkill here...
>
> Paul

You can’t easily do something like that in shortcodes, as they cannot
modify the context used to render pages that contain the post. You
could solve this with JS in two ways:

1. The shortcode embeds the full library JS, which checks if a global
variable was defined. If it’s defined, it exits without doing
anything; if it’s not defined, it defines it and then proceeds to do
its thing. This may or may not be good, depending on what the JS
library does (if it changes the HTML/DOM when included and not when
the document is ready, it will not work if the shortcode appears
twice).

Thanks. I had thought of doing something like that. I think for the sort of libraries I'm looking at, I can probably get away with it, but it would still be fiddly. I'd probably want to do something like

<script>
if (typeof lib_is_loaded !== 'undefined') {
    // load the library
    lib_is_loaded = true;
}
</script>

The fiddly bit is that "load the library", as most libraries tell you how to use <script src="..."> to load them, but not how to do so in raw Javascript. So I'll need to do some reseatch for that bit.
 
2. The shortcode adds a single line of JS that sets a global variable,
and then another piece of JS in BODY_END checks the variable and runs
the library only if needed. This may be wasteful if you use the
shortcode very rarely in your content.

To use BODY_END (or EXTRA_HEAD) I'd need to write a plugin in Python, wouldn't I? Or can I set that in a template shortcode as well? Although I'm getting the impression that doing this *without* a plugin may be more difficult than it's worth...

Alternatively, is there a way to say "if the page/post has a particular value in its metadata, add the following to the page HTML somewhere (header or body end seem like reasonable options)"? Basically, I'm thinking of a custom equivalent to the `has_math` metadata value.

Paul

Chris Warrick

unread,
Apr 16, 2023, 5:13:42 PM4/16/23
to nikola-...@googlegroups.com
On Sun, 16 Apr 2023 at 21:55, Paul Moore <p.f....@gmail.com> wrote:
> To use BODY_END (or EXTRA_HEAD) I'd need to write a plugin in Python, wouldn't I? Or can I set that in a template shortcode as well? Although I'm getting the impression that doing this *without* a plugin may be more difficult than it's worth...

You can’t do that from a template shortcode.

> Alternatively, is there a way to say "if the page/post has a particular value in its metadata, add the following to the page HTML somewhere (header or body end seem like reasonable options)"? Basically, I'm thinking of a custom equivalent to the `has_math` metadata value.

You could try doing the same thing math_helper.tmpl does in your
site’s theme/templates (the math_scripts_ifpost(s) functions are
called from other templates where math might be needed). has_math is
an attribute on post objects; your custom meta field won’t be, but you
can still access the metadata dictionary with
post.meta['lang'].get("has_fancy_js_library"). You could also expose a
custom Python function in GLOBAL_CONTEXT to make this easier (this
might be necessary if you’re using Jinja2, which doesn’t allow
arbitrary Python in templates.)

Paul Moore

unread,
Apr 16, 2023, 5:16:50 PM4/16/23
to nikola-discuss
Thanks. That all sounds quite complex, particularly for something that in all honesty I don't expect to need that often. I'll have a think about whether I want to go any further down this rabbit hole :-)

I appreciate the help.
Paul

Vinay Sajip

unread,
Apr 17, 2023, 2:14:38 PM4/17/23
to nikola-discuss

This might not be appropriate for your use case, but I found it useful to load the JS libraries I need using the extra_js template block - then everything in the page can rely on them being there. Of course, if the libraries take a long time to load or use humongous amounts of memory, that might be a non-starter.

Vinay
Reply all
Reply to author
Forward
0 new messages