How to trigger a var after x actions - javascript

I have the following code. I want to trigger the action in function activityDetected(eventName) only after 100 click. How to do this ?
I know I have to put let a = 1; ++a but not sure where...
https://pastebin.com/SMsJsikE
const intervalTimeout = 2000;
//here is where code should be added. let a = 1; ++a...
function activityDetected(eventName) {
console.log(`Activity detected with the event name: ${eventName}!`);
clearInterval(activityTimeout);
activityTimeout = setInterval(recordNoActivity, intervalTimeout);
}
document.addEventListener('click', _ => {
activityDetected('click');
});

You need to declare a counter outside the function and up it by 1 when the eventName is 'click'. After that check for a % 100 and put whatever action you want to call every 100 clicks in there.
Look at the code example:
// For ease, change this to a smaller value to detect more often, higher to detect less often!
const intervalTimeout = 2000;
let a = 0;
// Here's our interval, setting up the initial capture of no activity
let activityTimeout = setInterval(recordNoActivity, intervalTimeout);
// A single function to handle the events we're listening to.
// clears the interval and restarts it, also tells us which event has cleared the interval!
//here is where code should be added. let a = 1; ++a...
function activityDetected(eventName) {
if(eventName == 'click'){
a++;
if(a%100 == 0){
// Trigger whatever you want to trigger after every 100 clicks
}
}
console.log(`Activity detected with the event name: ${eventName}!`);
clearInterval(activityTimeout);
activityTimeout = setInterval(recordNoActivity, intervalTimeout);
}
// Set listening events
document.addEventListener('keydown', _ => {
activityDetected('keydown');
});
document.addEventListener('click', _ => {
activityDetected('click');
});

As correctly pointed by others on this thread the variable a should be declared and defined outside the function but the reason why this approach would work is because of Closure
So when the function is getting invoked an execution context is created which contains
scopeChain - it contains variableObject + all parent execution context's variableObject
variableObject - it contains function arguments / parameters, inner variable and function declarations
this - the this context.
Thus the variable a values would be saved before invoking the function and the variable will keep incrementing.

Related

How do I export this function without its value being undefined? [duplicate]

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 6 days ago.
I want to export the function manualStrobeTimeout with the values especified within the manualBPM_text.addEventListener("change")'s scope. Right now, the console logs the export as undefined, meaning that the value doesn't change from when the variable was declared. How would I export the function with the values declared within that scope? Keep in mind that I cannot declare the function's values outside of the event listener, as this would interfere with the set intervals.
Here's the relevant module's code:
import { strobeActive } from "./onBtn.js"; // variable to check if ON button is active/pressed
// manual bpm variables definition and event listener
var manualBPM = 0;
var manualBPM_interval = 0;
const body = document.querySelector("body");
var ranTimes = 0;
// strobe duration variable definition and event listener
var duration = 100;
const slider = document.getElementById("MM-duration-slider");
slider.addEventListener("input", function() {
duration = slider.value;
}, false);
var manualBPM_text = document.getElementById("manual-value");
var manualStrobeTimeout;
if (strobeActive == true) {
manualBPM_text.addEventListener("change", function() {
clearInterval(manualStrobeTimeout); // so that old value doesn't interfere with new value
manualBPM = manualBPM_text.value;
manualBPM_interval = (60 / manualBPM) * 1000;
manualStrobeTimeout = function() {
// repeat once the interval expires
setInterval(function() {
// trigger strobe
body.classList.remove("bg-black");
body.classList.add("bg-white");
// kill strobe once the strobe duration expires
setTimeout(function() {
body.classList.remove("bg-white");
body.classList.add("bg-black");
}, duration);
ranTimes++;
console.log("BPM: " + manualBPM + " (source: " + BPMvalueSource + ") | Strobe duration: " + duration + "ms | " + "Times ran: " + ranTimes);
}, manualBPM_interval);
}
}, false);
}
export { manualBPM_text };
export { manualStrobeTimeout };
I want use the imported function in the following statement (on a seperate JS file):
if (BPMvalueSource == "manual") {
manualStrobeTimeout();
console.log(manualStrobeTimeout()); // returns "undefined"
} else if (BPMvalueSource == "tap") {
tapBPM_strobe();
}
I have tried using window. to set the function as global, but to no avail. I have also made sure that I am importing and exporting correctly, and also tried using a dynamic import. This also did not work. Both JS files have the attribute type="module" especified.
The clearInterval was not clearing the interval.
The interval would not even be able to run since the wrapping function was never called
You cannot export non-constat values
let lastManualBPMValue;
if (strobeActive == true) {
manualBPM_text.addEventListener("change", function() {
lastManualBPMValue = manualBPM_text.value;
}, false);
}
export { manualBPM_text };
let manualStrobeTimeout;
export function manualStrobe() {
clearInterval(manualStrobeTimeout); // so that old value doesn't interfere with new value
manualBPM_interval = (60 / lastManualBPMValue) * 1000;
manualStrobeTimeout = setInterval(function() {
...
}, manualBPM_interval);
}
The function that is assigned to the manualStrobeTimeout variable has no return statement in it, and as such will always return undefined when executed.
If you want to check that manualStrobeTimeout is assigned a value, you may want to do console.log(manualStrobeTimeout) instead, which will log the value assigned to the variable.
With console.log(manualStrobeTimeout()) you are executing the manualStrobeTimeout function, and its return value will be fed to console.log. The return value of a JavaScript function is undefined by default.
Keep in mind that manualStrobeTimeout will not be a function unless the "change" event listener has triggered.
You didn't directly ask about this, but you may also want to look at the documentation for clearInterval(). The code is attempting to call clearInterval on a function, not an interval ID.

