33

I have a coffeescript class that has some jquery event listeners. I would like to use the fat arrow => to avoid having to reference the class, but I still need a reference to the element that would usually be used with this. How can I use both?

class PostForm
    constructor: ->
        $('ul.tabs li').on 'click', =>
            tab = $(this)
            @highlight_tab(tab)
            @set_post_type(tab.attr('data-id'))

    highlight_tab: (tab)->
        tab.addClass 'active'

    set_post_type: (id) ->
        $('#post_type_id').val(id)
hippietrail
  • 15,848
  • 18
  • 99
  • 158
Andrew
  • 227,796
  • 193
  • 515
  • 708
  • Can you describe a bit more what you want to do? Where do you want to use (this), and what part of the above is not working the way you want? IOW, what object do you want `this` to be a reference to? The target of the event, the PostForm class instance, etc? – Giscard Biamby Sep 28 '12 at 22:35

4 Answers4

37

CoffeeScript links both this and @ to the outer context, therefore you cannot access the context that jQuery provided (aka the desired "this"). Use event.target instead:

class PostForm
    constructor: ->
        $('ul.tabs li').on 'click', (event) =>
            tab = $(event.target)
            @highlight_tab(tab)
Niko
  • 26,516
  • 9
  • 93
  • 110
  • 1
    `event.target` saves us in `Backbone.js` as well – jwchang Sep 30 '12 at 15:00
  • 3
    `event.target` is also _far less confusing_ to someone who doesn't know the DOM rules around `this`. – Nevir Mar 14 '13 at 23:51
  • 9
    Note that you probably want evt.currentTarget if you do events with selectors. See http://codepen.io/ddopson/pen/erLiv – Dave Dopson Mar 15 '13 at 00:08
  • 4
    what do you do on d3? callbacks in d3 give you the object (not DOM) – Dan Apr 08 '13 at 09:32
  • 1
    I don't know if this was true when you wrote it, but event.target sometimes returns a different element than 'this'. 'this' on a click event callback returns the element that triggered the callback, while event.target returns the element the mouse was over when the click event fired (which is often a child element of some sort) – GlyphGryph Aug 22 '16 at 21:29
36

Using evt.currentTarget

You should probably use evt.currentTarget (which is equivalent to this) instead of evt.target (which isn't). If the node that you are tapping for click notifications has child nodes, evt.target might be one of those child nodes instead of the node you added the click handler to.

See http://codepen.io/ddopson/pen/erLiv for a demo of this behavior. (click on the inner red box to see that currentTarget points at the red div while target points at outer blue div that the event handler is registered on)

$('ul.tabs li').on 'click', (event) =>
  tab = $(event.currentTarget)
  @highlight_tab(tab)

Answer to the question asked - getting both `=>` and `this`:

You can do the following...

$('ul.tabs li').on 'click', (event) =>
  tab = $(` this `)     # MAKE SURE TO ADD THE SPACES AROUND `this`
  @highlight_tab(tab)

The spaces are critical as they prevent Coffee from munching this into _this.

Using `self` and `->`

Alternatively, do the following ...

self = this
$('ul.tabs li').on 'click', (event) ->
  tab = $(this)
  self.highlight_tab(tab)

This is similar to CQQL's answer, except that I prefer the idiomatic use of self as the variable name; my VIM syntax highlighting rules color self as a "special" variable just as it would for this, arguments, or prototype.

Dave Dopson
  • 41,600
  • 19
  • 95
  • 85
  • self = this is exactly what I do. It works perfectly! – Kirk Oct 25 '13 at 19:13
  • 1
    event.currentTarget is not actually the same as 'this', at least for jquery on click events. It may sometimes have the same value, but it also might not, depending on circumstances. 'this' refers to the element that was clicked, while event.currentTarget returns the element that was watching for click events. – GlyphGryph Aug 22 '16 at 21:30
  • @GlyphGryph - Thanks for the clarification! – Dave Dopson Aug 24 '16 at 18:15
3

I prefer this version, because I can understand it more easily.

class PostForm
    constructor: ->
        post_form = this

        $('ul.tabs li').on 'click', (event) ->
            tab = $(this)
            post_form.highlight_tab(tab)
Marten
  • 1,336
  • 10
  • 16
  • How would you go about accessing post_form from another function on the class? – Brendon Muir Aug 31 '18 at 04:14
  • Class methods should be able to use `this` normally to refer to the current instance of `PostForm`. – Marten Sep 01 '18 at 11:08
  • Thanks @CQQL :) I elaborated on my question with an answer that shows what I figured out in the end. Initially I wanted to hand the callback function directly to the `click` handler but then realised I should first wrap that in an anonymous function that grabs `this` and passes it to the named function via `self` in order to have the best of both worlds. – Brendon Muir Sep 02 '18 at 21:26
0

You may want to access variables set in the constructor from your functions. This would be how you do it (the key is calling the function via self while first extracting this with a thin arrow):

class PostForm
    constructor: ->
        self = this

        @some_contrived_variable = true

        $('ul.tabs li').on 'click', ->
            tab = $(this)
            self.highlight_tab(tab)
            self.set_post_type(tab.attr('data-id'))

    highlight_tab: (tab) ->
        # Because of the fat arrow here you can now access @ again
        if @some_contrived_variable
            tab.addClass 'active'

    set_post_type: (id) ->
        $('#post_type_id').val(id)

BTW: This is a great explanation of when to use the fat and thin arrow.

Summary:

  1. Do you use this (@) in the function?
  2. Do you want to execute the function later, possibly from a different scope?
Brendon Muir
  • 4,540
  • 2
  • 33
  • 55
  • You can access `@` in `highlight_tab` because `highlight_tab` is called on `self`, not because of the fat arrow. The fat arrow does not make a difference. You can try it yourself by compiling both versions. – Marten Sep 04 '18 at 17:26