This is an old topic and @jfriend00 did a really nice work explaining the basics behind closure function calls.
Lets update the snippets to ES6 and also use another technique that might help us to overcome this kind of scenarios.
First lets define a function handler this function will receive two parameters fnc and val where the fnc is the custom function that will handle the event usually is names on + theEventName in this case onClickEvent. There is nothing new here. Now let's define the onclick listener on the element red and it will be called clickEventHandler like this:
red.onclick = clickEventHandler
As we noticed clickEventHandler does not have any parameter Teo, how can I send the event and the index value to the handler?
Good question, first let's call the handler function to create our custom onClickEvent handler like this:
handler(onClickEvent, index)
But Teo, onClickEvent also does NOT have parameters, How we can send the closure function onClickEvent, the event produced by onclick and index value?
Patience my young padawan. First which function will handle the event? In our scenary will be onClickEvent so lets define this function to receive the event e and the index value v:
function onClickEvent(e, v) {
console.log(`${e.currentTarget.className} -> [x: ${e.x}, y: ${e.y}] | val: ${v}`)
}
Now lets implement our handler function. This function will return a closure function definition that will execute the function defined in fnc, also will merge the parameters received by the closure function and the val paramenter like this:
function handler(fnc, val) {
return (...params) => {
const ctx = this
const arg = [...params, val]
fnc.apply(ctx, arg)
}
}
To call the fnc we used apply(). This method calls the specified function with a given a context ctx, and arg as arguments provided as an array. Therefore, the fnc is executed usint the closure function context ctx.
This is a working snippet for this scenario.
const red = document.querySelector('.red')
const blue = document.querySelector('.blue')
function handler(fnc, val) {
return (...params) => {
const ctx = this
const arg = [...params, val]
fnc.apply(ctx, arg)
}
}
function onClickEvent(e, v) {
console.log(`${e.currentTarget.className} -> [x: ${e.x}, y: ${e.y}] | val: ${v}`)
}
const index = 50
// Define the handler function
const clickEventHandler = handler(onClickEvent, index)
// Call the debounced function on every mouse move
red.onclick = clickEventHandler
blue.addEventListener('click', clickEventHandler)
.boxes {
width: 100%;
height: 120px;
display: flex;
flex-flow: row nowrap;
}
.red {
background: red;
}
.blue {
background: blue;
}
.box {
width: 50%;
display: flex;
justify-content: center;
align-items: center;
color: white;
}
<div class='boxes'>
<div class='red box'>Click Me</div>
<div class='blue box'>Not ready</div>
</div>
Now let's apply this concept to your scenario:
const red = document.querySelector('.red')
const blue = document.querySelector('.blue')
red.clicked = false
let clickEventHandler
function handler(fnc, val) {
return (...params) => {
const ctx = this
const arg = [...params, val]
fnc.apply(ctx, arg)
}
}
function onRedClickEvent(e) {
if (red.clicked) return
red.clicked = true
red.textContent = 'Clicked'
blue.textContent = 'Ready'
console.log(`${e.currentTarget.className} -> blue is ready`)
// Define the handler function
clickEventHandler = handler(onClickEvent, red)
// Call the debounced function on every mouse move
blue.addEventListener('click', clickEventHandler)
}
function onClickEvent(e, elem) {
red.clicked = false
red.textContent = 'Click Me'
blue.textContent = 'Not Ready'
console.log(`${e.currentTarget.className} -> [x: ${e.x}, y: ${e.y}] | elem: ${elem.className}`)
blue.removeEventListener('click', clickEventHandler)
}
red.onclick = onRedClickEvent
.boxes {
width: 100%;
height: 120px;
display: flex;
flex-flow: row nowrap;
}
.red {
background: red;
}
.blue {
background: blue;
}
.box {
width: 50%;
display: flex;
justify-content: center;
align-items: center;
color: white;
user-select: none;
}
<div class='boxes'>
<div class='red box'>Click Me</div>
<div class='blue box'>Not ready</div>
</div>
e.target/e.currentTarget?