= Hypermedia In Action
:chapter: 8
:sectnums:
:figure-caption: Figure {chapter}.
:listing-caption: Listing {chapter}.
:table-caption: Table {chapter}.
:sectnumoffset: 7
// line above: :sectnumoffset: 7 (chapter# minus 1)
:leveloffset: 1
:sourcedir: ../code/src
:source-language:
= Client Side Scripting
This chapter covers
* How scripting can be effectively added to a Hypermedia Driven Application
* Adding a javascript-based confirmation dialog for deleting contacts
// js
* Adding a three-dot menu in our contacts table
// alpine
* Adding a keyboard shortcut for focusing the search input
// hyperscript
* Adding support for re-ordering contacts via drag-and-drop
// off the shelf
[partintro]
== Scripting in Hypermedia-Driven Applications
"REST allows client functionality to be extended by downloading and executing code in the form of applets or scripts. This simplifies clients by reducing the number of features required to be pre-implemented."
-- Roy Fielding, Architectural Styles and the Design of Network-based Software Architectures
Thus far we have avoided writing any JavaScript for Contact.app, mainly because the functionality we implemented so far does not need it. Contrary to popular belief, hypermedia is not just for "documents" (where a document is considered essentially different to an "app"), and it has many affordances for building interactive experiences. We want to show that it is possible to build sophisticated web applications using the original model of the web without the abstractions provided by JavaScript frameworks. On the other hand, htmx itself is written in JavaScript, and we don't want our message to be interpreted as "JavaScript bad", or, more generally, "Client-side scripting bad."
The question isn't "Should we be scripting for the web?" but rather "How should we be scripting for the web?"
Scripting has been a massive multiplier of the Web's capabilities. Through its use, Web application authors are not only able to enhance their hypertext-based websites, but also create full-fledged client-side applications that can compete with native apps in how they work (although they don't always win when they do). In other terms, the Web became a distribution medium for non-REST apps in addition to being a RESTful system. When it's not used as a replacement for the RESTful architecture provided by the Web, however, scripting is extremely useful in Hypermedia Driven Applications.
You are scripting in a way compatible with HDAs if:
* The main data format exchanged between server of client is hypermedia, the same as it would be in an application with no scripting.
* Client-side state (other than the DOM) is minimized.
This style of scripting requires us to adopt different practices than what is typically recommended for JavaScript, as the most common advice often comes, naturally, from SPA or server-side backgrounds. We will see these new practices in action in the upcoming chapter.
Simply listing "best practices", however, is rarely convincing or edifying (and, frankly, it is often boring). So, we instead will frame them around shiny tools that work well for scripting in a HDA. We will use each of these tools to add a feature to ContactApp:
* An overflow menu to hold the _Edit_, _View_ and _Delete_ actions, to clean up visual clutter in our list of contacts
* Reordering contacts by dragging and dropping
* A dialog to confirm the deletion of contacts
* A keyboard shortcut for focusing the search box
The important idea in the implementation of each of these features is that they are implemented entirely client-side and yet they don't exchange information with the server using, for example, JSON. This constraint is what will keep the features within the bounds of a proper Hypermedia Driven Application.
== Scripting tools for the Web
The primary scripting language for the web is, of course, JavaScript, which is ubiquitous in web development today. A bit of interesting internet lore, however, is that JavaScript was not always the only built-in option. As the quote from Roy Fielding above indicates, _applets_ written in other languages such as Java were considered part of the scripting infrastructure of the web. In addition, there was a brief period when Internet Explorer supported VBScript, a scripting language based on Visual Basic.
Today, we have a variety of _transcompilers_ (often shortened to _transpilers_) that convert another language to JavaScript, such as TypeScript, Dart, Kotlin, ClojureScript, F#. There is also the WebAssembly bytecode format, which is supported as a compilation target for C, Rust, and the WASM-first language AssemblyScript. However, most of these are not geared towards an HDA-compatible style of scripting --- compile-to-JS languages are often paired with SPA-oriented libraries (Dart and AngularDart, ClojureScript and Reagent, F# and Elmish), and WASM is currently mainly geared toward linking to C/C++ libraries from JavaScript.
We bring this up because we are going to look at three different mechanisms for adding scripting to our Hypermedia Driven Application:
* Vanilla JS, that is, using JavaScript without depending on any framework.
* Alpine.js, a JavaScript library for adding behavior directly in HTML.
* _hyperscript, a non-JavaScript scripting language created alongside htmx. Like AlpineJS, it is usually embedded in HTML.
Let's take a quick look at each of these scripting options, so we know what we are dealing with. As with CSS, we are not going to deep dive into any of these options: we are going to show just enough to give you a flavor of each and, we hope, spark your interest in looking into each of them more extensively.
== Vanilla JavaScript
[quote, Merb]
No code is faster than no code.
Vanilla JavaScript is simply using JavaScript in your application without any intermediate layers. The term came into vogue as a play on the fact that there were so many ".js" frameworks out there to help you write JavaScript. As JavaScript matured as a scripting language, standardized across browsers and provided more and more functionality, the utility of many of these frameworks and libraries has diminished. (At the same time, however, SPAs have become more popular, requiring more elaborate JavaScript frameworks).
A quote from the humorous website http://vanilla-js.com captures the situation well:
[quote, http://vanilla-js.com]
Vanilla JS is the lowest-overhead, most comprehensive framework I've ever used.
The message of _VanillaJS_ here is that since the browser already has JavaScript baked into it, there isn't any need to download a framework for your application to function. This is true more often than we might like to admit, and is especially the case in HDAs, since hypermedia obviates many features provided by JavaScript frameworks:
* Client-side routing
* An abstraction over DOM manipulation, i.e.: templates that automatically update when referenced variables change
* Server side rendering (rendering here refers to HTML generation)
* Attaching dynamic behavior to server-rendered tags on load
* Network requests
Installation of VanillaJS couldn't be easier: you don't have to. You can just start writing JavaScript in your web application, and it will simply work.
That's the good news. The bad news is that, despite improvements over the last decade, JavaScript has some significant limitations as a scripting language that often make it less than ideal as a stand-alone scripting technology for Hypermedia Driven Applications:
* It is a relatively complex language that has accreted a lot of features and warts.
* JavaScript's asynchrony model involves _colored functions_, a concept described in Robert Nystrom's oft-cited _What Color is Your Function?_
footnote:[https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/]
* It is surprisingly clunky to work with events.
* DOM APIs (a large portion of which were originally designed for Java) are verbose and do not make common functionality easy to use.
None of these are deal-breakers, of course, and many people prefer the "close to the metal" (for lack of a better term) nature of vanilla JavaScript to more elaborate client-side scripting approaches.
To dive into Vanilla JavaScript as a front end scripting option, let's write a simple counter footnote:[The counter is a common example widget for UI development tools, a trend that seems to have been started by React. İt's unclear if the "counterexample" pun was intentional.]. It will have a number and a button that increments the number. Nothing too elaborate, but it will give you the flavor of each of the three scripting approaches we are going to use in this chapter.
A problem with tackling this problem in Vanilla JavaScript is that it lacks something most JavaScript frameworks
provide: a standardized code style. This is not an insurmountable issue, and in fact, it presents a great opportunity to take a small journey through various styles. For our counter, we will start with the simplest thing possible.
.Counter in vanilla JavaScript, inline version
[source,html]
----