Andrew
Andrew

Reputation: 127

Execution context in JavaScript

I have an object named Gallery with several properties and methods. One of them, setCurrentPicture, removes all existing photos and videos DOM elements from gallery container before showing new photo/video. Besides my gallery stops playing video (set this to pause mode) when the user clicks on it. This is togglePlayVideo method inside the Gallery prototype. First I remove elements from DOM and then showing new content. If this is video, I add eventListener to document, use bind method.

So, in the setCurrentPicture I want to remove EventListener on video elements. Is it possible to bind this context to document inside Array.prototype.forEach method when deleting eventListener? If I saved the necessary context into new variable then I got succeed. But then I'm trying bind, my IDE is saying 'Potentially invalid usage of this'.

Code snippet:

Gallery.prototype = {
  setCurrentPicture: function(currentPhoto) {
        var self = this;

        Array.prototype.forEach.call(container.children, function(item) {
          if (item.tagName === 'VIDEO') {
            document.removeEventListener('click', self.togglePlayVideo);
          }
          if (item.tagName === 'VIDEO' || item.tagName === 'IMG') {
            container.removeChild(item);
          }
        });

       if (pictures[currentPhoto] instanceof Video) {
          var video = document.createElement('video');
          ....
          document.addEventListener('click', self.togglePlayVideo);
        }
    },
},

 togglePlayVideo: function(e) {
      if (e.target.tagName === 'VIDEO') {
        return e.target.paused ? e.target.play() : e.target.pause();
      }
    }
}

In case of addition document.addEventListener('click', self.togglePlayVideo); I can use bind instead of self: document.addEventListener('click', this.togglePlayVideo.bind(this).

Can I use bind in removeEventListener? document.removeEventListener('click', this.togglePlayVideo.bind(this);

Upvotes: 0

Views: 148

Answers (2)

Andrew
Andrew

Reputation: 127

I should add bind this to Array.prototype.forEach.call instead of implementing this to callback function.

 Array.prototype.forEach.call(container.children, function(item) {
          if (item.tagName === 'VIDEO') {
            document.removeEventListener('click', this.togglePlayVideo);
          }
          if (item.tagName === 'VIDEO' || item.tagName === 'IMG') {
            container.removeChild(item);
          }
        }.bind(this));

Thanks to all for the answers.

Upvotes: 0

Denis
Denis

Reputation: 747

The best for you is to get familiar with what is "this" and what is "bind". You can do this by reading this brilliant description

and second part

To cut a long story short - when you declare a function with "this" like this:

  1. in defaul case "this" will be the Window object

    var f = function(){return this;};

    f(); //will return Window object

  2. when an object owns a link to your function like this:

    var f = function(){return this;};

    var o = {func: f};

    o.func(); // will return the "o" object becuase "this" becomes o object

  3. when you use explicit binding like call/apply you'll set this to a particular value like this:

    var f = function(){return this;};

    var obj = {};

    f.call(obj); // will return your obj

  4. and there is so called "hard binding". used like this:

    var f = function(){return this;};

    var obj1 = {num:1};

    var obj2 = {num:2};

    var boundFunc = f.bind(obj1); //now it's hardbound to obj1

    //let's try to switch this by using "call"

    boundFunc.call(obj2); // will return obj1, because it's hardbound

  5. and this can be affected when function is called with "new" keyword:

    f = function(){ this.a =1; this.b =2; };

    var obj = new f(); // will set brand new object as "this" and return it

    //so we will get {a:1, b:2} object as a result

Upvotes: 2

Related Questions