This is a variation of the function found in https://wiki.fennel-lang.org/html, but the function returns a DOM node instead of a string. It's useful if you're running Fennel on the browser like on Fengari.
Before anything else, this assumes that we have a JS interop library that we're able to use. To make things easier, we're also creating a reference to the js.global.document
object so we don't have to write the whole thing out:
(local {:global {: document} &as js} (require :js))
The function html
will be accepting a table and return a DOM node that we can use on the frontend. It accepts a sequential table like the following:
(html [:p {:class "message"} "Hello world"])
document.createElement
.document.createAttribute
.The third item can be another sequential table. This makes it easy for us to create a node tree:
(html [:div {}
[:p {:class "message"} "Hello world"]])
Additionally, the function is able to attach arbitrary event listeners to the elements as well using the key :on
:
(html
[:input {:type "button"
:value "Click me!"
:on {:click (fn [] (js.global.console:log "Clicked!"))}}])
Here is the function in full. Note that it handles svg
nodes as well which need to be created using document.createElementNS
so if that's unnecessary, you can remove that.
(lambda html [doc ?ns]
(let [[element attrs & children] doc
; If we run into an SVG, every child should use document.createElementNS instead of document.createElement
?ns (if (= element :svg) "http://www.w3.org/2000/svg" ?ns)
createElement (if ?ns
(fn [el] (document:createElementNS ?ns el))
(fn [el] (document:createElement el)))
node (createElement element)]
; Create the attributes of a node
(each [k v (pairs attrs)]
(if (= k :on)
; Add event listeners if we find them
(each [event func (pairs v)]
(node:addEventListener event func))
; Otherwise, just add it as a value to the attribute
; Similar to createElementNS, if the node is an SVG or a child of an SVG
; attributes are set using document.createAttributeNS (even though the namespaceURI is null somehow)
(let [attr (if ?ns (document:createAttributeNS js.null k)
(document:createAttribute k))]
(set attr.value v)
(node:setAttributeNode attr))))
; Create and attach children nodes to the parent node
(each [_ child (ipairs children)]
(if (= (type child) :string)
(node:appendChild (document:createTextNode child))
child
(node:appendChild (html child ?ns))))
node))
Ths function originally appears in this code repository.