1

Pure Javascript only please. - also I'm pretty noob, so sorry if my question is a little convoluted.

I'm operating on an htmlCollection var puzzleAreaContents - which contains 16 <div> tags

next we go inside a loop, cycling through the elements, adding a click event listener.

puzzleAreaContents[movables[i]].addEventListener("click", shiftPuzzlePiece);

when I click on the element, I have access to this inside the shiftPuzzlePiece function, "this" being the the <div> tag I just clicked on.

I have two questions

  1. how / why does shiftPuzzlePiece function have access to this, the clicked dom element ?
  2. how can I pass any arbitrary element to shiftPuzzlePiece without breaking it's current usability? -- How can I define this when I'm passing the function an object, so that it behaves the same or similarly to when it is called via the click event listener?

that is to say that it is not currently set up to receive arguments

ex: shiftPuzzlePiece(some_arg)

James
  • 4,927
  • 3
  • 22
  • 27

4 Answers4

1
  1. The event handler creates a new Execution ContextECMA which binds this to the element clicked
  2. You can use your own binding in order to replace this with callMDN.

For example,

shiftPuzzlePiece.call(puzzleAreaContents[movables[i]]);
Travis J
  • 81,153
  • 41
  • 202
  • 273
0

Usually you call a function as a statement/expression:

var ret = shiftPuzzlePiece(arg0);

There are also Function.prototype.call and .apply, with which you can provide the this context:

var ret = shiftPuzzlePiece.call(that, arg0);
var ret = shiftPuzzlePiece.apply(that, [ arg0 ]);

Now that becomes this inside the function.


Example

var piece = { };

puzzleAreaContents[movables[i]].addEventListener("click", function () {

    shiftPuzzlePiece.call(piece, this /* element */);

});

function shiftPuzzlePiece(element) {

   // this === piece
   // element === the clicked puzzleAreaContents

}
metadings
  • 3,798
  • 2
  • 28
  • 37
0

You can bind any value to be the this object by using bind. For example:

shiftPuzzlePiece.bind({x:23});

Will ensure that the this object will have this.x equal to 23.

You can also give pass in other parameters as well, but they would have to be in order. Bind returns a function.

More info about bind here.

The call and apply functions work similarly, but they do not return a new function but instead call the function.

Wio
  • 1,197
  • 10
  • 9
0

Another way not mentioned here is using a closure which actually is faster than apply(), call() and bind()

(function f(){
  this.a = 0;
  var self = this;
  var e = document.getElementById('list').children, i;
  for (i = 0; i < e.length; i++) {
    (function(i){
      e[i].onclick = function(){
        _f(this, i);
      };
    })(i);
  }
})();

Or, it may be written like this as well

(function f(){
  this.a = 0;
  var self = this;
  var e = document.getElementById('list').children, i;
  for (i = 0; i < e.length; i++) {
    e[i].onclick = (function(i){
      return function() {
        _f(this, i);
      };
    })(i);
  }
})();

And finally, the _f function in this case

function _f(y, z){
  console.log(this.a + " / " + y.innerHTML + " / " + z);
}

HTML

<ul id="list">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>

Output:

0 / 1 / 0
0 / 2 / 1
0 / 3 / 2
0 / 4 / 3
0 / 5 / 4

Working jsBin | Banchmark

hex494D49
  • 9,109
  • 3
  • 38
  • 47