JavaScript
Weave provides JavaScript interoperability through Squint, a lightweight Clojure-to-JavaScript transpiler. This allows you to write Clojure code that gets converted to JavaScript for client-side execution.
How JavaScript Support Works
Weave uses Squint to:
- Transpile Clojure code to JavaScript at runtime
- Execute the generated JavaScript in the browser
- Integrate with the DOM and browser APIs
This approach gives you the expressiveness of Clojure with direct access to browser capabilities.
Using JavaScript in Weave
There are several ways to use JavaScript in Weave:
1. Inline Scripts with push-script!
The simplest way to execute JavaScript is with push-script!
:
This transpiles the Clojure code to JavaScript and sends it to the current browser tab for execution.
2. Broadcasting Scripts to All Sessions
To send JavaScript to all connected browser tabs for the current user:
3. Event Handlers with JavaScript
You can combine DOM updates with JavaScript execution:
{:data-on-click
(weave/handler []
;; Update the DOM
(weave/push-html! [:div#status "Processing..."])
;; Execute JavaScript
(weave/push-script!
(squint/clj->js
(let [result (js/fetch "/api/data")]
(.then result #(.json %))
(.then result #(js/console.log "Data received:" %))
(.catch result #(js/console.error "Error:" %))))))}
4. DOM Manipulation
You can directly manipulate the DOM:
(weave/push-script!
(squint/clj->js
(let [element (js/document.getElementById "counter")]
(set! (.-textContent element)
(inc (js/parseInt (.-textContent element)))))))
The clj->js
Macro
The clj->js
macro is the primary tool for JavaScript interoperability:
(weave/push-script!
(squint/clj->js
(defn greet [name]
(js/alert (str "Hello, " name "!")))
(greet "World")))
This transpiles the Clojure code to JavaScript and executes it in the browser.
The clj->js
macro accepts an optional map of options:
(weave/push-script!
(clj->js
{:elide-imports true
:elide-exports true
:top-level false
:context :expr
:core-alias "squint.core"}
(defn advanced-example []
(js/console.log "Custom options"))
(advanced-example)))
Limitations and Differences from ClojureScript
While Squint provides excellent JavaScript interoperability, it's important to understand its limitations:
- Not Full ClojureScript: Squint is a lightweight transpiler, not a complete ClojureScript implementation
- Different Data Structures: Squint maps to JavaScript primitives
- Limited Standard Library: Only a subset of Clojure's core functions are available
- No Advanced Optimizations: Unlike ClojureScript, there's no advanced compilation or optimization
- Runtime Transpilation: Code is transpiled at runtime, not ahead-of-time
Example: Timer Application
Here's a complete example of a timer application using JavaScript interoperability:
(defn timer-view []
[:div.p-6
[:h1#timer.text-4xl.font-bold "0"]
[:div.flex.gap-2.mt-4
[:button.bg-green-500.text-white.px-4.py-2.rounded
{:data-on-click
(weave/handler []
(weave/push-script!
(squint/clj->js
(let [interval-id (js/setInterval
(fn []
(let [timer (js/document.getElementById "timer")
current (js/parseInt (.-textContent timer))]
(set! (.-textContent timer) (inc current))))
1000)]
(set! (.. js/window -weaveTimerId) interval-id)))))}
"Start"]
[:button.bg-red-500.text-white.px-4.py-2.rounded
{:data-on-click
(weave/handler []
(weave/push-script!
(squint/clj->js
(when (.. js/window -weaveTimerId)
(js/clearInterval (.. js/window -weaveTimerId))
(set! (.. js/window -weaveTimerId) nil)))))}
"Stop"]
[:button.bg-blue-500.text-white.px-4.py-2.rounded
{:data-on-click
(weave/handler []
(weave/push-script!
(squint/clj->js
(let [timer (js/document.getElementById "timer")]
(set! (.-textContent timer) "0")))))}
"Reset"]]])
This example demonstrates:
- Setting up a JavaScript interval timer
- Storing state in a browser window property
- Manipulating the DOM directly
- Cleaning up resources when stopping the timer