Error in execution of function on an object

1

What I want to achieve is that if the message has the same or more than 15 characters it would show me this.c as true osino as false , but it shows me undefined .. why?

function A() {
  
  this.mp = function(msg) {
    
    this.c = msg.length >= 15 ? true : false;
    
    return this.c;
    
  };
  
 var to = (function() {
   
   window.addEventListener("load", function(){
     
     document.getElementById("b").addEventListener("click", function(){
       
       this.mp(document.getElementById("a").value.toString());
       console.log(this.c);
  
     });
     
   });
   
   
 }).bind(this)();
  
  
}

document.getElementById("b").addEventListener("click", function(){
  
  nw = new A();
  
  console.log(this.mp);
  
  
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
<input type="text" id="a" placeholder="Mensaje"/>
<input type="button" id="b" value="Enviar mensaje"/>
</body>
</html>
    
asked by Eduardo Sebastian 02.07.2017 в 17:32
source

1 answer

3

The problem is that this is not what you think it is here:

document.getElementById("b").addEventListener("click", function(){
  nw = new A();
  console.log(this.mp);
});

You are associating an event handler click with the b button. And within that controller, you create a new A() and assign it to nw . Then you do this.mp ... But this in that context is the b button, which does not contain any method mp ; that's why you receive undefined . It is nw that contains the method, so you should do nw.mp() . But there is still more. mp takes a parameter with the string and you would not be passing anything. You have to pass the string with the value of the text field a.

Changing those two things, it would work:

document.getElementById("b").addEventListener("click", function() {
  nw = new A();
  console.log(nw.mp(document.getElementById("a").value));
});

As you can see in this demo:

function A() {

  this.mp = function(msg) {
    this.c = msg.length >= 15 ? true : false;
    return this.c;
  };

  var to = (function() {
    window.addEventListener("load", function() {
      document.getElementById("b").addEventListener("click", function() {
        this.mp(document.getElementById("a").value.toString());
        console.log(this.c);
      });
    });
  }).bind(this)();

}

document.getElementById("b").addEventListener("click", function() {
  nw = new A();
  console.log(nw.mp(document.getElementById("a").value));
});
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

<body>
  <input type="text" id="a" placeholder="Mensaje" />
  <input type="button" id="b" value="Enviar mensaje" />
</body>

</html>

In the comments, you say why it does not work even when it is similarly put in the internal function to . I will explain in this edition why that is the case and why it is the expected behavior (and also why it should not be done that way).

We are going to analyze the code:

var to = (function() {
   window.addEventListener("load", function(){
     document.getElementById("b").addEventListener("click", function(){ 
       this.mp(document.getElementById("a").value.toString());
       console.log(this.c);
     });
   });
 }).bind(this)();

You create a function (that you automatically call trying to change the this with bind ) that adds a controller of the event load of the window in which a controller of the event click will be put to the button ... and here is the first problem.

The window has already been loaded before calling to (because it is called when the button has already been clicked, see problem with this later). Then adding an event handler load to window does not produce any results because that event has already been fired and will not fire again. We are going to eliminate it:

var to = (function() {
   document.getElementById("b").addEventListener("click", function(){ 
     this.mp(document.getElementById("a").value.toString());
     console.log(this.c);
   });
 }).bind(this)();

Perfect. Now with this code the controller of click is triggered ... although you have to press a second time to trigger the controller that was just associated but we received an error:

  

Uncaught TypeError: this.mp is not a function at HTMLInputElement.

Why does that happen? For the same reason I commented at the beginning of this answer: this is not what you think it is. Even if you changed the value of this with bind , you are now inside the function of the event handler click and the this has changed to be the button. This could be solved by changing the value of this in the controller by calling bind again:

var to = (function() {
   document.getElementById("b").addEventListener("click", (function(){ 
     this.mp(document.getElementById("a").value.toString());
     console.log(this.c);
   }).bind(this));
 }).bind(this)();

Perfect! It works ... or not? Now it finally returns the correct value, but there is an additional problem - every time I press the button the result is shown once more: the first time it is displayed once, the second time it is displayed twice, the third time it is displayed Show three times ... and like that.

Why does that happen? Now you have to look at another culprit:

document.getElementById("b").addEventListener("click", function(){
  nw = new A();
  //console.log(this.mp);
});

Each time the button is pressed, the controller function is called, a A() is created, which calls its to , which associates a new controller with the button ... so every time it is press the button, a new controller is being associated. That's why now the result is shown multiple times.

How could I solve that? Well, there are different modes. But perhaps it would be best if an event handler click were not associated with the button inside to . This way you would kill two birds with one stone: the actions will not be repeated multiple times and you will not have to press the button twice to see the result.

Then the code of to would look like this:

var to = (function() {
   this.mp(document.getElementById("a").value.toString());
   console.log(this.c);
 }).bind(this)();

And the final code would look like this:

function A() {

  this.mp = function(msg) {
    this.c = msg.length >= 15 ? true : false;
    return this.c;
  };

  var to = (function() {
    this.mp(document.getElementById("a").value.toString());
    console.log(this.c);
  }).bind(this)();
  
}

document.getElementById("b").addEventListener("click", function() {
  nw = new A();
});
<input type="text" id="a" placeholder="Mensaje" />
<input type="button" id="b" value="Enviar mensaje" />
    
answered by 02.07.2017 / 17:56
source