6
(defn domready [handler]
  (.addEventListener js/window "DOMContentLoaded" handler))

I borrowed this code from here. The problem is I don't totally understand what's going on. JS interop is still a bit of a mystery to me.

  1. .addEventListener

So this is clearly a procedure call, but it's kind of generic. It's like Clojurescript took everything that was inside an object, took it out, and you use it to call that method on "objects". As long as that "object" has the ".addEventListener" property it will call this. Is that what it's doing? Why not use a keyword instead? like (:addEventListener domElement) that seems more logical to me.

  1. js/window

What is this? Is it a namespace or an object? Are they the same thing?

  1. "DOMContentLoaded"

A string, that's familiar.

  1. handler

Also familiar, but does it have a notion of this? Not that I'm really going to miss this.

3
  • For this you can use the this-as macro. Commented Dec 23, 2015 at 7:19
  • 1
    Here's one of the tutorials I wrote for teaching interop chimeces.com/cljs-browser-repl/#/notebook/… I hope it helps. I'll add an answer with answers to your questions. Commented Dec 23, 2015 at 10:23
  • Just to avoid confusion, .addEventListener is a method call. There is also the notion of properties, which are accessed with a .- (period and minus/dash). In OO I guess you would say (.method obj) runs the object method while (.-property obj) accesses the property/variable in object. Commented Dec 29, 2015 at 3:21

3 Answers 3

7

.addEventListener

So this is clearly a procedure call, but it's kind of generic. It's like Clojurescript took everything that was inside an object, took it out, and you use it to call that method on "objects". As long as that "object" has the ".addEventListener" property it will call this. Is that what it's doing? Why not use a keyword instead? like (:addEventListener domElement) that seems more logical to me.

Your mental model about how this works is mostly fine. What it does when it compiles is move the function name to be run as a method on the first argument.

(.method obj ...args) get's transformed to obj.method(...args)

This type of interop comes from the parent language Clojure.

On why do we have an explicit version of calling the function that's not Clojure idiomatic, I think the idea is to have clear separation between what is native Clojure code with Clojure semantics (immutability, friendly to CLJ data structures, etc) and what is interoperating with the host environment (mutable, not friendly to CLJ data structures, etc).

In my opinion it is better to have clear separation between those two given how different the semantics of CLJS and the host platforms are. For me explicitness is better than implicit in this case (it is easy to spot looking at the code what is JS code in CLJS, and what is pure CLJS).

js/window

What is this? Is it a namespace or an object? Are they the same thing?

Both, js/ is accessing the namespace js, which is where CLJS puts the JS namespace (since there is only one and global). window is just grabbing the window variable from the js namespace.

This is no different from how you access variables in other namespaces in CLJS. If you (def a 1) in (ns cljs.test) and then run cljs.test/a that will give you 1. Same form, ns/something-in-that-ns.

"DOMContentLoaded" A string, that's familiar.

\o/

handler

Also familiar, but does it have a notion of this? Not that I'm really going to miss this.

Not sure what this has to do with handler. It is just a higher order function passed into domready as a parameter, like you would do in JS: function domready (onReady) { window.addEventListener("DOMContentLoaded", onReady) }


I hope this helps, if you want to try it out live and learn some more, maybe visit the Talking with JS on the Diving into ClojureScript tutorial, or maybe check this section of the lt-cljs-tutorial.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for such a fantastic answer!
3

I'm just learning clojurescript so I don't really know if it is the correct answer but I've done it in the following way:

(defn handler [] (js/console.log "ready"))
(js/document.addEventListener  "DOMContentLoaded" handler)

which is then translated to

cljs.user.handler = (function cljs$user$handler(){
  return console.log("ready");
});
document.addEventListener("DOMContentLoaded",cljs.user.handler);

to check how clojurescript translate code I've used KLIMPSE http://app.klipse.tech/

1 Comment

Klipse is really great to understand what is going on behind the scenes, thanks for sharing
2

.addEventListener is a method call on the global Javascript object js/window. This method call takes two parameters: "DOMContentLoaded" and handler.

When you are doing interop (either Java or Javascript) you really are calling methods on objects. There are macros behind what is happening here. What is straight after the ( is a verb, which I usually think of as a function call (although it might also be a macro or a special form). When doing interop what comes after the verb is the instance, and after that the parameters.

If it were straight Javascript it would look like this:

function domready(handler){
    window.addEventListener("DOMContentLoaded" handler);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.