Say hello to Clerk: In-page navigation for Single-Page Applications

clojurescript

#1

I have just released a tiny library that manages scroll positioning for ClojureScript SPAs. I call it Clerk.

It solves a problem with Single Page Applications that want to behave like ”regular” sites do, i.e. what the user perceives as new pages are just content changing in the current page and without attention to this the scroll position will remain when navigating to new ”pages”:

or strange things will happen when navigating the browser history within the SPA.

The core design of Clerk is tried and tested in the project I work on for a living. Even if the API and packaging is freshly made and you can expect some bugs in this first release. I would love me some feedback!

Note that if you want to try out Clerk’s support for navigation to in-page anchor targets you should not use Secretary, because it does not really support it. Bidi does, however, so you can try it out with that.

EDIT: Online demo here: https://clerk-demo.netlify.com/


#2

This is extremely useful and the first library I’ve seen in ClojureScript OR Javascript that aims to tackle this problem on its own. Thanks!


#3

This is very good!

Did you consider dropping the reagent- and rum-specific code from the main namespace, and just documenting in the Readme, or in some code examples, how to use Clerk with various front-ends?


#4

I did consider that. Still considering, It started with that the Reagent after render seemed a bit involved for the user of Clerk, so it seemed worth it to help with that and since I use Rum myself also make that mixin. But then I remembered that reagent/after-render function and I think maybe the lifecycle map is not really needed, or at least could be mentioned in some usage doc somewhere instead of living in Clerk core. And the Rum mixin is rather unnecessary.

What are the pros and cons, in your view? Specifically, why did you think this might be superfluous in the core namespace? (I am curious mostly because this is my first library and I think the API design is a quite tricky part of it.)


#5

I just tried Clerk with the cljs-spa-example and learned two things:

  1. Clerk was missing to add a dependency on core.async. (This is now fixed in 0.1.1-SNAPSHOT)
  2. Clerk depends on HTML5 history support. (Added this discovery to the README).

Are there yet anyone needing to target browsers lacking HTML5 history support?


#6

I was thinking from the perspective of maintainability: Which frameworks do you decide to support in core, which don’t you, and why? Do you really want to have to release an update to Clerk because e.g. Reagent changes something that breaks Clerk’s integration? Also, it is such a nicely minimal and contained library that it just feels better to not include unnecessary stuff. :slight_smile:

EDIT:

You asked for pros as well. As a user I definitely consider it a pro that I can bring in the library and immediately use with my UI framework of choice. However, that is only a pro so long as Clerk stays up to date with the UI frameworks.


#7

:smile: :heart:

What you say makes sense. Thanks for bringing my attention to it this quickly. Only three downloads from Clojars so far, so I decided to remove those utilities as they really did not motivate their potential weight in maintenance. Version 1.0.0-SNAPSHOT is out. Hopefully I can avoid removing stuff from the API going forward.


#8

I have added a test/demo project to the Clerk repo now. And also deployed it on Netlify so there is an online demo to make it easier to see what Clerk does. (It has a checkbox where you can switch Clerk on and off, even).

https://clerk-demo.netlify.com

Please note that there’s an issue with Accountant not dispatching a navigation when clicking a link with the same URL as the current one. This might make a test like:

  1. Switch Clerk off
  2. Click ”Go to bottom of page”
  3. Switch Clerk on
  4. Click "Go to bottom of page”

seem like it fails to show that Clerk makes a difference. But it is really just that you clicked the same link twice.

There is supposedly an option in Accountant to make it dispatch navigation for these situation (an option I happen to have helped with adding a while ago). But it doesn’t seem to work and, actually even makes things worse.I think I must try chase down this issue in Accountant, because it makes things feel quite broken at times.


#9

Nice demo! Clicking around in the lots of a’s and b’s, it did not look like Clerk made that big of a difference; but it became very clear with the links to the top of lots of b’s from lots of a’s. Very good! :+1:


#10

Very nice! Would love to put this into https://github.com/ingesolvoll/kee-frame.


#11

That would make wonders for the Clerk README. :smile:

I can cook a PR, since I need to check out kee-frame anyway.


#12

A PR would be super nice!


#13

Implementing Clerk in something like kee-frame is a bit of challenge, as @ingesolvoll and I have discovered.

It’s only partly kee-frame-related. It will be a smal challenge in many non-trivial apps. But kee-frame supports all kinds of apps so adding Clerk to it will need some solid thinking.


#14

I’d like some input on possible developments of Clerk. A thing I’d like to support is when navigation routing is used for things that are really not different pages (from the users perspective).

  1. One such scenario is a tab bar:

    1. If each tab is a route/has its own path, clicking them will currently cause Clerk to scroll to the top of the page (“thinking” it’s a new page).
    2. If each tab is a target anchor, clicking them will currently cause Clerk to scroll the tab widget to the top of the page.
  2. A thing that an SPA makes quite easy to do is to let the user click through a series of steps in a module on the page (a ”Wizard”). This suffers from the same Clerk behaviour as the tab bar.

It’s possible that the tab bar and the wizard are conceptually the same problem. I have not wrapped my mind around it well enough yet to tell. What would you say? From the users perspective the tab bar is probably stronger connected with an expectation that the widget should stay on the same position on the page. But it is still inconvenient in the wizard scenario.

I’m wondering what would be a good API to support widgets like the above. Should it be supported on initialization, or when navigating, or maybe both? Clerk currently only has the notion of the path,(.pathname js/location), but maybe it would be convenient to be able to provide rules using whatever notion of routes that the application may have?

Thanks in advance for any feedback!