Update as of Dec 2020: now that the Hey support code has been extracted and officially released under the name Hotwire, we know that this article actually speaks about the Turbo Frames. Basecamp renamed and polished a few things but the principles stay the same as described here.
In the previous post, I looked into the new feature of Turbolinks - the Frames. We saw that they allow you to download any page area content automatically from the server with a separate async request upon main page load.
But that’s not all! Often it is convenient to load a page area from the server not right after the main content loads, but some time later, when the user requests it. It’s all the menus, submenus, popup windows, overlays, alerts and more we’re talking about here! There are usually many of them on a typical site with users to log in and get their job done… The distinct quality of these elements is that they are not needed often, that’s why they are hidden from the main content view in the first place! And Turbolinks frames allow the browser to be unaware of them until specifically requested by the user.
Again, this is usually a good thing as it can greatly lighten and speed up your pages. Why load all the stuff that you need just once a day? On the other hand, I think this approach makes your users more dependent on a stable network connection. I guess this might be a part of the problem with the Hey web that its offline experience is not particularly great (however, note that the mobile app works offline nicely, we’re talking about the web here). AFAIK, the Hey team is looking into this matter so we’ll see what they come with later on…
Hey.com uses this technique quite heavily, let’s take a look at the contact page, for example: the main menu, my profile menu, the action to edit the contact name as well as the settings buttons underneath, all these elements are (re-)loaded freshly from the server when I click on them:
In the Network developer tool it looks like this (note that the requests were not simultaneous but sequential, as I clicked on the elements, one after another):
All responses to these requests are pure HTML and note that all of them are cached as I’ve already visited them a few minutes ago.
How does this work? In the previous post we learned that these updates are handled by the new Turbolinks custom element called
turbolinks-frame. It watches its
src attribute for changes and when such change occurs, it visits the
src URL via AJAX and replaces its own content with the server response.
So, all we have to do, is alter the given element’s
src attribute somehow to update the corresponding part of the web page. There are many ways to do it but all the usual ones are dependent on JS code (if you use Stimulus, you can e.g. find and update the element in a Stimulus controller).
When we reload the page to have it fresh and clean and use the Page inspector Element picker tool in Dev tools to show what’s inside the HEY logo, we’ll see something like this:
So, again, we have the
turbolinks-frame custom element here but this time it’s not empty, it contains a link. And this link has a
data-turbolinks-frame attribute with the value equal to the HTML id of the Turbolinks frame element. This must mean something!
If we search through the JS sources for
"data-turbolinks-frame", we’ll find, among other results, the
shouldInterceptNavigation in the
FrameController class that supports the frame element. This method returns
true if it finds a
data-turbolinks-frame attribute in the link and tells Turbolinks whether it should leave the link up to normal Turbolinks processing (i.e. visit the URL) or intercept it and handle differently (i.e. trigger update of that page part).
If the frame element decides to intercept the click event, the code eventually gets to the
linkClickIntercepted method, which just updates the
src attribute of the frame element:
As we’ve seen before, this change is noticed by the browser and leads to an AJAX call to the server followed by an update of the frame element’s content with the response.
Once the Hey main menu frame element is loaded, it is much more complex than before and it gets linked to a few Stimulus controllers that further define it’s behavior − open up the popup menu, activate live search, etc…
I’m pretty sure that most of what we talked about here, is a too low-level stuff, that you won’t need to deal with at all once they release the new Turbolinks. The Basecamp / Hey team will definitely come with some nice helpers / DSL that will make this pattern a breeze.
Also, clicking links for sure is not the only way to trigger the updates. I’ve seen in the source code that Turbolinks supports also forms associated with frame elements, so a form submit can trigger a partial page update, again without needing custom JS. But of course, as we said, it can be easily triggered from JS code, too.
Overall, it seems to me that the Turbolinks frames feature is a successor of the Rails UJS library, that, among other things, allowed to issue an AJAX request triggered by an interactive event and process the result somehow, without needing much JS code.
However the new Turbolinks frames are both more radical (normally they expect only HTML on the wire) and more opinionated (they simply replace the frame element content with the response). This alone, I guess, will cover most of the partial page update use cases, while still not touching any custom JS code! And if you need something more complex, you can always “sprinkle” your HTML with Stimulus attributes and put that JS code into controllers.
Next, we’ll take a short peek into some new syntactic sugar in the Stimulus controllers, stay tuned!