Skip to content

Instantly share code, notes, and snippets.

@city41
Last active January 19, 2021 12:51
Show Gist options
  • Star 27 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save city41/aab464ae6c112acecfe1 to your computer and use it in GitHub Desktop.
Save city41/aab464ae6c112acecfe1 to your computer and use it in GitHub Desktop.
ClojureScript secretary client side navigation without hashes

This is the example that comes with the reagent template converted to use HTML5 based history. This means there are no # in the urls.

I just got this working, so there might be better approaches

The changes are

  • use goog.history.Html5history instead of goog.History
  • listen to clicks on the page, extract the path from them, and push them onto the history
  • listen to history changes, and have secretary do its thing in response
(ns routing.core
    (:require [reagent.core :as reagent :refer [atom]]
              [reagent.session :as session]
              [secretary.core :as secretary :include-macros true]
              [goog.events :as events]
              [goog.history.EventType :as EventType])
    (:import goog.history.Html5History
             goog.Uri))

;; -------------------------
;; Views

(defn home-page []
  [:div [:h2 "Welcome to routing"]
   [:div [:a {:href "/about"} "go to about page"]]])

(defn about-page []
  [:div [:h2 "About routing"]
   [:div [:a {:href "/"} "go to the home page"]]])

(defn current-page []
  [:div [(session/get :current-page)]])

;; -------------------------
;; Routes

(secretary/defroute "/" []
  (session/put! :current-page home-page))

(secretary/defroute "/about" []
  (session/put! :current-page about-page))

;; -------------------------
;; Initialize app
(defn init! []
  (reagent/render-component [current-page] (.getElementById js/document "app")))

;; -------------------------
;; History
(defn hook-browser-navigation! []
  (let [history (doto (Html5History.)
                  (events/listen
                    EventType/NAVIGATE
                    (fn [event]
                      (secretary/dispatch! (.-token event))))
                  (.setUseFragment false)
                  (.setPathPrefix "")
                  (.setEnabled true))]

    (events/listen js/document "click"
                   (fn [e]
                     (. e preventDefault)
                     (let [path (.getPath (.parse Uri (.-href (.-target e))))
                           title (.-title (.-target e))]
                       (when path
                         (. history (setToken path title))))))))

;; need to run this after routes have been defined
(hook-browser-navigation!)
@Rodeoclash
Copy link

Thanks for this, it works wonderfully. It's great to be able to do an implementation then see how someone else has done it, makes you learn a few things :)

@Rodeoclash
Copy link

Hmm, a couple more questions on this.

This causes route changes when you click on any element, including the document (which will navigate to an empty href). You can get around that with something like:

(if (= (.-nodeName (.-target e)) "A") ...)

It doesn't handle <a> tags that have nested components. E.g. an <a><img src="..."></a> any thoughts on handling that? Perhaps it could inspect up the dom tree looking for parent <a> tags and use them?

@city41
Copy link
Author

city41 commented Jan 17, 2015

I'm not sure yet. I agree this gist is not ideal, it literally was "just got it working". I also don't like having a click handler on the entire document.

@aaronblenkush
Copy link

Thanks for this. I ended up using the following form for the body of the let in hook-browser-navigation! which will allow normal hyperlinks to work if they didn't match a secretary route.

(events/listen js/document "click"
               (fn [e]
                 (let [path (.getPath (.parse Uri (.-href (.-target e))))
                       title (.-title (.-target e))]
                   (when (secretary/locate-route path)
                     (. e preventDefault)
                     (. history (setToken path title))))))

@jdkealy
Copy link

jdkealy commented Aug 11, 2015

Hi,

Thanks for this! it works very well! Any idea why the app gets re-rendered upon clicking something / anything ?

@venantius
Copy link

For those of you looking for a highly consumable version of this, I'd recommend checking out a small lib I made for exactly this purpose - https://github.com/venantius/accountant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment