From a25f336d800cd64217279f1a4be4b3c6b6107314 Mon Sep 17 00:00:00 2001 From: Alex King Date: Tue, 9 Apr 2019 16:23:33 +0200 Subject: [PATCH 1/5] Ignore anchors with '_self' not 'self' --- modules/reitit-frontend/src/reitit/frontend/history.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/reitit-frontend/src/reitit/frontend/history.cljs b/modules/reitit-frontend/src/reitit/frontend/history.cljs index 35cbf52d..e1aba76d 100644 --- a/modules/reitit-frontend/src/reitit/frontend/history.cljs +++ b/modules/reitit-frontend/src/reitit/frontend/history.cljs @@ -92,7 +92,7 @@ (not (.-ctrlKey e)) (not (.-metaKey e)) (not (.-shiftKey e)) - (not (contains? #{"_blank" "self"} (.getAttribute el "target"))) + (not (contains? #{"_blank" "_self"} (.getAttribute el "target"))) ;; Left button (= 0 (.-button e)) ;; isContentEditable property is inherited from parents, From b50b44cced491a3e210d142d0d024eebf3a084c0 Mon Sep 17 00:00:00 2001 From: Alex King Date: Thu, 11 Apr 2019 22:08:03 +0200 Subject: [PATCH 2/5] Invert link 'target' check --- modules/reitit-frontend/src/reitit/frontend/history.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/reitit-frontend/src/reitit/frontend/history.cljs b/modules/reitit-frontend/src/reitit/frontend/history.cljs index e1aba76d..025141aa 100644 --- a/modules/reitit-frontend/src/reitit/frontend/history.cljs +++ b/modules/reitit-frontend/src/reitit/frontend/history.cljs @@ -92,7 +92,8 @@ (not (.-ctrlKey e)) (not (.-metaKey e)) (not (.-shiftKey e)) - (not (contains? #{"_blank" "_self"} (.getAttribute el "target"))) + (or (not (.hasAttribute el "target")) + (contains? #{"" "_self"} (.getAttribute el "target"))) ;; Left button (= 0 (.-button e)) ;; isContentEditable property is inherited from parents, From 5ffb9fc59f8b23dd7382032e43e72dd443f45114 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Mon, 15 Apr 2019 11:40:59 +0300 Subject: [PATCH 3/5] Add ignore-anchor-click function --- doc/frontend/browser.md | 23 +++++++++++++++++++ .../src/reitit/frontend/history.cljs | 20 ++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/doc/frontend/browser.md b/doc/frontend/browser.md index 08d3b4b1..edf4b5c1 100644 --- a/doc/frontend/browser.md +++ b/doc/frontend/browser.md @@ -18,6 +18,29 @@ request to the server. This means the URL will look normal, but the downside is that the server must respond to all routes with correct file (`index.html`). Check examples for simple Ring handler example. +### Anchor click handling + +HTML5 History router will handle click events on anchors where the href +matches the route tree (and other [rules](../../modules/reitit-frontend/src/reitit/frontend/history.cljs#L84-L98)). +If you have need to control this logic, for example to handle some +anchor clicks where the href matches route tree normally (i.e. browser load) +you can provide `:ignore-anchor-click` function to add your own logic to +event handling: + +```clj +(rfe/start! + router + {:use-fragment false + :ignore-anchor-click (fn [e el] + (not= "false" (gobj/get (.-dataset el) "reititHandleClick")))}) + +;; Use data-reitit-handle-click to disable Reitit anchor handling +[:a + {:href (rfe/href ::about) + :data-reitit-handle-click false} + "About"] +``` + ## Easy Reitit frontend routers require storing the state somewhere and passing it to diff --git a/modules/reitit-frontend/src/reitit/frontend/history.cljs b/modules/reitit-frontend/src/reitit/frontend/history.cljs index 025141aa..333c8c95 100644 --- a/modules/reitit-frontend/src/reitit/frontend/history.cljs +++ b/modules/reitit-frontend/src/reitit/frontend/history.cljs @@ -78,6 +78,9 @@ (if (exists? js/location) (.getDomain (.parse Uri js/location))) + ignore-anchor-click-fn (or (:ignore-anchor-click this) + (constantly true)) + ;; Prevent document load when clicking a elements, if the href points to URL that is part ;; of the routing tree." ignore-anchor-click @@ -99,6 +102,7 @@ ;; isContentEditable property is inherited from parents, ;; so if the anchor is inside contenteditable div, the property will be true. (not (.-isContentEditable el)) + (ignore-anchor-click-fn e el) (reitit/match-by-path router (.getPath uri))) (.preventDefault e) (let [path (str (.getPath uri) @@ -134,15 +138,23 @@ - on-navigate Function to be called when route changes. Takes two parameters, ´match´ and ´history´ object. Options: - - :use-fragment (default true) If true, onhashchange and location hash are used to store current route." + - :use-fragment (default true) If true, onhashchange and location hash are used to store current route. + + Options (Html5History): + - :ignore-anchor-click Function (event, anchor element) to check if Reitit + should handle click events on the anchor element. By default + hrefs matching the route tree are handled by Reitit." ([router on-navigate] (start! router on-navigate nil)) ([router on-navigate {:keys [use-fragment] - :or {use-fragment true}}] - (let [opts {:router router - :on-navigate on-navigate}] + :or {use-fragment true} + :as opts}] + (let [opts (-> opts + (dissoc :use-fragment) + (assoc :router router + :on-navigate on-navigate))] (-init (if use-fragment (map->FragmentHistory opts) (map->Html5History opts)))))) From 51c5aad4920821120eade68583fe3de0fa0d6de9 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Fri, 26 Apr 2019 15:10:17 +0300 Subject: [PATCH 4/5] Refactor ignore-anchor-click check to predicate function --- doc/frontend/browser.md | 6 +- .../src/reitit/frontend/history.cljs | 76 ++++++++++--------- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/doc/frontend/browser.md b/doc/frontend/browser.md index edf4b5c1..7217e9f5 100644 --- a/doc/frontend/browser.md +++ b/doc/frontend/browser.md @@ -31,8 +31,10 @@ event handling: (rfe/start! router {:use-fragment false - :ignore-anchor-click (fn [e el] - (not= "false" (gobj/get (.-dataset el) "reititHandleClick")))}) + :ignore-anchor-click? (fn [router e el uri] + ;; Add additional check on top of the default checks + (and (rfh/ignore-anchor-click? router e el uri) + (not= "false" (gobj/get (.-dataset el) "reititHandleClick"))))}) ;; Use data-reitit-handle-click to disable Reitit anchor handling [:a diff --git a/modules/reitit-frontend/src/reitit/frontend/history.cljs b/modules/reitit-frontend/src/reitit/frontend/history.cljs index 333c8c95..85eea8b6 100644 --- a/modules/reitit-frontend/src/reitit/frontend/history.cljs +++ b/modules/reitit-frontend/src/reitit/frontend/history.cljs @@ -67,6 +67,29 @@ (first (.composedPath original-event)) (.-target event)))) +(defn ignore-anchor-click? + "Precicate to check if the anchor click event default action + should be ignored. This logic will ignore the event + if anchor href matches the route tree, and in this case + the page location is updated using History API." + [router e el uri] + (let [current-domain (if (exists? js/location) + (.getDomain (.parse Uri js/location)))] + (and (or (and (not (.hasScheme uri)) (not (.hasDomain uri))) + (= current-domain (.getDomain uri))) + (not (.-altKey e)) + (not (.-ctrlKey e)) + (not (.-metaKey e)) + (not (.-shiftKey e)) + (or (not (.hasAttribute el "target")) + (contains? #{"" "_self"} (.getAttribute el "target"))) + ;; Left button + (= 0 (.-button e)) + ;; isContentEditable property is inherited from parents, + ;; so if the anchor is inside contenteditable div, the property will be true. + (not (.-isContentEditable el)) + (reitit/match-by-path router (.getPath uri))))) + (defrecord Html5History [on-navigate router listen-key click-listen-key] History (-init [this] @@ -74,42 +97,22 @@ (fn [e] (-on-navigate this (-get-path this))) - current-domain - (if (exists? js/location) - (.getDomain (.parse Uri js/location))) - - ignore-anchor-click-fn (or (:ignore-anchor-click this) - (constantly true)) + ignore-anchor-click-predicate (or (:ignore-anchor-click? this) + ignore-anchor-click?) ;; Prevent document load when clicking a elements, if the href points to URL that is part ;; of the routing tree." - ignore-anchor-click - (fn ignore-anchor-click - [e] - ;; Returns the next matching anchestor of event target - (when-let [el (closest-by-tag (event-target e) "a")] - (let [uri (.parse Uri (.-href el))] - (when (and (or (and (not (.hasScheme uri)) (not (.hasDomain uri))) - (= current-domain (.getDomain uri))) - (not (.-altKey e)) - (not (.-ctrlKey e)) - (not (.-metaKey e)) - (not (.-shiftKey e)) - (or (not (.hasAttribute el "target")) - (contains? #{"" "_self"} (.getAttribute el "target"))) - ;; Left button - (= 0 (.-button e)) - ;; isContentEditable property is inherited from parents, - ;; so if the anchor is inside contenteditable div, the property will be true. - (not (.-isContentEditable el)) - (ignore-anchor-click-fn e el) - (reitit/match-by-path router (.getPath uri))) - (.preventDefault e) - (let [path (str (.getPath uri) - (if (seq (.getQuery uri)) - (str "?" (.getQuery uri))))] - (.pushState js/window.history nil "" path) - (-on-navigate this path))))))] + ignore-anchor-click (fn [e] + ;; Returns the next matching anchestor of event target + (when-let [el (closest-by-tag (event-target e) "a")] + (let [uri (.parse Uri (.-href el))] + (when (ignore-anchor-click-predicate router e el uri) + (.preventDefault e) + (let [path (str (.getPath uri) + (if (seq (.getQuery uri)) + (str "?" (.getQuery uri))))] + (.pushState js/window.history nil "" path) + (-on-navigate this path))))))] (-on-navigate this (-get-path this)) (assoc this :listen-key (gevents/listen js/window goog.events.EventType.POPSTATE handler false) @@ -141,9 +144,10 @@ - :use-fragment (default true) If true, onhashchange and location hash are used to store current route. Options (Html5History): - - :ignore-anchor-click Function (event, anchor element) to check if Reitit - should handle click events on the anchor element. By default - hrefs matching the route tree are handled by Reitit." + - :ignore-anchor-click? Function (router, event, anchor element, uri) which will be called to + check if the anchor click event should be ignored. + To extend built-in check, you can call `reitit.frontend.history/ignore-anchor-click?` + function, which will ignore clicks if the href matches route tree." ([router on-navigate] (start! router on-navigate nil)) ([router From f8f44b9ea6e7a56723886cde219dbc8e84b8f6e1 Mon Sep 17 00:00:00 2001 From: Miikka Koskinen Date: Thu, 2 May 2019 11:52:13 +0300 Subject: [PATCH 5/5] Update doc/frontend/browser.md Co-Authored-By: Deraen --- doc/frontend/browser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/frontend/browser.md b/doc/frontend/browser.md index 7217e9f5..0d1d8b0d 100644 --- a/doc/frontend/browser.md +++ b/doc/frontend/browser.md @@ -24,7 +24,7 @@ HTML5 History router will handle click events on anchors where the href matches the route tree (and other [rules](../../modules/reitit-frontend/src/reitit/frontend/history.cljs#L84-L98)). If you have need to control this logic, for example to handle some anchor clicks where the href matches route tree normally (i.e. browser load) -you can provide `:ignore-anchor-click` function to add your own logic to +you can provide `:ignore-anchor-click?` function to add your own logic to event handling: ```clj