jenlky
jenlky

Reputation: 396

How to use this keyword?

Hi I'm making a pomodoro clock. I want to allow the timer to increase or decrease every 100 milliseconds when the user holds down the button. The running conditions for mousedown and clickUpdate are very similar.

The entire code of clickUpdate relies on using this keyword to achieve that goal. But how can I let setInterval inherit or have access to this keyword? This referring to the button object that mousedown is a method of.

https://codepen.io/jenlky/pen/ypQjPa?editors=0010

var timer;
const session = document.getElementById("session");
const breaktime = document.getElementById("break");
const mins = document.getElementById("mins");
const secs = document.getElementById("secs");

function clickUpdate () {
    // if data-action = increase and its under session-input, increase session.value else increase breaktime.value
    if (this.dataset.action === "increase") {
        if (this.parentElement.className === "session-input") {
            // if session.value is 60 mins, this increase click changes it to 1 min
            if (session.value === "60") {
                session.value = 1;
            } else {
                session.value = Number(session.value) + 1;
            }
            mins.innerText = session.value;
            // if breaktime.value is 60 mins, this increase click changes it to 1 min
        } else {
            if (breaktime.value === "60") {
                breaktime.value = 1;
            } else {
                breaktime.value = Number(breaktime.value) + 1;
            }
        }
    }

    // if data-action = decrease and its under session-input, decrease session.value else decrease breaktime.value
    if (this.dataset.action === "decrease") {
        if (this.parentElement.className === "session-input") {
            // if session.value is 1 min, this decrease click changes it to 60 mins
            if (session.value === "1") {
                session.value = 60;
            } else {
                session.value = Number(session.value) - 1;
            }
            mins.innerText = session.value;
            // if breaktime.value is 1 min, this decrease click changes it to 60 mins
        } else {
            if (breaktime.value === "1") {
                breaktime.value = 60;
            } else {
                breaktime.value = Number(breaktime.value) - 1;
            }
        }
    }
    console.log(this);
}

// Problem is how can I let clickUpdate or setInterval(function(){},100) inherit this
// setInterval's function doesn't seem to inherit or have any parameters
// I'm not sure how forEach thisArg parameter works, or how to use bind, or how to use addEventListener last parameter
function mousedown() {
    var obj = this;
    timer = setInterval(clickUpdate, 100);
}

function mouseup() {
    if (timer) {
        clearInterval(timer);
    }
}

const buttons = Array.from(document.getElementsByClassName("symbol"));
mins.innerText = session.value;
//buttons.forEach(button => button.addEventListener("click", clickUpdate));
buttons.forEach(button => button.addEventListener("mousedown", mousedown));
buttons.forEach(button => button.addEventListener("mouseup", mouseup));
console.log(session);

Upvotes: 1

Views: 76

Answers (2)

JLRishe
JLRishe

Reputation: 101690

The this value within functions defined using function () ... is usually dependent on how the function is called. If you call a.myFunction() then this within the function will be a reference to a. If you call myFunction(), then this will either be undefined or the global object depending on whether you are using strict or sloppy mode.

Usually the most straightforward way to get a callback function to use a particular this value is to use .bind(n). This basically creates a wrapped version of the original function, with the this value locked in as n

setInterval(clickUpdate.bind(this), 100);

Then again, my preference would be to not use this at all. It's confusing and has wacky behavior that often requires all sorts of contrived workarounds (as you have experienced). You could just as easily pass in the value as a parameter here:

function mousedown(e) {
    timer = setInterval(clickUpdate, 100, e.target);
}

Upvotes: 2

arpitshah101
arpitshah101

Reputation: 9

One trick is to create a separate reference (either global or at least on a common ancestor scope) holding the reference to the "this" reference from the level you want.

Once you do that, you can refer to it within the setInterval function.

Example:

var scope = this;
this.name = 'test';
setInterval(function() {
    console.log(scope.name); // should print 'test' every 1 second
},1000);

Upvotes: 0

Related Questions