2

Coffeescript wraps your code into a wrapper like

(function() { 
/* your code */
}).call(this);

Here, this means window. So, to create a public interface I do something like

this.publicObject =
  someMethod: ->
    document.getElementById("button1").innerHTML = "Changed!"

I can then register a callback in the HTML document invoking my .js file with something like <span onclick="publicObject.someMethod();">Click</span>.

However, what if I wanted to call someMethod from the .coffee file (to be called on document ready, I think EDIT: See accepted answer + comments below)? If I just follow the above code up with

publicObject.someMethod()

it seems like the document object is not accessible within someMethod due to context issues. How can I call publicObject.someMethod() from my .coffee file and have it recognize document?

Note: apply() and call() trickery is OK, but I don't want to get rid of the wrapper, if possible. If you care, I use the following to compile my script:

coffee -j -p -c coffee/*.coffee > www/app.js
MarkovCh1
  • 276
  • 1
  • 3
  • 11

2 Answers2

4

Just do window.publicObject.someMethod(). This avoids the whole variable this scope issue. You can use this and window interchangeably in the top level scope of you CS code, but once you get inside functions, you'll need to use window. I suggest using window all the time as its A) clearer and B) sidesteps the whole this issue which has caused countless hours of head scratching.

Also, this is the namespace pattern I use. I create 1 top-level global object and hang everything off of that neatly. It starts like this.

OT = window.OT = {} #root of the public API namespace
OT.someNestedPublicObj = {}
Peter Lyons
  • 142,938
  • 30
  • 279
  • 274
  • For those seeing this in the future: `window.publicObject.someMethod()` does the same, in my case, as `publicObject.someMethod()`, but I agree with Peter that I should start using `window` instead of `this` (it's clearer, simpler). – MarkovCh1 Aug 05 '11 at 16:54
  • I handle this a bit differently. I use `root = window ? global` to grab a root namespace. Then when exporting, I do something like `root.exportableMethod = exportableMethod`. In consumers I do `exportableMethod = root.exportableMethod` to import the name from some other file. – Steve Ross Aug 16 '11 at 23:36
4

The wrapper doesn't hide document, which is a global because it's attached to window. Unless you've declared a variable named document within your .coffee file (by writing document = ...), document will be accessible from someMethod. Try adding console.log document to the top of someMethod to check for yourself.

So there must be something else going on. What exactly is the error message you get when someMethod is called?

Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196
  • When I call `publicObject.someMethod()` from my .coffee file, the exact error is `Uncaught TypeError: Cannot set property 'innerHTML' of null`. So I thought, "Perhaps coffeescript doesn't compile onload?" In fact, this is true. When I call `window.onload = publicObject.someMethod` my code runs as expected (no errors and "button1"'s HTML is updated to "Changed!"). P.S. If you want to edit your answer for others to see more clearly the issue, that'd be great. – MarkovCh1 Aug 05 '11 at 16:50
  • Sounds like you assumed that the `.call(this)` wrapper would be equivalent to `document.onload = ...` or the ubiquitous `$(...)` powered by jQuery. It just runs the function immediately, though, whether `button1` exists or not. An alternative solution would be to move the ` – Trevor Burnham Aug 05 '11 at 17:26