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
? – Stoicism