Reputation: 35
I have two javascript functions, one which attaches an eventhandler to an entire class of elements and the other which is called when the event handler is activated:
function attachDefinition(obj) {
var classArr = document.getElementsByClassName("flashcards");
for (let i = 0, len = classArr.length; i < len; i++) {
classArr[i].addEventListener('click', cardClicked);
}
}
function cardClicked(obj) {
console.log(this.id);
console.log(obj);
var img = document.createElement('img');
img.src = 'https://www.wordnik.com/img/wordnik_badge_a2.png';
document.getElementById(this.id).innerHTML = '';
document.getElementById(this.id).appendChild(img);
}
The above function runs without error on click. this.id
logged to the console displays the id of the div
element being clicked and obj logs the global object.
This is fine however I need to pass in an object created in a different function in the program. The above code only needs the obj argument added to addEventListener
call but when I do that everything falls apart. This code:
function attachDefinition(obj) {
var classArr = document.getElementsByClassName("flashcards");
for (let i = 0, len = classArr.length; i < len; i++) {
classArr[i].addEventListener('click', cardClicked(obj)); //only thing I've changed!
}
}
function cardClicked(obj) {
console.log(this.id);
console.log(obj);
var img = document.createElement('img');
img.src = 'https://www.wordnik.com/img/wordnik_badge_a2.png';
document.getElementById(this.id).innerHTML = '';
document.getElementById(this.id).appendChild(img);
}
Now successfully console logs the passed in object but the line logging this.id
is now undefined and I get "Unable to set property 'innerHTML' of undefined or null reference" on the innerHTML line.
I'm struggling to understand why passing in an argument would change this
and how I can go about fixing it.
Upvotes: 0
Views: 63
Reputation: 1075219
If we assume that your
classArr[i].addEventListener('click', cardClicked(obj);
is really
classArr[i].addEventListener('click', cardClicked(obj));
// Note ---------------------------------------------^
that's calling cardClicked
and passing its return value into addEventListener
, exactly the way foo(bar())
calls bar
and passes its return value into foo
.
But addEventListener
expects a function in the second argument, and cardClicked
doesn't return a function.
Instead, since you're relying on this
referring to the clicked element inside cardClicked
, you either need:
classArr[i].addEventListener('click', function() {
cardClicked.call(this, obj);
});
or
classArr[i].addEventListener('click', cardClicked.bind(classArr[i], obj));
The first works by responding to a click by calling cardClicked
such that the this
it sees is the same as the this
the anonymous function receives.
The second works by using Function#bind
to create a new function that, when called, will call cardClicked
with this
set to the avlue of classArr[i]
and its first argument set to obj
.
Upvotes: 2
Reputation: 3536
when you assign a function to an event as action on trigger then the function's this
would point to element which event fire on. your first implementation uses this advantage and then you can use this
in your function to refer to related element.
But the problem in second implementation is that the function is called instead of assignment.
If you want to send additional data to you callback you can use function call
or apply
:
element.addEventListener('click', function(){
var obj = { addtional: 'data' };
myCallback.call(this, obj);
})
function myCallback (obj){
console.log(this.id, obj);
}
The call() method calls a function with a given this value and arguments provided individually.
The apply() method calls a function with a given this value and arguments provided as an array (or an array-like object).
Upvotes: 1
Reputation: 21672
Change your classArr[i].addEventListener('click', cardClicked(obj);
to this instead:
classArr[i].addEventListener('click', function() {
cardClicked(obj);
});
First off, you're missing a )
in the original. Additionally, you need to create an anonymous function when passing parameters in setInterval, otherwise the function in question will execute immediately upon reading.
Upvotes: 1