I need to dynamically pass in a function name that will be added as an onclick event handler as part of a dynamic UI creator. Most of the function is easy but I can't work out how to turn the string function name into the function that is bound to the event handler.
I've tried things like:
// Add event handlers
Object.keys(compToAdd.events).forEach( (type) => {
newEl.addEventListener( type, Function.prototype.bind( compToAdd.events[type] ) )
})
But that doesn't work.
Also tried:
window.mycb = function() {
console.log('>>>> hello >>>>')
}
// ...
Object.keys(compToAdd.events).forEach( (type) => {
newEl.addEventListener( type, window['mycb'] )
})
Using window['mycb']()
immediately executes the fn when applied to the event listener which is obviously not correct. Without ()
, nothing happens when the click event fires.
CodePudding user response:
A simplest and arguably best approach would be loading all your callback functions into a single object, versus create them in global scope:
const compToAdd =
{
events:
{
click: "onClick",
mousedown: "onMouseDown",
mouseup: "onMouseUp",
}
}
const myCallbacks =
{
onClick: function(e)
{
console.log("onClick type:", e.type)
},
onm ouseDown: function(e)
{
console.log("onMouseDown type:", e.type)
},
onm ouseUp: "this is not a function"
}
// ...
Object.keys(compToAdd.events).forEach( (type) => {
const callback = myCallbacks[compToAdd.events[type]];
if (callback instanceof Function) //make sure it's a function
newEl.addEventListener( type, callback )
})
<div id="newEl" style="height: 100vh">click here</div>
P.S.
your second example works fine though. If it executed without ()
it means something triggered the event.
CodePudding user response:
I have managed to find a couple of possible answers. However, I don't know that I like either of them and I am sure there are better ones.
- Using
eval
// Add event handlers
Object.keys(compToAdd.events).forEach( (type) => {
// Add the event listener - hate eval but it is the only way I can get it to work
try {
newEl.addEventListener( type, (evt) => {
eval(`${compToAdd.events[type]}(evt)`)
} )
} catch (err) {
console.error(`Add event '${type}' for element '${compToAdd.type}': Cannot add event handler. ${err.message}`)
}
})
- Using
setAttribute
instead ofaddEventListener
// Add event handlers
Object.keys(compToAdd.events).forEach( (type) => {
if (type.toLowerCase === 'onclick' || type.toLowerCase === 'click') type = 'click'
// Add the event listener
try {
newEl.setAttribute( type, `${compToAdd.events[type]}()` )
} catch (err) {
console.error(`Add event '${type}' for element '${compToAdd.type}': Cannot add event handler. ${err.message}`)
}
})
I'm preferring (1) since it seems a bit more flexible and allows the use of pre-defined functions in any context reachable from the context when setting the event listener. But it is far from ideal.