延迟 :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"

编辑:

基于 Taylor Wood's answer ,这里有一个抽象,它包装了 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"]

最佳答案

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

幸运的是,点击事件带有一个名为 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 %))

尽管三次点击听起来很奇特,但它们确实有一个目的:选择整行文本,所以我不希望用户错过这种能力。

其余部分是对那些有兴趣在监听单击的元素上进行文本选择的人的附录。我从谷歌来到这里寻找如何做到这一点,所以也许它可以帮助某人。

我遇到的一个挑战是,我的应用程序规定用户可以执行双击而无需首先释放第二次单击;有点像“一次半点击”。为什么?因为我正在监听跨度上的点击,并且用户很可能执行双击以选择整个单词,但然后按住第二次点击并拖动鼠标以选择原始单词旁边的其他单词。问题是双击事件处理程序仅在用户释放第二次点击后才触发,因此 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 了,因为在双击处理程序运行时,鼠标按下处理程序已经这样做了。

最后,在我的特定应用程序中,碰巧用户可能希望在不触发点击处理程序的情况下选择文本。为此,他将单击一段文本,然后在不释放鼠标的情况下拖动光标以选择更多文本。当光标被释放时,不应触发点击事件。因此,我必须在用户松开鼠标(一种“半点击”)之前的任何时间额外跟踪用户是否进行了选择。对于这种情况,我必须向组件的状态添加更多内容(名为 selection-made? 的 bool 原子和名为 selection-handler 的事件处理函数)。这种情况依赖于选择的检测,并且由于选择是在双击时进行的,因此不再需要检查事件的详细信息属性来防止三次或多次点击。

整个解决方案如下所示(但请记住,这是专门针对文本元素的,因此只是对 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]])))

关于clojure - 在 ClojureScript 中,延迟 `:on-click` 事件并仅在 `:on-double-click` 事件未触发时触发,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48969496/

10-10 01:04
查看更多