CallBack function with parameters

This is what I have in my html:

<p id="dz"> Dropzone </p>

and in my sketch.js I have this:

function setup() {
  noCanvas();

  var dzPtr=select("#dz");
  dzPtr.dragOver(highlight);
  dzPtr.dragLeave(unhighlight);
}

function highlight(){
  this.style('background-color','#ccc');
}

function unhighlight(){
  this.style('background-color','#fff');
}

Question: Instead of having two function to work on the paragraph element, could I use one with parameters? How to implement something like this? I understand I will need a closure. Could somebody provide me an example?

Kf

Answers

  • edited June 2017

    ... could I use one with parameters?

    Like all regular callbacks, we don't choose which parameters are sent when its trigger invokes it. [-X

  • edited June 2017 Answer ✓

    Nonetheless, I did an example w/ dragOver() only, relying on setTimeout() in order to dehighlight it after 400 ms; and clearTimeout() in order to cancel it when highlight() is re-invoked before 400 ms: :ar!

    1. https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
    2. https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout
    function setup() {
      noCanvas();
      createP('Drag me into "Drag Here!"');
      createP('Drag Here!').dragOver(highlight);
    }
     
    function highlight() {
      this.style('background-color', 'red');
      clearTimeout(highlight.id);
      highlight.id = setTimeout(() => this.style('background-color', 'gray'), 400);
    }
    
  • edited June 2017

    This next attempt doesn't work, but should it?

    In setup():

      //NOTICE: h0 and h1 are global
      h0=new onOffAction("#fff");
      h0=h0.highlight2();
      h1= new onOffAction("#ccc");
      h1=h1.highlight2();
    
      var dzPtr=select("#dz");
      dzPtr.dragOver(h1);     //highlight
      dzPtr.dragLeave(h0);   //unhighlight
    

    And then using a closure:

    function onOffAction(v) {
      this.val=v
    }
    
    onOffAction.prototype.highlight2=function() {
      var dzPtr=select("#dz");
      dzPtr.style('background-color', this.val);
    }
    

    Using a different indirect approach, in the console log I can do:

    var pp = new onOffAction("#456");
    pp.highlight2();
    

    and it works, my HTML element's style gets updated. So I guess I cannot pass a closure's function as a callback?

    Kf

  • This next worked using anonymous functions following this link:

    In setup():

      var dzPtr=select("#dz");
      dzPtr.dragOver(function()  {setListStyle('#ccc')} );
      dzPtr.dragLeave(function() {setListStyle('#fff')} );
    

    And the extra definition:

    function setListStyle( param ) {
      var dzPtr=select("#dz");
      dzPtr.style('background-color', param);
    }
    

    Kf

  • edited June 2017

    In your latest example, you're still using both dragOver() & dragLeave() events.
    I thought you wanted to use dragOver() only?! :-/

    You're also relying on global variable _dzPtr_ select() to a specific Element instead of this for setListStyle(). Therefore, you can't re-use that same callback function for another p5.Element. #-o

    You're still creating 2 distinct callback wrappers for callback setListStyle().
    1 for dragOver() and the other for dragLeave(). 8-|

  • edited June 2017 Answer ✓

    Now that I think I understand what you actually wanted, I've made the following variation example below based on Function::call() method: :bz

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

    function setup() {
      noCanvas();
      createP('Drag me into "Drag Here!"');
    
      createP('Drag Here!')
        .style('background-color: gray')
        .dragOver(function ()  { changeBG.call(this, 'red'); })
        .dragLeave(function () { changeBG.call(this, 'gray'); });
    }
      
    function changeBG(colour) {
      this.style('background-color', colour);
    }
    
  • edited June 2017 Answer ✓

    And a much concise 1 based on Function::bind() method.
    No need to wrap up callback changeBG() w/ another function this time: :-bd

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

    function setup() {
      noCanvas();
      createP('Drag me into "Drag Here!"');
    
      const p = createP('Drag Here!').style('background-color: gray');
      p.dragOver(changeBG.bind(p, 'red')).dragLeave(changeBG.bind(p, 'gray'));
    }
      
    function changeBG(colour) {
      this.style('background-color', colour);
    }
    
  • @GoToLoop

    I have both dragOver and dragLeave and instead of having highlight and unhighlight functions, I would like to use a callback function with a parameter. My last attempt worked... I was hoping one could do it some other way...

    Kf

  • @GoToLoop Thank you!

    Question 1: Any big difference between using your call() version and my setListStyle() version related to efficiency? Would one be more proper over the other?

    Question 2: Could you comment on the difference between bind and call? They have almost the same signature, The subtle difference is how they access the object to modify. On one you use 'this' and on the second you use the actual reference to the element, aka. 'p'. Also, I can see the second parameter of bind and call gets assigned as the parameter for changeBG(). This is very neat. Could I do something like this, passing multiple param instead of only one?

    createP('Drag Here!').dragOver(function () { changeCol.call(this, 'red', 'blue'); })

    then

    function changeCol(colourBG, colourTxt) {
      this.style('color', colourTxt);
      this.style('background-color', colourBG);
    }
    

    Question 3: Related to my closure attempt (my post where I use highlight2() above), why it doesn't work? Any ideas?

    Kf

  • edited June 2017 Answer ✓

    Answer 1:

    The diff. is that your setListStyle() can only act upon the HTMLElement whose attribute id is '#dz':
    https://developer.Mozilla.org/en-US/docs/Web/HTML/Global_attributes/id

    While my changeBG() callback expects its this to be any p5.Element object:
    https://p5js.org/reference/#/p5.Element

    That enforcement is achieved by call(), apply() or bind(), which change a function's this to a specific object. :ar!

    There's some slightly bottleneck when we force a function's this to become some other object. But I doubt it to be any perceptible amount for most cases. ;;)

    As a bonus, here's a version in which changeBG() callback doesn't rely on this.
    Instead, it requests a p5.Element object as its 1st argument; thus still keeping its flexibility to act upon on any HTMLElement on the HTML page: \m/

    function setup() {
      noCanvas();
      createP('Drag me into "Drag Here!"');
     
      createP('Drag Here!')
        .style('background-color: gray')
        .dragOver(function ()  { changeBG(this, 'red'); })
        .dragLeave(function () { changeBG(this, 'gray'); });
    }
       
    function changeBG(p5Element, colour) {
      p5Element.style('background-color', colour);
    }
    
  • edited June 2017 Answer ✓

    Answer 2:

    A) call() case:

    • For the call() case, it is run inside the wrapper function after the event has been triggered.
    • At that moment, inside the just invoked wrapper function, this is a p5.Element reference.
    • Then the statement changeBG.call(this, 'red'); invokes changeBG() via call(), forcing it to have its this to be the same as wrapper's p5.Element reference this as well. :ar!

    B) bind() case:

    • Both call() & apply() immediately invoke a function w/ its this set to some object reference.
    • But bind() creates a wrapper for a function rather than invoking it, permanently setting its this to some object reference for later calls of the wrapper. :-B
    • The reason I had to pass variable p (which is of datatype p5.Element btW) instead of this is b/c the bind()'s wrapper is being created, not called.
    • And this is the global Window object rather than the p5.Element object at that moment. @-)
  • edited June 2017 Answer ✓

    Answer 3:

    Related to my closure attempt...

    I didn't see any closure technique there. That was merely an ordinary prototypal OOP pattern. :-@

    ... why it doesn't work?

    • In JS, regular functions w/o return returns undefined.
    • So when h0 = h0.highlight2(); is run, variable h0 is reassigned to undefined. @-)
    • Therefore @ dzPtr.dragLeave(h0);, undefined is the argument passed to dragLeave()! #-o
  • @GoToLoop

    This is my new attempt and it works :-bd :

    button_olist.js

    function setup() {
      noCanvas();
      var dzPtr=select("#dz");
      dzPtr.dragOver(onOffAction("#ccc"));
      dzPtr.dragLeave(onOffAction("#fff"));
    
    }
    
    function draw() {
    }
    
    
    function onOffAction(v) {
      var val=v;
      function highlight2(){
          var dzPtr=select("#dz");
          dzPtr.style('background-color', val);
      }
      return highlight2;
    }
    

    Index.html

      <html>
      <head>
      <meta charset="UTF-8">
    
      <!-- PLEASE NO CHANGES BELOW THIS LINE (UNTIL I SAY SO) -->
      <script language="javascript" type="text/javascript" src="libraries/p5.js"></script>
      <script language="javascript" type="text/javascript" src="libraries/p5.dom.js"></script>
      <script language="javascript" type="text/javascript" src="button_olist.js"></script>
      <!-- OK, YOU CAN MAKE CHANGES BELOW THIS LINE AGAIN -->
    
      <!-- This line removes any default padding and style. 
      You might only need one of these values set. -->
      <style> 
      body {
        padding:   0; 
        margin:  0;
      } 
      #dz {
        padding:36px;
        border-style:dashed;
        font-size:36pt;
        width:50%;
      }
       </style>
    
    
      </head>
    
      <body>
    
      <h1> Custom List</h1>
    
      <p id="dz"> Dropzone </p>
    
      <button id="but">Add</button>
    
      <ol id="olist">
      </ol>
    
      </body>
      </html>
    

    Now I get one question. One of the things I was doing wrong before was:

    function onOffAction(v) {
      this.val=v;    <<<<-------NOTICE "this."
      function highlight2(){
          var dzPtr=select("#dz");
          dzPtr.style('background-color', this.val);  <<<<-------NOTICE "this."
      }
       return highlight2;  <<<----HERE I was doing something else before
    }
    

    Could you comment why this.val is different to var val?

    Kf

  • edited June 2017 Answer ✓

    Could you comment why this.val is different to var val?

    • The diff. between them seems subtle in JS, but there are quite some indeed. L-)
    • val is a variable & v is a parameter, which is a variable as well.
    • While at this.val, val is a property of dynamic object this. :-B
    • Variables & parameters belong to a function.
    • While properties belong to an object. ;;)
    • Variables & parameters aren't preceded by the operator . dot.
    • While properties always are. :P
    • And properties don't create closures.
    • While variables & parameters from an enclosing outer function can be accessed, thus becoming a closure, from its nested functions & classes. \m/
    • BtW, there are 5 JS keywords to declare variables: var, let, const, function & class. B-)
Sign In or Register to comment.