MauriceRandomNumber
MauriceRandomNumber

Reputation: 412

Function parameter evaluates to undefined

When dynamically creating an element of type select, there are two problems when setting the onclick method:

  1. It is impossible to simply set the onclick with element.onclick="updateInput(this.articleIndex)";

This results in a final HTML tag where no onclick is shown at all.

When set by e.setAttribute("onclick","updateInput(this.articleIndex)");, it does appear in the final HTML. And the updateInput method does get called.

  1. However the functionality seems to be broken, as the argument always evaluates to undefined

Here a simple example of my problems:

var selectElem = document.createElement("select");
selElem.id="articleSelector_"+this.articleIndex; 
console.log("the index of the article is " + this.articleIndex);
selElem.setAttribute("onclick","updateInput(this.articleIndex);");
//selElem.onclick="updateInput(this.articleIndex)"; //this does not work

The log shows the correct number. Inside the updateInput method, the argument is of value undefined instead of the number previously shown in the log.

Upvotes: 2

Views: 98

Answers (3)

Racil Hilan
Racil Hilan

Reputation: 25351

it is impossible to simply set the onclick with element.onclick="updateInput(this.articleIndex)";

What that code does is it assigns the string "updateInput(this.articleIndex)" to the onclick which makes no sense and certainly not what you want.

Even if you remove the quotes:

element.onclick = updateInput(this.articleIndex);

It is still incorrect because it assigns the result of the updateInput() function to the onclick which is again not what you want.

You need to assign a function name to the onclick like this:

element.onclick = updateInput;

However, this doesn't allow you to pass a parameter as you wish. To do so, you need to use an anonymous function:

element.onclick = function() {
    updateInput(this.articleIndex)
};

When set by e.setAttribute("onclick","updateInput(this.articleIndex)");, it does appear in the final HTML. And the updateInput method does get called.

This works because it sets the attribute onclick and it is a string type, so everything is correct. It is equivalent to using the anonymous function above. The only difference is this, which in this case refers to the element itself, while in the above code it depends on the context that the code appears in. That's why in this case the argument always evaluates to undefined because the select element doesn't have an articleIndex property.

Upvotes: 2

Ele
Ele

Reputation: 33726

The problem is the value of the context this when that element is clicked, the context this is not available anymore at that moment.

You have two ways to solve this problem:

  • You can use the function addEventListener to bind the event click, and bind the function/handler with the desired context this:

The function bind binds a specific context to a function.

selElem.addEventListener('click', updateInput.bind(this));

function updateInput() {
    console.log(this.articleIndex);
}
  • As you need a specific value, you can use data attributes. That way, you don't need to worry about the context this.
selElem.dataset.articleIndex = this.articleIndex;
selElem.addEventListener('click', function() {
  updateInput(this.dataset.articleIndex); // Here you can get that value.
});

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370739

Try attaching handlers with pure Javascript, and not with HTML, without onclick = "... (which is as bad as eval).

The this in your script refers to the calling context of the function - what is it?

You might want:

element.addEventListener('click', () => {
  updateInput(this.articleIndex);
});

(arrow functions retain the this of their surrounding scope)

Upvotes: 2

Related Questions