在ClojureScript中,延迟`:on-click`事件并仅在未触发`:on-double-click`事件时触发

时间:2018-02-25 01:51:17

标签: clojure clojurescript reagent

如果:on-click事件被触发,有什么简单的方法可以推迟:on-double-click事件首先查看?

[:div {:on-click "listen to double-click event, within 500ms, 
             if so on-double-click-fn, 
             if not, on-click-fn"
       :on-double-click "on-click-fn"}]

谢谢!

首次尝试:

(defn sleep [timeout]
  (let [maxtime (+ (.getTime (js/Date.)) timeout)]
    (while (< (.getTime (js/Date.)) maxtime))))

[:div {:on-click #(do (sleep 500) (print "single-clicked"))
       :on-double-click #(print "double-clicked")}]

第二次尝试:

(def state (atom {:click-count 0}))

(defn handle-click [e click-fns-map]
  (do (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 #(when (compare-and-set! waiting? false true)
                   (js/setTimeout (fn [] (when @waiting?
                                           (on-click %)
                                           (reset! waiting? false)))
                                 300))
      :on-double-click #(do (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"]

2 个答案:

答案 0 :(得分:6)

以下是一种方法:

(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计时器,它将在500ms后执行给定的函数。当超时超时时,该函数会检查我们是否仍然在另一次点击时waiting?,如果是,则执行single-click-fn。如果我们不是waiting?这意味着双击事件已经发生,请将waiting?重置为false,然后调用double-click-fn

:on-click处理程序使用compare-and-set仅在我们尚未处于waiting?状态时才采取措施,避免了三重/四重点击的某些行为,等等。 / p>

答案 1 :(得分:1)

Taylor Wood's answer接近了,但是compare-and-set!并没有保护我免受三次点击(甚至更多点击!),因为如果说在500毫秒内发生了三次点击,waiting?将大约在第三次设置为false并计划第二次超时。我认为,这意味着从技术上来说,每一次奇数点击都会安排一个新的超时时间。

幸运的是,click事件带有一个名为detail的属性,该属性设置为连续单击的次数。我找到了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 %))

三次听起来可能充满异国情调,但它们的确有一个目的:选择整行文本,所以我不希望用户错过该功能。


其余的内容是那些对使文本选择适用于正在监听单击的元素的人的附录。我从Google来到这里寻找方法,所以它可能对某人有帮助。

我遇到的一个挑战是我的应用程序要求用户可以执行双击而不必先释放第二个点击。有点像“一键半点击”。为什么?因为我正在监听跨度上的点击,并且用户可以双击执行以选择整个单词,但是随后按住第二下并拖动鼠标以选择原始单词旁边的其他单词。问题在于,双击事件处理程序仅在用户释放第二次单击后才触发,因此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的机会,如果用户尚未释放鼠标,则因为用户尚未触发{{1 }}事件处理程序。

请注意,现在您甚至不需要在双击处理程序中将:on-double-click设置为false,因为在运行双击处理程序时鼠标按下处理程序已经做到了。

最后,在我的特定应用程序中,碰巧用户可能希望选择文本而不触发点击处理程序。为此,他将单击一段文本,然后在不释放鼠标的情况下拖动光标以选择更多文本。释放光标时,这不应触发click事件。因此,我还必须跟踪用户在放开鼠标之前是否曾做出选择(某种“半按”)。对于这种情况,我必须在组件的状态中添加更多内容(一个名为waiting?的布尔原子和一个名为selection-made?的事件处理函数)。这种情况依赖于检测到的选择,并且由于选择是双击完成的,因此不再需要检查事件的detail属性来防止三次或多次点击。

整个解决方案如下所示(但请记住,这是专门针对文本元素的,因此仅是OP要求的补充内容)

selection-handler