1

I have a javascript function that's supposed to toggle an animation when clicked by calling another function outside of it.

function MyFunction(id) {
    var target = document.getElementById(id);
    var on = true;

    this.startMe = function() {
        //animation code
        on = true;
    }
    this.stopMe = function() {
        //animation code
        on = false;
    }
    this.toggleMe = function() {
        if (on) this.stopMe();
        else this.startMe();
    }
    target.addEventListener('click', function() {
        this.toggleMe();
    }, false);
}

The problem lies in the toggleMe and addEventListener functions. "this" refers to the function itself and not the one containing it, which is what I need it to reference. How can I work around this?

1
  • Assign the outer this to a variable, e.g. var MyFunctionId = id; and use it inside the addEventListener. Commented Jan 24, 2014 at 15:38

2 Answers 2

3

The easy fix is to use a closure variable as given below

function MyFunction(id) {
    var self = this;
    var target = document.getElementById(id);
    var on = true;

    this.startMe = function () {
        //animation code
        on = true;
    }
    this.stopMe = function () {
        /animation code
        on = false;
    }
    this.toggleMe = function() {
        if (on) this.stopMe();
        else this.startMe();
    }
    target.addEventListener('click', function() {
        //this refers to the element here not the instance of MyFunction
        //use a closure variable
        self.toggleMe();
    }, false);
}

Another solution is to pass a custom execution context to the callback using $.proxy() - you can use Function.bind() also but not supported in IE < 9

function MyFunction(id) {
    var target = document.getElementById(id);
    var on = true;

    this.startMe = function () {
        //animation code
        on = true;
    }
    this.stopMe = function () {
        //animation code
        on = false;
    }
    this.toggleMe = function () {
        if (on) this.stopMe();
        else this.startMe();
    }
    //use Function.bind() to pass a custom execution context to 
    target.addEventListener('click', jQuery.proxy(function () {
        // this refers to the element here not the instance of MyFunction
        //use a closure variable
        this.toggleMe();
    }, this), false);
}

Also use .click()/on('click') to register the click handler instead of addEventListener

    $(target).on('click', jQuery.proxy(function () {
        // this refers to the element here not the instance of MyFunction
        //use a closure variable
        this.toggleMe();
    }, this), false);
Sign up to request clarification or add additional context in comments.

2 Comments

Should be applied within toggleMe() as well, no?
Thanks a bunch, I never thought of creating a variable to hold the original function. I'll mark as the answer when available.
2

Simply add another variable with a reference to this but with a different name; then you can use that in your functions.

function MyFunction(id) {
    var self = this;

    var target = document.getElementById(id);
    var on = true;

    this.startMe = function() {
        on = true;
    }

    this.stopMe = function() {
        on = false;
    }

    this.toggleMe = function() {
        if (on) self.stopMe();
        else self.startMe();
    }

    target.addEventListener('click', function() {
        self.toggleMe();
    }, false);
}

My personal preference is to take it even one step further and continue to use self everywhere that makes sense:

function MyFunction(id) {
    var self = this;

    var target = document.getElementById(id);
    var on = true;

    self.startMe = function() {
        on = true;
    }

    self.stopMe = function() {
        on = false;
    }

    self.toggleMe = function() {
        if (on) self.stopMe();
        else self.startMe();
    }

    target.addEventListener('click', function() {
        self.toggleMe();
    }, false);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.