Clojurescript DOM接口

6

是否有一种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

等等


好的,刚刚发现了Domina - 最接近的东西。 - Hendekagon
1个回答

4
Clojure的数据结构都是持久化的,但在你的示例中,似乎想要产生副作用(即直接操作DOM来改变它)。
这是一种相当过程化/命令式的方法,因此可能值得退后一步,以更功能化的方式重新制定问题。 我的个人哲学是将“视图视为数据”,并使用Clojure的持久化数据结构对其进行建模,直到最后需要呈现为止。
你是否熟悉Hiccup? 其思想是使用简单的向量和映射表示HTML或SVG DOM:
[:div {:with "attribute"} "and" [:span "children"]]

您可以通过组合普通的Clojure函数来构建它。在Clojure中,您可以使用原始的Hiccup库将其呈现为HTML,但至少有两个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*)]])

(摘自C2 TodoMVC 实现) 类似“全选”复选框是否被选中的事项直接从底层数据(存储在原子中)派生。 每当数据发生变化时,模板将重新运行,dom 会自动更新。

基本思想是在应用程序数据到 Hiccup 数据结构的正向映射上建立,然后让库来处理DOM同步(添加 / 移除子元素、属性等)。 如果不必关心DOM的状态(已经添加过了吗?我需要切换一些类吗?),那么很多偶然的复杂性都会消失。


谢谢。我实际上是在考虑通过将DOM片段视为Clojure风格的持久结构(但实现为“不可变”的DOM片段),从而删除先前的DOM元素并将它们与新生成的元素交换。然而,现在我想想,这可能会很慢且浪费资源。我将尝试使用您的库...它可以处理SVG吗? - Hendekagon
嗨,我尝试了http://keminglabs.com/c2/上的条形图示例,但是出现了“未捕获的错误:TODO:返回元素样式映射”的提示。此外,需要一段时间才能弄清楚哪些内容需要:require、:use和:use-macros,因为大多数示例都是孤立的代码片段。不过看起来很有趣。关于数据到dom元素的映射,你是如何管理节点的标识?unify是如何知道哪些数据对应于dom中的哪些节点,以及是否要添加新节点? - Hendekagon
没错,它可以很好地处理SVG。Singult会自动为您命名常见的SVG元素(<g>,<rect>等),因此您不必再写例如[:svg:rect {:x 1}]这样的代码了。 - Kevin L.
默认情况下,节点映射使用索引,但您可以向unify提供一个可选的:key-fn参数,并给它一个函数f(datum, index)作为输入。该函数返回值将用于标识该节点。相关源代码在此处:https://github.com/lynaghk/singult/blob/master/src/coffee/Singult.coffee#L198 - Kevin L.
好的,我已经成功实现了使用(bind! (unify动态更新的简单折线图,而且代码比我之前手工操作DOM的add-or-update方法更加简洁。在Firefox中使用Clojurescript更新SVG非常缓慢(每秒只能承受1个更新)!Chrome可以处理每100ms的更新 - 这意味着接收JSON、转换为Clojure映射并将其映射到SVG图表。你有C2的群吗?在github页面或google群中找不到任何参考资料。 - Hendekagon
有一个非常新的谷歌小组:https://groups.google.com/forum/?hl=en&fromgroups#!forum/c2-cljs(我刚刚添加到C2自述文件中,感谢提醒)。至于您的速度问题,没有查看代码很难说。确保您使用最新版本的lein cljsbuild(现在是0.2.3),因为最近几个cljs编译器版本要快得多。此外,如果您没有使用高级编译,请尝试打开它-某些东西会快两到三倍。 - Kevin L.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接