using a DOM event callback inside an object

Hello !

How do you make a DOM element callback to a function if both the DOM element and the function are inside the same object. I have made this little example:

this is my object :

function Snape()
{
    this.myInput = createInput("");
    this.myInput.changed(this.erase);
    this.erase = function()
    {

    }
}

But I get the error 11913: Uncaught TypeError: Cannot read property 'bind' of undefined

Is that possible ?

Answers

  • edited March 2017 Answer ✓

    @ statement this.myInput.changed(this.erase);, much probably property erase doesn't exist yet.
    You may try out to move up this.erase = function() {} before this.myInput.changed(this.erase);.

    Or place erase() outside your class Snape.
    Keyword this inside the callback function refers to the p5.Element which called it back. :P

    function setup() {
      new Snape;
    }
    
    class Snape {
      constructor() {
        this.myInput = createInput('Type Here').changed(erase);
      }
    }
    
    function erase() {
      console.log("I've got changed by:");
      console.log(this);
    }
    

    Look up changed() web reference too: :D
    http://p5js.org/reference/#/p5.Element/changed

  • I didn’t think it would but moving up the function before the callback actually fixed it. That’s a bit messy though, being force to put a function in the middle of the rest. is there another way to let it know that this function exists before, and explicit the function later ?

  • edited January 2017

    Another way, in which callback erase() is a Snape's method: :bz

    function setup() {
      new Snape;
    }
    
    class Snape {
      constructor() {
        this.myInput = createInput('Type Here').changed(this.erase);
      }
    
      erase() {
        console.log("I've got changed by:");
        console.log(this);
      }
    }
    
  • edited January 2017

    However, if you really need to access this inside the callback as an instance of class Snape instead of this representing the p5.Element which invoked the callback, you're better off passing it as an arrow lambda expression for change(): :(|)

    function setup() {
      new Snape;
    }
    
    class Snape {
      constructor() {
        this.myInput = createInput('Type Here').changed(() => {
          console.log("I've got changed by:");
          console.log(this.myInput);
    
          console.info('From instance object:');
          console.info(this);
        });
      }
    }
    
  • edited January 2017

    Actually, I needed to access both the instance of my class and the p5.Element which invoked the callback :) so I just added var self = this; at the beginning of the constructor function

    function Snape(radius)
    {
        this.radius = radius;
        var self = this;
        this.myInput = createInput("");
        this.myInput.changed(this.erase);
        this.erase = function()
        {
              self.radius = Number(this.value());
        }
    }
    

    I can’t use the => function because I need to call this.erase a lot of times in different instance of the object, and from multiple DOM elements.

    I don’t seem to be able to declare a class in p5.js editor : Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

    I was suggested to do something like this : this.myInput.changed(function(){ myself.erase(); }); but this is not working either.

    I get the same error Uncaught TypeError: undefined is not a function I tried all the different possibilities :

    this.myInput.changed(function(){ myself.erase() }); 
    this.myInput.changed(function(){ myself.erase; }); 
    this.myInput.changed(function(){ myself.erase }); 
    

    neither of those are working.

  • Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

    Such error message should only show up on JS engines older than 1 year. [-(
    It means your p5.js IDE editor is internally relying on some deprecated Electron browser.

    Rather than using such old technology, why not online editors, such as these: :bz

    1. https://OpenProcessing.org/sketch/create
    2. http://p5js.SketchPad.cc/
    3. http://CodePen.io/pen/

    Regardless, just type in "use strict"; as the 1st statement for your ".js" file and the error should go away. ;)

    https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode

  • edited September 2017

    I need to call this.erase a lot of times in different instance of the object, and from multiple DOM elements.

    If you really need for callback erase() to be flexible enough to be re-used by other p5.Element createInput(), you're gonna need to apply some advanced stuff. :ar!

    For regular function, its this refers to the object which invoked it.
    As argument for method change(), that object would be a p5.Element.

    The var self = this; idea would create a closure for callback erase().
    Thus we got self for the instance object and this for p5.Element inside erase(). *-:)

    In my latest example below, I've added method addNewInput() which push() a new createInput() for array myInputs[]. And passes erase() as callback for changed().

    Callback erase() knows about both self and this inside its body.
    And besides logging those, it also changes Snape's radius property.

    // forum.Processing.org/two/discussion/20459/
    // using-a-dom-event-callback-inside-an-object#Item_7
    
    // 2017-Jan-25
    
    "use strict";
    
    let snape;
    
    function setup() {
      createCanvas(400, 300);
      snape = new Snape;
      snape.addNewInput();
    }
    
    class Snape {
      constructor() {
        const self = this;
    
        this.erase = function () {
          console.log('Instance Object:\n', self);
          console.log('p5.Element Caller:\n', this);
    
          console.info('Current value:', this.value());
          self.radius = +this.value();
          console.info('Current radius:', self.radius);
        };
    
        this.myInputs = [];
        this.addNewInput();
    
        this.radius = 0;
      }
    
      addNewInput() {
        this.myInputs.push(createInput('').changed(this.erase));
        this.myInputs[this.myInputs.length - 1].elt.focus();
      }
    }
    
  • I see that this.erase is still defined as a function inside the class and addNewInput() is consider as a method of the class. Could you elaborate a bit on the difference between the two ? when is it better to use the one or the other ? Could I define erase as a method and then use a callback from a DOM object calling this method ?

  • this.erase is still defined as a function inside the class and addNewInput() is consider as a method of the class.

    Given they can be called as snape.erase(); and assnape.addNewInput();, they're both methods of class Snape. ~:>

    Could you elaborate a bit on the difference between the two?

    Sure! B-) They mainly differ on actual location, when created and how many times created:

    1) Where does each 1 dwell?

    Let's start w/ erase(). Given it's created as this.erase = function () {}, it is surely a property of the object created by new Snape;. And it is thus annexed to it.

    So erase() is an instance property of class Snape too, just like myInputs[] & radius. :>

    But what about method addNewInput()? It isn't located inside instances of class Snape, but rather in its prototype object.
    Here's its actual location: Snape.prototype.addNewInput. @-)

    https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype

    Now you'd ask: How come snape.addNewInput(); finds out where Snape.prototype.addNewInput is, given it's not actually inside instance object snape? 8-}

    Explanation: Inside all JS objects, there's a hidden property called __proto__.
    And every time a property isn't found inside an object at 1st, JS turns its search to __proto__.

    https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

    When objects are created via new, __proto__ is automatically initialized to point to its class constructor() function's prototype object. For class Snape, that's Snape.prototype object.

    So for snape.addNewInput();, given addNewInput isn't an own property of instance object snape, it ends up finding addNewInput @ Snape.prototype.addNewInput via snape's __proto__. :-B

  • edited January 2017

    2) When is each 1 created?

    Method addNewInput() is created at the same time its class is created.
    But erase() is created when its class' constructor() function is called by new, like all the other instance properties. :-\"

    3) How many times is each 1 created?

    Method addNewInput() is created once only, just like its class.
    But erase() is created every time a new is issued and its constructor() is then called. 8-|
    Same for the rest of the instance properties: myInputs[] & radius. :P

  • edited January 2017

    When is it better to use the one or the other?

    Given prototype located methods are created once only, if you value RAM, you should always favor such over own instance methods. :-bd

    Although own instance methods got slightly faster access b/c they're found right up and skip looking up its prototype via __proto__. \m/
    However, I find such CPU gains are insignificant compared to RAM savings for most cases. :-@

    Another advantage of own instance methods is they can use the revealing pattern in order to emulate private properties in JS, if you value such feature as well. :(|)

  • edited January 2017

    Could I define erase() as a method and then use a callback from a DOM object calling this method?

    Oops! Almost forgot that 1... #-o

    As I had explained already, the this inside a function points to the object which had called that function.

    If "as a method" you mean the case where the method resides in its constructor's prototype object, you won't be able to use the const self = this; closure trick on it. :-SS

    That is, you won't be able to access the instance reference provided by the self variable closure. :(

    For example, the prototype erase() method can't access, modify or invoke properties of its own class, b/c its this is the callback caller. That is, The p5.Element created by createInput(). :-B

    In that exceptional case where a method is used as a callback, it needs to be defined inside its constructor in order to inherit a closure storing its instance object this.

    Of course, for 95%+ cases, stick w/ prototype methods in order to conserve RAM. :-bd

Sign In or Register to comment.