Reputation: 13
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
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
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
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 eval
ing!):
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