0

I can't seem to find around a way to resolve one apparently simple task. I've made a simple Js class, let's say:

class MyClass{
    constructor() {
        this.myProperty = 10;
    }
    
    init(){
        $(".my-items").on('click',this.myMethod);
    }
    
    myMethod(){
        // How do i access both this.myProperty and $(this) to have the current called jQuery object?
    }
}

If i want to access this.myProperty i have to use:

$(".my-items").on('click',this.myMethod.bind(this));

but if i do that i lose access to $(this) which would give the jQuery object on which the click event was triggered.

How am i supposed to keep reference to both "this"? because that's what i would need like 90% of the times

Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
Diego
  • 1,610
  • 1
  • 14
  • 26
  • At least related: https://stackoverflow.com/questions/27670401/using-jquery-this-with-es6-arrow-functions-lexical-this-binding – T.J. Crowder Nov 04 '22 at 10:46
  • Well that would be a way to go, but even with vanilla JS i may end in the same situation. I know that i could use e.target to get the DOM element but i feel like actual vanilla is overcomplicated compared to jquery (personal experience ofc) – Diego Nov 04 '22 at 10:46
  • Your question is answered here: https://stackoverflow.com/questions/51540829/binded-click-callback-on-multiple-html-element-and-getting-the-actual-element-cl Basically: Bind the method, then use `event.currentTarget` to access the element you otherwise would have accessed via `this`. – T.J. Crowder Nov 04 '22 at 10:47
  • 1
    @Diego - Indeed, removing jQuery from this equation changes nothing. – T.J. Crowder Nov 04 '22 at 10:48
  • @T.J.Crowder i think the most real thing about your 2nd suggestion is this statement "If you want to write ES6, you need to write ES6 all the time. You can't switch in and out of it on certain lines of code...". I have a feeling im going down that road and i end in unfortunate situations – Diego Nov 04 '22 at 10:52
  • @Diego - I don't know what answer that's from, but that statement is...suspect. You can adopt ES2015+ stuff while still doing ES5-style things, there's nothing preventing that. ES2015+ is entirely backward compatible (other than a **very** small number of **very** edge cases). This isn't complicated: You want to use `this` for two different things, which you can't. So you use `this` for one of those things, and something else for the other. You can either use `this` to access your instance and `event.currentTarget` to access the element for the event, or do the closure thing to... – T.J. Crowder Nov 04 '22 at 10:55
  • ...access your instance and `this` to access the element for the event. (I'd do the former, it's much simpler.) – T.J. Crowder Nov 04 '22 at 10:56
  • @T.J.Crowder could you write an answer with an easy example of both ways so we mark this as accepted + closed for future reference? – Diego Nov 04 '22 at 11:00
  • That answer already exists in my second link above, [here](https://stackoverflow.com/a/51540909/157247). The question is a duplicate of that one. On SO, we don't post answers to duplicates, we close them pointing to the (ory more often, *an*) earlier answered question, so people finding either question are directed to a single set of answers, rather than having dozens of questions with dozens of slightly-different answers. (Sadly, I misread your question initially and can't now vote to close as duplicate.) – T.J. Crowder Nov 04 '22 at 11:03

1 Answers1

1

class MyFirstClass{
  constructor() {

    this.myProperty = 'first class';

    // assure `this` context by using an arrow function.
    this.myThisContextAwareHandler = evt => {

      const { currentTarget } = evt;
      const { myProperty } = this;

      console.log({ currentTarget, myProperty });
    }
  }
  init(){
    $('.my-item').on('click', this.myThisContextAwareHandler);
  }
}


// But if it comes to binding an event handler function
// there is no need of implementing it as ...
//
// - either arrow function based own method (see above 1st example)
// - or as prototypal method (as shown with the OP's provided code)
//
// A simple good old function is perfectly suited
// for the binding task.

function myThisContextAwareHandler(evt) {

  const { currentTarget, target } = evt;
  const { myProperty } = this;

  console.log({ currentTarget, target, myProperty });
}
class MySecondClass{
  constructor() {

    this.myProperty = 'second class';
  }
  init(){
    // assure `this` context by binding a simple
    // handler function exactly where it is needed.

    // $('.my-item').on('click', myThisContextAwareHandler.bind(this));


    // to the OP ... consider using the Selector API instead of jQuery ...

    /*
    const handler = myThisContextAwareHandler.bind(this);
    document
      .querySelectorAll('.my-item')
      .forEach(elmNode =>
        elmNode.addEventListener('click', handler)
      );
    */
    // ... maybe even in combination with event delegation.
    document
      .querySelector('.my-item-list')
      .addEventListener('click', myThisContextAwareHandler.bind(this));
  }

  // prototypal methods not needed ...
  //
  // ... neither within a constructor function
  //     as source for a to be bound own method.
  // ... nor as source for a to be bound `this`
  //     context aware event-handler.
}

(new MyFirstClass).init();
(new MySecondClass).init();
body, ul { margin: 0; }
li { font-size: .8em; cursor: pointer; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<ul class="my-item-list">
  <li class="my-item">Item 01</li>
  <li class="my-item">Item 02</li>
  <li class="my-item">Item 03</li>
</ul>
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37