在 ClojureScript 中,延迟 `:on-click` 事件并仅在 `:on-double-click` 事件未触发时触发
In ClojureScript, delay `:on-click` event and trigger only if `:on-double-click` event is not triggered
延迟 :on-click
事件以首先查看 :on-double-click
事件是否触发的简单方法是什么?
[:div {:on-click (fn [e]
;; listen to double-click event, within 500ms,
;; if so on-double-click-fn,
;; if not, on-click-fn
)
:on-double-click (fn [e]
;; on-click-fn
)}]
谢谢!
第一次尝试:
(defn sleep [timeout]
(let [maxtime (+ (.getTime (js/Date.)) timeout)]
(while (< (.getTime (js/Date.)) maxtime))))
[:div {:on-click (fn [e] (sleep 500) (print "single-clicked"))
:on-double-click (fn [e] (print "double-clicked"))}]
第二次尝试:
(def state (atom {:click-count 0}))
(defn handle-click [e click-fns-map]
(swap! state update :click-count inc)
(sleep 500)
(let [click-count (get @state :click-count)]
(swap! state assoc :click-count 0)
(cond
(= click-count 1) ((:on-single-click click-fns-map) e)
(> click-count 1) ((:on-double-click click-fns-map) e)))))
[:div
{:on-mouse-down
(fn [e]
(handle-click e {:on-single-click #(print "single-click")
:on-double-click #(print "double-click")}))}]
;;=> "single-click"
;;=> "single-click"
编辑:
基于,这是一个抽象,它包装了html 元素args 并为您覆盖了:on-click
和:on-double-click
。
(defn ensure-single-double-click
[{:keys [on-click on-double-click] :as args}]
(let [waiting? (atom false)]
(merge
args
{:on-click (fn [e]
(when (compare-and-set! waiting? false true)
(js/setTimeout
(fn [] (when @waiting?
(on-click %)
(reset! waiting? false)))
300)))
:on-double-click (fn [e]
(reset! waiting? false)
(on-double-click %))})))
[:a (ensure-single-double-click
{:style {:color "blue"} ;; this works
:on-click #(print "single-click")
:on-double-click #(print "double-click")})
"test"]
这是一种方法:
(defn slow-link [text single-click-fn double-click-fn]
(let [waiting? (atom false)]
[:a {:on-click #(when (compare-and-set! waiting? false true)
(js/setTimeout (fn [] (when @waiting?
(single-click-fn %)
(reset! waiting? false)))
500))
:on-double-click #(do (reset! waiting? false)
(double-click-fn %))}
text]))
[slow-link "Test" #(prn "single-click") #(prn "double-click")]
这将启动一个 JS 计时器,它将在 500 毫秒后执行给定的功能。该函数会在超时时间结束后检查我们是否仍在 waiting?
进行另一次点击,如果是,则执行 single-click-fn
。如果我们不是 waiting?
,则意味着 double-click 事件已经发生,将 waiting?
重置为 false
,并调用 double-click-fn
.
:on-click
处理程序使用 compare-and-set
仅在我们尚未处于 waiting?
状态时才采取行动,避免 triple/quadruple 点击等的一些不正当行为.
接近,但 compare-and-set!
并没有保护我免受三次点击(或更多点击!),因为如果说,三次点击发生在 500 毫秒内,waiting?
第三次将再次设置为 false
并安排第二次超时。我认为这意味着在技术上,每次奇数点击都会安排一个新的超时。
幸运的是,点击事件带有一个名为 detail
, which is set to the number of consecutive clicks. I found it here 的 属性。以下内容应该可以解决 OP 的问题,而无需三次点击:
:on-click
(fn [e]
; This prevents the click handler from running
; a second, third, or other time.
(when (-> e .-detail (= 1))
(reset! waiting? true))
; Wait an appropriate time for the double click
; to happen...
(js/setTimeout
(fn []
; If we are still waiting for the double click
; to happen, it didn't happen!
(when @waiting?
(single-click-fn %)
(reset! waiting? false)))
500)))
:on-double-click #(do (reset! waiting? false)
(double-click-fn %))
虽然三次点击听起来很奇怪,但它们确实有一个目的:select 整行文本,所以我不希望用户错过这种能力。
其余部分是对那些有兴趣使文本 selection 在监听单击的元素上工作的人的附录。我从 Google 来到这里寻找如何做到这一点,所以也许它对某人有帮助。
我 运行 遇到的一个挑战是我的应用程序规定用户可以执行双击 而无需首先释放第二次单击 ;有点像"one-and-a-half-clicks"。为什么?因为我正在监听跨度上的点击,用户可能会双击 select 整个单词,但随后按住第二次单击并拖动鼠标以 select 其他单词在原来的旁边。问题是双击事件处理程序仅在用户 释放 第二次单击后触发,因此 waiting?
未按时设置为 false
。
我使用 :on-mouse-down
处理程序解决了这个问题:
:on-click
(fn [e]
; This prevents the click handler from running
; a second, third, or other time.
(when (-> e .-detail (= 1))
(reset! waiting? true))
; Wait an appropriate time for the double click
; to happen...
(js/setTimeout
(fn []
; If we are still waiting for the double click
; to happen, it didn't happen!
(when @waiting?
(single-click-fn %)
(reset! waiting? false)))
500)))
:on-double-click #(double-click-fn %)
:on-mouse-down #(reset! waiting? false)
请记住,:on-click
和 :on-double-click
处理程序仅在 释放 时触发(并且处理程序按照按下鼠标、单击、双击),这让 :on-mouse-down
处理程序有机会将 waiting?
设置为 false
,如果用户尚未释放鼠标,则需要这样做,因为他不会触发了 :on-double-click
事件处理程序。
请注意,现在您甚至不需要在双击处理程序中将 waiting?
设置为 false,因为在双击处理程序 [=71 时鼠标按下处理程序已经完成了该操作=].
最后,在我的特定应用程序中,碰巧用户可能想要 select 文本而不触发点击处理程序。为此,他将单击一段文本,然后在不松开鼠标的情况下将光标拖动到 select 更多文本。释放光标时,不应触发点击事件。因此,我必须另外跟踪用户是否在放开鼠标之前的任何时候进行了 selection(一种 "half-click")。对于这种情况,我必须向组件的状态添加更多内容(一个名为 selection-made?
的布尔原子和一个名为 selection-handler
的事件处理函数)。这种情况依赖于 selection 的检测,并且由于 selection 是在双击时生成的,因此不再需要检查事件的详细信息 属性 以防止三次或更多次点击.
整个解决方案如下所示(但请记住,这是专门针对文本元素的,因此只是对 OP 要求的内容的补充):
(defn component
[]
(let [waiting? (r/atom false)
selection-made? (r/atom false)
selection-handler
(fn []
(println "selection-handler running")
(when (seq (.. js/document getSelection toString))
(reset! selection-made? true)))]
(fn []
[:div
; For debugging
[:pre {} "waiting? " (str @waiting?)]
[:pre {} "selection-made? " (str @selection-made?)]
; Your clickable element
[:span
{:on-click
(fn [e]
(println "click handler triggered")
; Remove the selection handler in any case because
; there is a small chance that the selection handler
; was triggered without selecting any text (by
; holding down the mouse on the text for a little
; while without moving it).
(.removeEventListener js/document "selectionchange" selection-handler)
(if @selection-made?
; If a selection was made, only perform cleanup.
(reset! selection-made? false)
; If no selection was made, treat it as a
; simple click for now...
(do
(reset! waiting? true)
; Wait an appropriate amount of time for the
; double click to happen...
(js/setTimeout
(fn []
; If we are still waiting for the double click
; to happen, it didn't happen! The mouse-down
; handler would have set waiting? to false
; by now if it had been clicked a second time.
; (Remember that the mouse down handler runs
; before the click handler since the click handler
; runs only once the mouse is released.
(when @waiting?
(single-click-fn e)
(reset! waiting? false)))
500))))
:on-mouse-down
(fn [e]
; Set this for the click handler in case a double
; click is happening.
(reset! waiting? false)
; Only run this if it is a left click, or the event
; listener is not removed until a single click on this
; segment is performed again, and will listen on
; every click everywhere in the window.
(when (-> e .-button zero?)
(js/console.log "mouse down handler running")
(.addEventListener js/document "selectionchange" selection-handler)))
:on-double-click #(double-click-fn %)}
some content here]])))
延迟 :on-click
事件以首先查看 :on-double-click
事件是否触发的简单方法是什么?
[:div {:on-click (fn [e]
;; listen to double-click event, within 500ms,
;; if so on-double-click-fn,
;; if not, on-click-fn
)
:on-double-click (fn [e]
;; on-click-fn
)}]
谢谢!
第一次尝试:
(defn sleep [timeout]
(let [maxtime (+ (.getTime (js/Date.)) timeout)]
(while (< (.getTime (js/Date.)) maxtime))))
[:div {:on-click (fn [e] (sleep 500) (print "single-clicked"))
:on-double-click (fn [e] (print "double-clicked"))}]
第二次尝试:
(def state (atom {:click-count 0}))
(defn handle-click [e click-fns-map]
(swap! state update :click-count inc)
(sleep 500)
(let [click-count (get @state :click-count)]
(swap! state assoc :click-count 0)
(cond
(= click-count 1) ((:on-single-click click-fns-map) e)
(> click-count 1) ((:on-double-click click-fns-map) e)))))
[:div
{:on-mouse-down
(fn [e]
(handle-click e {:on-single-click #(print "single-click")
:on-double-click #(print "double-click")}))}]
;;=> "single-click"
;;=> "single-click"
编辑:
基于:on-click
和:on-double-click
。
(defn ensure-single-double-click
[{:keys [on-click on-double-click] :as args}]
(let [waiting? (atom false)]
(merge
args
{:on-click (fn [e]
(when (compare-and-set! waiting? false true)
(js/setTimeout
(fn [] (when @waiting?
(on-click %)
(reset! waiting? false)))
300)))
:on-double-click (fn [e]
(reset! waiting? false)
(on-double-click %))})))
[:a (ensure-single-double-click
{:style {:color "blue"} ;; this works
:on-click #(print "single-click")
:on-double-click #(print "double-click")})
"test"]
这是一种方法:
(defn slow-link [text single-click-fn double-click-fn]
(let [waiting? (atom false)]
[:a {:on-click #(when (compare-and-set! waiting? false true)
(js/setTimeout (fn [] (when @waiting?
(single-click-fn %)
(reset! waiting? false)))
500))
:on-double-click #(do (reset! waiting? false)
(double-click-fn %))}
text]))
[slow-link "Test" #(prn "single-click") #(prn "double-click")]
这将启动一个 JS 计时器,它将在 500 毫秒后执行给定的功能。该函数会在超时时间结束后检查我们是否仍在 waiting?
进行另一次点击,如果是,则执行 single-click-fn
。如果我们不是 waiting?
,则意味着 double-click 事件已经发生,将 waiting?
重置为 false
,并调用 double-click-fn
.
:on-click
处理程序使用 compare-and-set
仅在我们尚未处于 waiting?
状态时才采取行动,避免 triple/quadruple 点击等的一些不正当行为.
compare-and-set!
并没有保护我免受三次点击(或更多点击!),因为如果说,三次点击发生在 500 毫秒内,waiting?
第三次将再次设置为 false
并安排第二次超时。我认为这意味着在技术上,每次奇数点击都会安排一个新的超时。
幸运的是,点击事件带有一个名为 detail
, which is set to the number of consecutive clicks. I found it here 的 属性。以下内容应该可以解决 OP 的问题,而无需三次点击:
:on-click
(fn [e]
; This prevents the click handler from running
; a second, third, or other time.
(when (-> e .-detail (= 1))
(reset! waiting? true))
; Wait an appropriate time for the double click
; to happen...
(js/setTimeout
(fn []
; If we are still waiting for the double click
; to happen, it didn't happen!
(when @waiting?
(single-click-fn %)
(reset! waiting? false)))
500)))
:on-double-click #(do (reset! waiting? false)
(double-click-fn %))
虽然三次点击听起来很奇怪,但它们确实有一个目的:select 整行文本,所以我不希望用户错过这种能力。
其余部分是对那些有兴趣使文本 selection 在监听单击的元素上工作的人的附录。我从 Google 来到这里寻找如何做到这一点,所以也许它对某人有帮助。
我 运行 遇到的一个挑战是我的应用程序规定用户可以执行双击 而无需首先释放第二次单击 ;有点像"one-and-a-half-clicks"。为什么?因为我正在监听跨度上的点击,用户可能会双击 select 整个单词,但随后按住第二次单击并拖动鼠标以 select 其他单词在原来的旁边。问题是双击事件处理程序仅在用户 释放 第二次单击后触发,因此 waiting?
未按时设置为 false
。
我使用 :on-mouse-down
处理程序解决了这个问题:
:on-click
(fn [e]
; This prevents the click handler from running
; a second, third, or other time.
(when (-> e .-detail (= 1))
(reset! waiting? true))
; Wait an appropriate time for the double click
; to happen...
(js/setTimeout
(fn []
; If we are still waiting for the double click
; to happen, it didn't happen!
(when @waiting?
(single-click-fn %)
(reset! waiting? false)))
500)))
:on-double-click #(double-click-fn %)
:on-mouse-down #(reset! waiting? false)
请记住,:on-click
和 :on-double-click
处理程序仅在 释放 时触发(并且处理程序按照按下鼠标、单击、双击),这让 :on-mouse-down
处理程序有机会将 waiting?
设置为 false
,如果用户尚未释放鼠标,则需要这样做,因为他不会触发了 :on-double-click
事件处理程序。
请注意,现在您甚至不需要在双击处理程序中将 waiting?
设置为 false,因为在双击处理程序 [=71 时鼠标按下处理程序已经完成了该操作=].
最后,在我的特定应用程序中,碰巧用户可能想要 select 文本而不触发点击处理程序。为此,他将单击一段文本,然后在不松开鼠标的情况下将光标拖动到 select 更多文本。释放光标时,不应触发点击事件。因此,我必须另外跟踪用户是否在放开鼠标之前的任何时候进行了 selection(一种 "half-click")。对于这种情况,我必须向组件的状态添加更多内容(一个名为 selection-made?
的布尔原子和一个名为 selection-handler
的事件处理函数)。这种情况依赖于 selection 的检测,并且由于 selection 是在双击时生成的,因此不再需要检查事件的详细信息 属性 以防止三次或更多次点击.
整个解决方案如下所示(但请记住,这是专门针对文本元素的,因此只是对 OP 要求的内容的补充):
(defn component
[]
(let [waiting? (r/atom false)
selection-made? (r/atom false)
selection-handler
(fn []
(println "selection-handler running")
(when (seq (.. js/document getSelection toString))
(reset! selection-made? true)))]
(fn []
[:div
; For debugging
[:pre {} "waiting? " (str @waiting?)]
[:pre {} "selection-made? " (str @selection-made?)]
; Your clickable element
[:span
{:on-click
(fn [e]
(println "click handler triggered")
; Remove the selection handler in any case because
; there is a small chance that the selection handler
; was triggered without selecting any text (by
; holding down the mouse on the text for a little
; while without moving it).
(.removeEventListener js/document "selectionchange" selection-handler)
(if @selection-made?
; If a selection was made, only perform cleanup.
(reset! selection-made? false)
; If no selection was made, treat it as a
; simple click for now...
(do
(reset! waiting? true)
; Wait an appropriate amount of time for the
; double click to happen...
(js/setTimeout
(fn []
; If we are still waiting for the double click
; to happen, it didn't happen! The mouse-down
; handler would have set waiting? to false
; by now if it had been clicked a second time.
; (Remember that the mouse down handler runs
; before the click handler since the click handler
; runs only once the mouse is released.
(when @waiting?
(single-click-fn e)
(reset! waiting? false)))
500))))
:on-mouse-down
(fn [e]
; Set this for the click handler in case a double
; click is happening.
(reset! waiting? false)
; Only run this if it is a left click, or the event
; listener is not removed until a single click on this
; segment is performed again, and will listen on
; every click everywhere in the window.
(when (-> e .-button zero?)
(js/console.log "mouse down handler running")
(.addEventListener js/document "selectionchange" selection-handler)))
:on-double-click #(double-click-fn %)}
some content here]])))