Clojurescript DOM界面

Hen*_*gon 6 dom clojure clojurescript

是否有一个Clojurescript库使DOM看起来像Clojure数据结构?我发现像Enfocus这样的一些库可以进行某些类型的DOM操作,但我想要的是能够像这样处理DOM:

(get dom id) - returns element called id in dom
(get dom id create-fn) - return element if exists, otherwise creates it
(update-in dom [:body this that] update-fn) - set attribute on a DOM element 
(assoc parent-element id child-element) - associate child element with parent
(conj parent child) - append child element to parent element
Run Code Online (Sandbox Code Playgroud)

等等

Kev*_* L. 4

Clojure 数据结构都是持久的,但在您的示例中,您似乎想要产生副作用(即,点击 DOM 来更改它)。

这是一种非常程序化/命令式的方法,因此可能值得退后一步,以更实用的方式重新表述问题。我个人的理念是将“视图视为数据”,并使用 Clojure 的持久数据结构对其进行建模,直到我需要渲染的最后一刻。

你熟悉希卡普吗?这个想法是使用简单的向量和地图来表示 HTML 或 SVG DOM:

[:div {:with "attribute"} "and" [:span "children"]]
Run Code Online (Sandbox Code Playgroud)

您可以通过组合普通的旧 Clojure 函数来构造它。在 Clojure 中,您可以将其渲染为 HTML(使用原始的 Hiccup 库),但至少有两个 ClojureScript 库可以直接渲染为(可能存在的)DOM 结构。 Crate是 Hiccup 的紧密移植版,而Singult有一些额外的语义,例如受 D3.js 启发的数据绑定(Singult 实际上是用 CoffeeScript 编写的,因此可以从纯 JavaScript 中使用,并且比 Crate 更快)。

我的C2库在 Singult 之上构建数据绑定语义,以保持 DOM 与底层数据同步。考虑这个 TODO 列表模板:

(bind! "#main"
        [:section#main {:style {:display (when (zero? (core/todo-count)) "none")}}
        [:input#toggle-all {:type "checkbox"
                            :properties {:checked (every? :completed? @core/!todos)}}]
        [:label {:for "toggle-all"} "Mark all as complete"]
        [:ul#todo-list (unify (case @core/!filter
                                :active    (remove :completed? @core/!todos)
                                :completed (filter :completed? @core/!todos)
                                ;;default to showing all events
                                @core/!todos)
                              todo*)]])
Run Code Online (Sandbox Code Playgroud)

(取自C2 TodoMVC 实现)。像是否选中“全部选中”框之类的事情是直接从底层数据(存储在原子中)得出的。每当数据发生变化时,模板就会重新运行,并且 dom 会自动更新。

基本思想是构建从应用程序数据到 Hiccup 数据结构的正向映射,然后让库负责同步 DOM(添加/删除子项、属性等)。如果您不必关心 DOM 的状态(这已经被添加了吗?我需要切换一些类吗?),那么很多附带的复杂性就会消失。