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