Infinite Scrolling in Go

A screenshot of the MovieGoers application
A screenshot of the MovieGoers application

I’m continuing my exploration server-side rendered HTML with HTMX, to achieve of SPA-like features, with no JavaScript frameworks.

This time I’m implementing a standard SPA sample project, namely a TMDB client.

The source code is on Github. The UI is inspired by this Angular example. This Angular app is admittedly fast, but my version is at least as fast.

A screenshot of the Google Chrome Lighthouse performance results
The performance results from Google Chrome Lighthouse. The application was deployed on Heroku and performs similarly to our Angular inspiration (that gets a 92 in performance from Lighthouse)

The SPA-like features of this app are

  1. Infinite scrolling
  2. Smooth changing of the genre with no full page reload

The starting point was a plain HTML application that can be used without JavaScript. Adding Ajax is just a matter of including the HTMX library, and adding appropriate HTMX attributes.

The infinite scrolling effect is achieved by adding a few HTMX attributes to the element that we want to animate:

<div class="grid-container" id="movieGrid">
  {{ $nextPageUrl := printf "/?page=%d&genre=%d" $.nextPage $.selectedGenre }}
  {{  range $index, $movie := .movies }}
  <div
    class="movie"
    {{ if eq 19 $index }}
      data-hx-get="{{ $nextPageUrl }}"
      data-hx-trigger="revealed"
      data-hx-swap="afterend"
      data-hx-select="#movieGrid .movie"
    {{end}}
  >
    {{ if .PosterPath }}
      <img src="https://image.tmdb.org/t/p/w185/{{.PosterPath}}" alt="Movie poster">
    {{ else }}
      <img src="images/no_poster_available.jpg" alt="Movie poster missing">
    {{end}}
    <h3>{{.Title}}</h3>
    <p class="overview">{{.Overview}}</p>
  </div>
  {{ end }}
</div>

The key attribute is data-hx-trigger="revealed" that kicks in when the user scrolls the page down enough to reveal the last element; in this case I hardcoded 19 because the application always delivers pages of 20 movies. The other HTMX attributes we add are

The other bit of HTMX is in the genre selection form.

<form action="/"
  data-hx-trigger="change"
  data-hx-get="/"
  data-hx-target="#movieGrid"
  data-hx-select="#movieGrid .movie"
  data-hx-push-url="true"
  data-hx-swap="innerHTML show:#main:top"
>
  <label><input type="radio" name="genre" value="0" {{if eq 0 $.selectedGenre}} checked="checked" {{end}}> All</label><br>
  <br>
  {{ range .genres }}
    <label><input type="radio" name="genre" value="{{.Id}}" {{if eq .Id $.selectedGenre}} checked="checked" {{end}}> {{.Name}}</label><br>
  {{ end }}
  <input id="submitGenre" type="submit" value="Choose">
  <script>document.getElementById("submitGenre").style.display = "none"</script>
</form>

Here we add three more HTMX attributes:

The bit of JavaScript is meant to hide the submit button when JavaScript is enabled. If JavaScript is not enabled, the HTMX attributes have no effect and this form works perfectly well as a non-Ajax, traditional HTML form.

The conclusion? It seems to me you can get sleek SPA-like effects at a fraction of the development cost (assuming you already have a backend team) and with a performance that is at least comparable to what you get with the big JavaScript frameworks.

Want to leave a comment? Please do so on Linkedin!

#Go   #Webapp