Alex
Alex

Reputation: 13

How can I JavaScript add a value to the "onClick" event if the function name is in a string?

THIS ISSUE HAS BEEN FIXED

Note: You can read the solution and the evaluation that I carried out thanks to the answers after the explanation of the problem.

I am trying to create a small JavaScript script in order to solve some needs for users (a UserLand Script).

The operation is very simple, and to make it as simple as possible, I need to add some elements to the DOM, with CSS, text, functions etc.

So that the script knows what elements to create, what CSS to apply, what text, etc. I have created an object that has the following form:

    options: {
      /* Button */
      'button': {
        'tagName'   : 'button',
        'innerHTML' : 'X',
        'css'       : {
          'position'          : 'fixed',
          'z-index'           : '9999999',
          'bottom'            : '20px',
          'left'              : '20px',
          'background-color'  : 'black',
          'cursor'            : 'pointer',
          'color'             : 'white',
          'width'             : '60px',
          'height'            : '60px',
          'border'            : 'none',
          'border-radius'     : '50%',
          'font-weight'       : 'bold',
          'font-size'         : '1.5rem'
        },
        'onClick'   : 'objectName.method()'
      }
    }

My script executes the following code through a loop:

    createElement: (element) => {
      
      /* Create element */
      const createElement = document.createElement( element.tagName );
      /* Add innerHTML */
      createElement.innerHTML = element.innerHTML;
      /* Add CSS */
      objectName.addCSStoElement( createElement, element.css );
      /* Add onClick event*/
      createElement.addEventListener( 'click', element.onClick, false );
      /* Add to DOM */
      document.body.appendChild ( createElement );

    }

Everything runs correctly, except for the instruction:

createElement.addEventListener( 'click', element.onClick, false );

I guess it's because it is as a string, and this is how it is appended by JS to the "onClick" event.

Assuming I am not mistaken, how could I go about adding the name of the function so that it can be executed when clicking on the created element?

I hope I have expressed myself well and that my bad English does not lead to confusion.

Thanks for your time and your help, greetings to all!

Regards, Alex

SOLUTION:

I have realized that the information that I contributed when exposing my problem was not completely complete, for example, I did not indicate if it was a global or local object, I am very sorry.

It is a local Object, and, in this case, it is code that can be trusted since it is code that I myself am injecting into the browser every time it enters a website with the aim of favoring me in certain tasks.

Taking this into account, we can proceed, if in any case this is not your case, please take into account the information that the colleagues added in their answers, they are very interesting and important.

The changes I had to make are: First, modify, inside "options", the value that "onClick" had by the following:

'onClick'   : '(function(){objectName.method();})'

Finally, I modified the way in which the event to the HTML element is added by the following:

createElement.addEventListener( 'click',eval(element.onClick), false );

And, with these simple changes, I managed to solve and understand what the problem was!

Thank you very much to all who responded and gave me their help and time, I wish you the best!

Upvotes: 1

Views: 2989

Answers (3)

traktor
traktor

Reputation: 19301

You could add a click handler for the button by adding an onclick attribute and passing a code string for a call to the method supplied in the configuration object.

The example below adds the event object as call argument so the method can look at event.target and event.currentTarget for more information about what was clicked.

An example of using a button and omitting all other configuration details:

// dummy object for testing:
const object = {
   name: {
      method: function(event) {
         alert(`You clicked '${event.target.textContent}'`)
      }
   }
}

// Create a button and add an "onclick" attribute set to a code string.
// This example adds the click event object as an argument for the method call


const button = document.createElement("button");
button.textContent = "click me";
button.setAttribute("onclick", "object.name.method(event)");
document.body.appendChild(button);

This works by having the HTML parser convert the code string into a function object that gets called with a click event object as its argument, just as it would if you put an onclick attribute in HTML source.

Never do this with untrusted code but I assume the configuration data is coming from application code and not from user or unvalidated input.

Setting an attribute is not suited to adding multiple event handlers to a single element, but internally it creates a function that executes the code (string) supplied as the attribute value, and calls addEventListener for you.

Upvotes: 0

Rameez Usmani
Rameez Usmani

Reputation: 147

I have reproduced your example with required modifications and it is now working as expected. Function is being called on button click.

<html>
    <head><title>Example for string as function call</title></head>
    <body>
        <script type="text/javascript">
            let options = {
            /* Button */
            'button': {
                'tagName'   : 'button',
                'innerHTML' : 'X',
                'css'       : {
                    'position'          : 'fixed',
                    'z-index'           : '9999999',
                    'bottom'            : '20px',
                    'left'              : '20px',
                    'background-color'  : 'black',
                    'cursor'            : 'pointer',
                    'color'             : 'white',
                    'width'             : '60px',
                    'height'            : '60px',
                    'border'            : 'none',
                    'border-radius'     : '50%',
                    'font-weight'       : 'bold',
                    'font-size'         : '1.5rem'
                },
                //check how onClick method is changed
                'onClick'   : '(function(){console.log("hello");objectName.method();})'
            }
            };
            
            function createElement(element) {
                /* Create element */
                const createElement = document.createElement( element.tagName );
                /* Add innerHTML */
                createElement.innerHTML = element.innerHTML;
                /* Add CSS */
                objectName.addCSStoElement( createElement, element.css );
                /* Add onClick event*/
                //check how I am passing eval(element.onClick) instead of just element.onClick
                createElement.addEventListener( 'click',eval(element.onClick), false );
                /* Add to DOM */
                document.body.appendChild ( createElement );
            }

            createElement(options['button']);
        </script>
    </body>
</html>

As T.J. Crowder has pointed out: please make sure that objectName is a global variable

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074435

Since you're asking specifically about using a string to create an HTML event handler fro a string, and you know you can trust the conents of the string (that it doesn't have malicious content), you could use setAttribute:

createElement.setAttribute("onclick", element.onClick);

Live Example:

const objectName = {
    method() {
        console.log("objectName.method() called");
    }
};

const btn = document.createElement("button");
btn.textContent = "Click Me";
const onClick = "objectName.method()";
btn.setAttribute("onclick", onClick);
document.body.appendChild(btn);

Note that objectName must be a global variable, not local to the code doing this.


Alternatively, you can do it the way this question's answers show, which I wrote up below before realizing I should look for a duplicate (now un-dupehammered per comments):

Since you can trust the source of the string containing the code, you can use new Function to create a function using that code as its body:

createElement.addEventListener( 'click', new Function(element.onClick), false );

Don't do this with strings you can't trust.

Note that any variables the code refers to will have to be globals.

Live Example:

const objectName = {
    method() {
        console.log("objectName.method() called");
    }
};

const btn = document.createElement("button");
btn.textContent = "Click Me";
const onClick = "objectName.method()";
btn.addEventListener("click", new Function(onClick), false);
document.body.appendChild(btn);

If you need to refer to local variables, you can use eval (again: don't if you can't trust the source of the text! you're evaling!):

createElement.addEventListener( 'click', eval("(function() { " + element.onClick + "})"), false );

Live Example:

{ // A block so the code isn't at global scope
    const objectName = {
        method() {
            console.log("objectName.method() called");
        }
    };

    const btn = document.createElement("button");
    btn.textContent = "Click Me";
    const onClick = "objectName.method()";
    btn.addEventListener("click", eval("(function() { " + onClick + "})"), false);
    document.body.appendChild(btn);
}

Upvotes: 2

Related Questions