A
common problem
people have when using HTML5 pushState is that previous scroll
positions are not always restored when navigating back. A strategy to address
this is to store the current scroll position in history.state
before navigating
forward.
Let’s see how you might use the Push State API directly to accomplish this:
1
2
3
4
5
6
7
8
9
10
( declare scroll-top ) ;; returns current scroll position
( defn set-scroll-top! []
( let [ state ( or ( .-state js/history ) # js {})]
( aset state "scroll-top" ( scroll-top ))
( .replaceState js/history state )))
( defn get-scroll-top []
( when-let [ state ( .-state js/history )]
( aget state "scroll-top" )))
This works, but what if we could interface with history.state
as if it were
Clojure data?
More specifically, stateful Clojure data, like an atom
.
It might look something like this:
1
2
3
4
5
6
7
8
9
( require 'history )
( declare scroll-top )
( defn set-scroll-top! []
( swap! history/state assoc :scroll-top ( scroll-top )))
( defn get-scroll-top []
( :scroll-top @ history/state ))
And in fact, this can be done by anonymously implementing a few protocols.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
( ns history )
( def state
( let [ clj-state # ( js->clj ( .-state js/history ) :keywordize-keys true )]
( reify
IDeref
( -deref [ _ ]
( clj-state ))
IReset
( -reset! [ _ v ]
( .replaceState js/history ( clj->js v ) ( .-title js/document )))
ISwap
( -swap! [ s f ]
( -reset! s ( f ( clj-state ))))
( -swap! [ s f x ]
( -reset! s ( f ( clj-state ) x )))
( -swap! [ s f x y ]
( -reset! s ( f ( clj-state ) x y )))
( -swap! [ s f x y more ]
( -reset! s ( apply f ( clj-state ) x y more ))))))
You can checkout a more complete wrapper for js/history
here: https://gist.github.com/loganlinn/930c043331c52cb73a98