Tutorial

LiveView and page specific javascript

hooks liveview javascript

In most applications you have some page specific javascript that is only used in one or just a few pages. The solution for this is to either setup your javascript build system to output several bundles that you can include on the pages you want or import the script files from the CDN sources on the pages you want and then use them in your code.

In this tutorial, I want to show how I use option two, import the javascript from external CDN:s and use that in conjunction with Phoenix LiveView to avoid having the user to download a larger javascript bundle that is not needed on most pages. This is a pattern that I have started to follow recently and that I feel good about.

On my new site, https://livesaaskit.com/ I use this in a couple of places. The first example is when a user wants to copy a snippet of code.

On the top right in the code box, there is an icon that when you click on it, it copies the entire code snippet. For this I use the library https://github.com/feross/clipboard-copy which is available as a NPM package but I prefer to keep my bundle as small as possible.

This works by adding an event listener to the click event on the copy button. Since my entire site is a LiveView site, I want to set this up by using a LiveView hook.

Since I don’t want to add the library to my javascript bundle, I need to import it from an external source. One of the sources I like to use is Skypack. https://www.skypack.dev/view/clipboard-copy

The example says I can use it like:

<script type="module">
  import clipboardCopy from 'https://cdn.skypack.dev/clipboard-copy';
</script>
So, in the bottom of my root layout file, I use it like:
<!-- lib/live_saas_kit_web/templates/layout/root.html.heex -->
<script type="module">
  import clipboardCopy from 'https://cdn.skypack.dev/clipboard-copy'
  window.clipboardCopy = clipboardCopy
</script>

Note that I attach it directly to the window, which you probably should be a little careful about to avoid name collisions.

Since I want to use this is a Phoenix LiveView hook, I need to add the hook on the button and I name it CodeCopy

<!-- show.html.heex -->
<button phx-hook="CodeCopy" id="copy-button-1" type="button" title="Copy Code" class="btn copy-block-btn btn-circle text-opacity-30 hover:text-opacity-70">
  <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
  </svg>
</button>

Then I define the hook in javascript like this. Note that I add an event listener to the button and listens for a click. And when someone clicks the button, I copy the content of the codeblock.

// assets/js/code_copy.js
export const CodeCopy = {
  mounted() {
    const elm = this.el    const btn = elm.querySelector('.copy-block-btn')
    const content = elm.querySelector('code') && elm.querySelector('code').innerText    if (btn && content) {
      btn.addEventListener('click', () => {
        clipboardCopy(content)
      })
    }
  },
}

Note that clipboardCopy are available from window object through the Skypack import.

I have come to like this approach since I can keep the javascript bundle as small as possible and I can even avoid using any kind of javascript processing.

Related Tutorials

Published 03 Mar - 2020
Updated 01 May - 2020

Share LiveView state between tabs

Each LiveView on each tab spawns a separate state. That might or might not be the desired behaviour. In this tutorial, I am going to share state bet..

Published 29 Jan - 2020
Updated 01 May - 2020

Send events from JS to a LiveView component

Let say you app uses a javascript library that needs to interact with your app. For example a LiveView component. That is possible with the built in..