       (let [attr-ns (namespace k)
             attr-name (name k)
             attr-str (if attr-ns (str attr-ns "/" attr-name) attr-name)]
-        (rebinder node attr-ns attr-name new-val node-state)
         (js-delete old-attrs attr-str)
-        (aset new-attrs-js attr-str new-val)))
+        (aset new-attrs-js attr-str
+              (rebinder node attr-ns attr-name new-val node-state))))
     (goog.object/forEach old-attrs
       (fn [_ attr-str _]
         (let [parts (.split attr-str "/")

          (if (.-substring prop-value)
            prop-value
            (.toString prop-value)))
    (js-delete (.-style elem) prop-name)))

(defn- remove-style-prop! [elem prop-name]
  (js-delete (.-style elem) prop-name))

          (if (.-substring attr-value) ;; tests if attr-value is a string
            attr-value
            (.toString attr-value)))
        (.removeAttribute element attr-name))
      attr-value)))

(defn- bind-lifecycle-callback! [node node-state cb-name cb-value]
  (when (identical? cb-name "on-disposed")
    (set! (.-disposed-callback node-state) cb-value)
    ;; other callbacks automatically included in attr map
    )
  cb-value)

(def ^:private attr-ns-lookup
  #js
  {:node bind-lifecycle-callback!
   :state (fn [_ _ _ v] v) ;; automatically handled in attr map
   })

(defn register-attr-prefix! [prefix xml-ns-or-handler]
