-3

When user clicks on .step, the jquery will execute the hello function which works fine. The hello function will call hi function => I got error here: Hi is not a function

jQuery(document).ready(function( $ ) {
    const form = {

        init: function() {
            $('.step').on('click', this.hello)
        },

        hello: function() {
          console.log('Index: ', $(this).index()); // I can get the right index
          this.hi(); // ERROR: Hi is not a function!
        },

        hi: function() {
            console.log('HI')
        },
    }

    form.init()
})

When I put the code like this, it works fine

jQuery(document).ready(function( $ ) {
    const form = {

        init: function() {
            $('.step').on('click', this.hi) // Look here
        },

        hi: function() {
            console.log('HI')
        },
    }

    form.init()
})

Can you guys explain why?

domsson
  • 4,553
  • 2
  • 22
  • 40
coinhndp
  • 2,281
  • 9
  • 33
  • 64
  • `this` is reset in event-handlers unless you `bind` them. – Dai May 18 '20 at 08:08
  • Index in jquery when you click on a dom, that comment is just let you know that the code still works there. I will update. thanks –  coinhndp May 18 '20 at 08:08
  • because you defined this inside the console, so it has no scope out of the console statement so do call hi without this – Engineer S. Saad May 18 '20 at 08:08
  • Does this answer your question? [Javascript "this" pointer within nested function](https://stackoverflow.com/questions/9644044/javascript-this-pointer-within-nested-function) – Martin May 18 '20 at 08:10
  • @SaadSohail You had a great point! It works when I remove this. Could you add a separate comment so we can discuss? –  coinhndp May 18 '20 at 08:10
  • yeah sure do ahead i am here – Engineer S. Saad May 18 '20 at 08:12
  • @SaadSohail ok test it and actually I got your point now :) When I call this.hi() in hello function, this refers to hello. So in order to make it work, I have to bind hi to form object via this.hi = this.hi.bind(this). My question is how can we avoid the binding using Es6 or arrow function? –  coinhndp May 18 '20 at 08:15
  • Hmm actually it does not work when I add this.hi = this.hi.bind(this) to init function –  coinhndp May 18 '20 at 08:25
  • Your own debugging (console.log) should give you a clue. What exactly is `this` inside `hello` when it's used as a click event handler? – freedomn-m May 18 '20 at 08:53
  • Above comment *"because you defined this inside the console"* is just confusing. There's nothing "inside" the console and there's no code that "defines this". What's happening is a rewording of @Dai stated: *"`this` is **set** in event-handlers"*. In code 1 `hello` is an event handler, in code 2 `hi` is the event handler and inside event handlers `this` is set to the DOM element that the event handler applies to. – freedomn-m May 18 '20 at 08:56
  • What do you want / expect `this` to refer to inside the event handler (code 1 `hello`)? Do you want it to be the button that was clicked or your `form` class? At the moment you're using it as if it's both (`$(this).index` and `this.hi`) - which it clearly can't be. – freedomn-m May 18 '20 at 09:00
  • If you want it to be the button, then no changes but you can't use `this.hi` and will need to work around this. If you want it to be `form`, then use: `$('.step').on('click', () => this.hello())` but you lose the ability to know which button was clicked (may not be an issue) – freedomn-m May 18 '20 at 09:03

1 Answers1

1

Hi is not a function

Can any one explain why?

In this code

    init: function() {
        $('.step').on('click', this.hello)
    },

    hello: function() {
      console.log('Index: ', $(this).index()); // I can get the right index
      this.hi(); // ERROR: Hi is not a function!
    },

you setup form.hello as an event handler for the click event. Within event handlers, this is the element that was clicked, as in:

  $("button").click(function() { console.log(this.id); }

the button will (given this code) not have a .hi method.

How can we avoid the binding using Es6 or arrow function?

If you don't need to know which button was clicked, you can use an arrow function when defining the event handler:

    init: function() { 
        $('.step').on('click', () => this.hello())
    },

Example: https://jsfiddle.net/mcuh5awt/2/

Ideally, you'll want to access both the button and the form class, this is possible in two ways, the first is to change how you define your class, but keeping with how you've done it, we can add a new property self to store a reference to its ...well... self...

jQuery(document).ready(function( $ ) {
    const form = {
        self : {},

        init: function() {
            self = this;
            $('.step').on('click', self.hello)
        },

        hello: function() {
          console.log('Index: ', $(this).index());
          self.hi(); 
        },

        hi: function() {
            console.log('HI')
        },
    }

    form.init()
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<button class='step' type='button'>
Step 1
</button>
<button class='step' type='button'>
Step 2
</button>
</div>

As this becomes a public property of your class, you may prefer a different name in this case eg form to match the class name.

freedomn-m
  • 27,664
  • 8
  • 35
  • 57
  • Hi, Thanks you. I misunderstood that in this.hi, this refers to form class while it actually refers to the element that was clicked. How can I make this refer to the form (do we have sth to do with binding here?) Could you explain the binding? What happens if I put this.hi = this.hi.bind(this); in the init function. There are many blogs about binding but I found it really hard to understand (mostly explain with really old Javascript). I think this simple example like we have is the best way to explain –  coinhndp May 18 '20 at 10:15