Skip to content

Commit

Permalink
Fix reagent-project#79 - position cursor after resetting an input's v…
Browse files Browse the repository at this point in the history
…alue
  • Loading branch information
mike-thompson-day8 committed May 8, 2015
1 parent 10d54ea commit f2342b3
Showing 1 changed file with 38 additions and 4 deletions.
42 changes: 38 additions & 4 deletions src/reagent/impl/template.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,46 @@
(defn input-unmount [this]
(.! this :cljsInputValue nil))

;; <input type="??" >
(def these-inputs-have-a-cursor #{"text" "password" "email" "number" "search" "tel" "url"})

(defn has-cursor?
[input-type]
(contains? these-inputs-have-a-cursor input-type))

(defn input-set-value [this]
(when-some [value (.' this :cljsInputValue)]
(.! this :cljsInputDirty false)
(let [node (.' this getDOMNode)]
(when (not= value (.' node :value))
(.! node :value value)))))
(.! this :cljsInputDirty false)
(let [node (.' this getDOMNode)
node-value (.' node :value)]
(when (not= value node-value)
(if-not (has-cursor? (.' node :type))
; just set the value, no need to worry about a cursor
(.! node :value value)

;; Setting "value" (below) moves the cursor position to the end which
;; gives the user a jarring experience.
;;
;; But repositioning the cursor within the text, turns out
;; to be quite a challenge because changes in the text can be
;; triggered by various events like:
;; - a validation function rejecting a certain user inputted char
;; - the user enters a lower case char, but is transformed to upper.
;; - the user selects multiple chars and deletes text
;; - the user pastes in multiple chars, and some of them are rejected
;; by a validator.
;; - the user selects multiple chars and then types in a single
;; new char to repalce them all.
;; Coming up with a sane cursor repositioning strategy hasn't been easy
;; ALTHOUGH in the end, it kinda fell out nicely, and it appears to sanely
;; handle all the cases we could think of.
;; So this is just a warning. The code below is simple enough, but if
;; you are tempted to change it, be aware of all the scenarios you have handle.
(let [existing-offset-from-end (- (count node-value) (.' node :selectionStart))
new-cursor-offset (- (count value) existing-offset-from-end)]
(.! node :value value)
(.! node :selectionStart new-cursor-offset)
(.! node :selectionEnd new-cursor-offset)))))))

(defn input-handle-change [this on-change e]
(let [res (on-change e)]
Expand Down

0 comments on commit f2342b3

Please sign in to comment.