Home > OS >  How to bind an event callback passed as a string parameter?
How to bind an event callback passed as a string parameter?

Time:04-17

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.

  1. 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}`)
            }
        })
  1. Using setAttribute instead of addEventListener
        // 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.

  • Related