How to pass React state variable into setInterval function

I created a drag and drop menu and so far everything works, however, I have run into a small coding problem for my last step. I need to get the menu of items to automatically scroll when the dragged item is towards the bottom of the container (or top). I decided to structure it so that when the cursor dragging the item falls below a specific range towards the bottom, a scroll function will trigger and continually scroll down at a pace I can set. In order to achieve this I set up a setInterval function and will continually call a scrollBy function.
My issue is that In order to stop the setInterval and call clearInterval, I need to know when the position of the cursor is no longer in that range. Every time the function is called in setInterval, it takes a snapshot of the current state variables. Instead of updating the state of those variables (yAxis position) within the function, it is stuck on the first value it gets.
The yPoisiton is obtained with the window.event.clientY from another component.
document.onmousemove = (e) => {
if (dragging.state) {
var newYPos = window.event.clientY;
setYPos(newYPos);
handleScroll(newYPos, dragging.state);
The rest of the code goes as follows:
const menuContainer = useRef();
const [scrolling, setScrolling] = useState(false);
const [yPos, setYPos] = useState(null);
const [dragging, setDragging] = useState({
state: false,
index: null,
y: null,
height: null,
});
function continueScrolling() {
console.log("continueScrolling function");
console.log(yPos); // Same value even after updating via setYPos)
const date = new Date();
console.log(date.toLocaleTimeString()); //Different values in accordance to the time
}
console.log("Index.js yPos");
console.log(yPos); // Different values every render when dragging
const handleScroll = (draggingYPos, isDragging) => { // This is triggered onMouseMove in another component
if (
draggingYPos >
menuContainer.current.getBoundingClientRect().bottom - 100 &&
isDragging
) {
setScrolling(true); // On the first cycle scrolling == false and executes the setInterval function. Each subsequent call is then skipped.
if (scrolling) {
return;
} else {
var intr = setInterval(function () {
continueScrolling(); // Different method of testing and trying to get the current yPos in the function
//Commented but shows a bit of what I'm trying to accomplish
// if (
// !(
// yPos >
// menuContainer.current.getBoundingClientRect().bottom - 100 &&
// dragging.state
// )
// )
// clearInterval(intr);
}, 1000);
Please let me know if there is more information you need or anything else I can clarify.
This is a temporary solution I found to my problem, however, I would still like to know a more proper and robust solution if anyone has one, as well as information as to the scope of functions, React state, and how to pass them between one another.
I realize that it is not recommended to mutate states directly, however, this is my only current working solution. If I create a temporary value and pass in the setInterval, it will call setInterval a second time and will still be active after clearing it.
If I declare a global const variable, I cannot reassign the variable with setInterval. When I declared a global variable with var, the setInterval function continued even after I cleared it.
Adjusted code:
const [scroll, setScroll] = useState(null);
const handleScroll = (draggingYPos, isDragging) => {
if (
draggingYPos >
menuContainer.current.getBoundingClientRect().bottom - 100 &&
isDragging
) {
setScrolling(true);
if (scrolling) {
return;
} else {
var count = 0;
if (
!(
yPos > menuContainer.current.getBoundingClientRect().bottom - 100 &&
dragging.state
)
) {
setScroll(
setInterval(function () {
count += 1;
console.log(count);
}, 1000)
);
}
}
} else {
console.log("Not Scrolling");
setScroll(clearInterval(scroll));
setScrolling(false);
}
};

How to properly set a new value for a variable into a javascript class

I would like to check the value of a variable inside my requestAnimationFrame function.
I implement this value on the scroll wheel event. It seems to work but when I check the value inside my RAF fonction, it's still set at its first value.
I wonder how can I properly set my this.deltaY value ? Thank you very much
class MyClass
{
constructor ()
{
this.deltaY = 0;
}
init ()
{
this.playRAF();
window.addEventListener('wheel', function(e) {
this.deltaY = e.deltaY;
console.log(this.deltaY);
// it returns a number
});
}
playRAF ()
{
this.leRaf = requestAnimationFrame(this.playRAF.bind(this));
console.log(this.deltaY);
// it returns 0 all the time despite the function above :(
}
}
//////
const foo = new MyClass();
foo.init();
This is an this issue
pay attention to this block, in the event listener callback, this is window, instead of the class instance as you thougt it is.
window.addEventListener('wheel', function(e) {
this.deltaY = e.deltaY;
console.log(this.deltaY);
// it returns a number
});
try to check the value window.deltaY, you will find that it's your logged number, or try a breakpoint debugging, the this in the function(e){} is Window object.
turn it into
window.addEventListener('wheel',(e) => {
this.deltaY = e.deltaY;
console.log(this.deltaY);
// it returns a number
});
will solve this problem, as the arrow will not create a function block, this will be the class instance as expected.

How To Avoid Event Listeners Shared Scope

I'm making a bot for a game to move to a block and mine it by listening for events and I noticed something weird when trying to run async functions in a event listener. When the aysnc function finishes it runs a call back function using a variable that is declared in the second event listener called pos but once the function is called the event has already happened again. The call back function uses the new value from the event from the first listener instead of the old value from when the async function was called in the second listener. I was wondering how I could avoid this problem.
This the first event listener:
bot.client.on('block_change', packet => {
let id = packet.type
let loc = packet.location;
let pos = new Vec3(Math.floor(loc.x), Math.floor(loc.y), Math.floor(loc.z))
let block = new Block(Math.floor(id / 16), 5, id % 16)
setBlock(pos, block);
});
This the second listener:
bot.client.on('block_change', (packet) => {
loc = packet.location
block = new Block(Math.floor(packet.type/16),0,packet.type%16)
pos = new Vec3(loc.x,loc.y,loc.z)
if (packet.type === 16) {
bot.navigate.to(pos,{endRadius:3, onArrived: ()=>{bot.dig(pos)}});
}
});
both listeners need to use the keyword "let" or "const" to change the global variables
e.g.
bot.client.on('block_change', (packet) => {
let loc = packet.location
let block = new Block(Math.floor(packet.type/16),0,packet.type%16)
let pos = new Vec3(loc.x,loc.y,loc.z)
if (packet.type === 16) {
bot.navigate.to(pos,{endRadius:3, onArrived: ()=>{bot.dig(pos)}});
}
});

Get value from event listener and apply it to global variable

I apologize if that title is confusing, so let me explain. I'm creating a Hangman Game to improve my HTML/CSS/Javascript skills. The HTML/CSS is all complete and functions, but the issue is with my javascript.
Basically my javascript works like this:
A user clicks the start button, the game function begins. Once the game begins, the user can type a letter and click 'submit.' The user's input should update the global variable, userInput but it does not. If my understanding is right (from looking around stack overflow unsure of the exact phrasing of my problem), this is something that cannot be done because of the async and scope nature of javascript. Is this correct? If not, is there any advice I can get on making it work?
let userInput = '';
function getValue(e) {
e.preventDefault();
userInput = document.getElementById('inputText').value.toLowerCase();
form.addEventListener('click', form.reset());
//console.log(userInput); <-- works
}
function hangMan(e) {
submit.addEventListener('click', getValue);
const word = getWords(words);
const MAX_ATTEMPTS = word.length + 3;
console.log(userInput) <-- does not
let attempt = 0;
let win = false;
dashedWord = getWordBlanks(word);
wordBlanks.innerHTML = dashedWord;
while (attempt <= MAX_ATTEMPTS && win === false) {
let checkedWord = checkAndReplaceCharacter(word, dashedWord, userInput);
if (checkedWord === word) {
win = true;
} else {
wordBlanks.innerHTML = checkedWord;
}
attempt += 1;
}
}
function main() {
startButton.addEventListener('click', hangMan);
}
main();
The async and scope seem to be fine.
You can change a global variable from inside an event listener.
console.log(userInput) <-- does not displays an empty string because at the time it executes userInput still holds the default value let userInput = ''; i.e. the whole hangMan(e) method is executed before any click on the submit button so the listener is not yet executed.
Keep in mind, though. If you actually allow the form to be submited, you reload the papge and loose all variables.

Categories

Resources