Reputation: 131
While playing with a script to automatically add event listeners, came to a situation when a varaible let activeModal;
declared inside a function remains "alive" after function execution but a variable created inside another function let currentPlayTime;
does not live after function execution. Making the let currentPlayTime;
variable global, as expected, obviously works.
My main question is why does variable let activeModal;
"survive" after the function as finished?
Second question, how would I go about avoiding making let currentPlayTime;
global?
I suspect it has to do with Scope, lexical environment or Execution Context, but so far what I've read about such topics has left me a little puzzled. Regards.
//THIS WORKS
function addModalHandlers()
{
// Get the modal
let modalsCollection = document.getElementsByClassName("modal");
let btn = document.getElementsByClassName('stations-pointer');
let span = document.getElementsByClassName("close");
let modals = [];
let modalsContents = []
let activeModal; //<----RELEVANT POINT
console.log("modalsCollection");
console.log(modalsCollection);
//convert HTML Collection to array for later using Array.includes() method
for (let i=0;i<modalsCollection.length; i++)
{
modals[i] = modalsCollection[i]
// modals[i].style.opacity = "0";
}
// Attach event listeners to buttons to open the modals
for (let i=0;i<btn.length;i++)
{
btn[i].onclick = function() {
setTimeout(function (){
modals[i].style.display = "block";
console.log("TIMEOUT");
},1200);
activeModal = modals[i];
}
(...)
}
//THIS WORKS
let currentPlayTime;
function toggleAnimation()
{
console.log(path.style.transition);
if (path.style.transition == 'none 0s ease 0s')
{
toggleIcon()
console.log("currentPlayTime on play: "+currentPlayTime);
wavesurfer.play(currentPlayTime);
compatibleTransition(wavesurfer.getDuration() - currentPlayTime );
path.style.strokeDashoffset = 0;
}
else
{
toggleIcon()
path.style.strokeDashoffset = window.getComputedStyle(path).strokeDashoffset;
path.style.transition = 'none';
currentPlayTime = wavesurfer.getCurrentTime();
console.log("currentPlayTime on pause: "+currentPlayTime);
wavesurfer.pause();
}
}
//DOES NOT WORK
function toggleAnimation()
{
let currentPlayTime; //<---- SAME RATIONALE AS addModalHandlers() ABOVE BUT WORKS NOT
console.log(path.style.transition);
if (path.style.transition == 'none 0s ease 0s')
{
toggleIcon()
console.log("currentPlayTime on play: "+currentPlayTime);
wavesurfer.play(currentPlayTime);
compatibleTransition(wavesurfer.getDuration() - currentPlayTime );
path.style.strokeDashoffset = 0;
}
else
{
toggleIcon()
path.style.strokeDashoffset = window.getComputedStyle(path).strokeDashoffset;
path.style.transition = 'none';
currentPlayTime = wavesurfer.getCurrentTime();
console.log("currentPlayTime on pause: "+currentPlayTime);
wavesurfer.pause();
}
}
Upvotes: 0
Views: 141
Reputation: 1538
When you do this
for (let i=0;i<btn.length;i++) {
btn[i].onclick = function() {
setTimeout(function (){
modals[i].style.display = "block";
console.log("TIMEOUT");
},1200
);
activeModal = modals[i];
}
You are basically telling the compiler to attach an event handler on each button. Also, compiler whenever this callback function is called make sure that the activeModal
is updated.
Now, you have created a dilemma for the compiler. It likes to keep things clean. Whever it gets the chance it cleans up unused references. Now, it can't do that for the case of activeModal
here because you have told it to update that value later when click handler is triggered. So, it keeps it and you are able to use it.
Now, in your toggleAnimation
function, the currentPlayTime
is not getting accessed outside the scope of its parent function unlike the activeModal
which is accessed by event handler which executed on the global scope. So, once the toggleAnimation
function is executed it just makes sense to clean up currentPlayTime.
If you want to avoid global variable, one way to achieve that is to make a function that returns another function creating a closure to safe keep currentPlayTime
.
function toggleAnimation() {
let currentPlayTime;
return function doDomething() {
if (path.style.transition == 'none 0s ease 0s') {
toggleIcon()
console.log("currentPlayTime on play: "+currentPlayTime);
wavesurfer.play(currentPlayTime);
compatibleTransition(wavesurfer.getDuration() - currentPlayTime );
path.style.strokeDashoffset = 0;
}
else {
toggleIcon()
path.style.strokeDashoffset = window.getComputedStyle(path).strokeDashoffset;
path.style.transition = 'none';
currentPlayTime = wavesurfer.getCurrentTime();
console.log("currentPlayTime on pause: "+currentPlayTime);
wavesurfer.pause();
}
}
}
Now, you need to do
const variableName = toggleAnimation();
variableName();
So here what happens is,
toggleAnimation
returns a function that would need currentPlayTime
when it execute.
This means the block enclosing the returned function will be necessary for its execution. Hence, the compiler will keep hold of this closure, so that, whenever it is called it could be executed.
Upvotes: 1