Select this or variable in function - javascript

The listenerForm() function is executed with addeventlistener. In the function I get the "this" object for get the dom element. Now I need use this function but I need pass the dom element in parameters. How to difference if I pass a parameter?
for (var i = 0; i < form.length; i++) {
form[i].addEventListener("click", listenerForm,true);
}
function listenerForm(form) {
console.log(form); //result MouseEvent {isTrusted: true}
console.log(this);//result fomr element.
}
listenerForm(domElement);
If I execute function with addeventlistener, I only need get this. If I pass the form parameter, I only need parameter.
I found solution passing parameters in addeventlistener, but I can't create a function in for loop.

A solution using currying and the bind method is this
form.forEach(function(item){
item.addEventListener('click', listenerForm.bind(null, item));
})
now the first argument of listenerForm is the element clicked and this refers to the global object

If you don't need access to the event object (which it seems you don't?) then call the function from the event handler instead of using it as event handler:
form[i].addEventListener("click", function() {
listenerForm(this);
}, true);
Now the parameter form will always refer to the DOM element.

try this:
for (var i = 0; i < form.length; i++) {
form[i].addEventListener("click", listenerForm(this), true);
}
function listenerForm(clickedFormElement) {
console.log(clickedFormElement);//result form element
}
listenerForm(clickedFormElement);// i don't know what you're trying to do here!!

Related

Pass a counter from a for loop into an event listener

I'm trying to pass the counter into an event listener's parameter function. I know it is possible using an anonymous function for the click event but wanted to use a named function instead. I can't seem to figure this out or if it is possible. Is there any way to do this?
function addKeys(){
for(let i = 0; i < keys.length; i++){
keys[i].addEventListener("click", testing)
}
}
function testing(e){
console.log(e)
console.log(i)
}
You can bind a parameter to the function:
keys[i].addEventListener("click", testing.bind(null, i))
Now i will be passed as the first parameter, before any others:
function testing(i, e) {
...
}

JQuery on click event listeners and functions, fire differently with different parameters?

I have elements that when I click on them I need to run a function and that function needs to know what element was clicked on.
Example A:
var elements = $(".config-cell");
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener("click", function() {
console.log("clicked");
});
}
When calling the function right there it works fine, however I don't know how to pass through the element to the function so it can use it.
So I tried using this method and found something strange.
Example B:
var elements = $(".config-cell");
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener("click", this.cellCrop());
}
When simply calling the function located elsewhere I noticed when loading the window it just automatically fires the function and then it doesn't even add the event listener so any time you click after that nothing happens.
Bottom line I would like to be able to pass through the current element being clicked on and have it fire a function. But I would like to know out of curiosity why method B works the way it does.
Learned that it knows which to use because 'forEach' has a callback with parameters
.forEach(function callback(currentValue[, index[, array]])
For instance: How does this call back know what is supposed to be
'eachName' and 'index'
var friends = ["Mike", "Stacy", "Andy", "Rick"];
friends.forEach(function (eachName, index){
console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
});
And can you do this with .addEventListener instead of setting it as a
var?
That being said is there a way to have it pass variables with your own function? Like:
var passthrough = 5;
$(".config-cell").on("click", function(passthrough) {
var five = passthrough;
console.log(five);
});
First, this.cellCrop() calls the function, this.cellCrop passes it. So if you wanted to set the listener it would have been
elements[i].addEventListener("click", this.cellCrop);
Now to actually get the element clicked on inside the function you can do it a couple ways.
Using currentTarget / target from the event object that is automatically passed to event listeners
elements[i].addEventListener("click", function(event){
//the actual element clicked on
var target = event.target;
//the element the event was set on, in this case whatever elements[i] was
var currentTarget = event.currentTarget;
});
//same using jQuery
$(elements[i]).click(function(event){
var target = event.target;
var currentTarget = event.currentTarget;
});
Using the this keyword
elements[i].addEventListener("click", function(event){
//this will refer to whatever elements[i] was
var target = this;
});
//same using jQuery
$(elements[i]).click(function(event){
var target = $(this);
});
This would apply the same with using object method:
obj = {
cellCrop:function(event){
var target = event.target;
/* etc */
},
someOtherMethod:function(){
//...
elements[i].addEventListener("click",this.cellCrop);
//or jQuery
$(elements[i]).click(this.cellCrop);
//...
}
};
obj.someOtherMethod();
How does this call back know what is supposed to be 'eachName' and 'index'
Because documentation for the forEach method tells the person using it how it is going to be called. So you write the callback based on that documentation.
For instance the callback for forEach usually takes the form of
function callback(currentValue[, index[, array]])
Which means inside forEach() it is going to call your callback in this fashion
function forEach(callback){
//`this` inside forEach is the array
for(let i=0; i<this.length; i++){
callback(this[i], i, this);
}
}
As for passing arbitrary data to the function, there are a few ways it can be done:
Wrap a call to a function in an anonymous function and explicitly call the function with more arguments
obj = {
cellProp:function(event,a,b,c){
var element = event.currentTarget;
}
}
//normal anonymous function
elements[i].addEventListener('click',function(e){
obj.cellProp(e,1,2,3);
});
//fat arrow function
elements[i].addEventListener('click',e=>obj.cellProp(e,1,2,3))
In the above a, b and c will respectively contain the values 1,2 and 3
You can also use methods like bind which will change the thisArg(see this question to see more on that) of the function but also pass in arguments to the function
obj = {
//note event comes last as bind, call, and apply PREPEND arguments to the call
cellProp:function(a,b,c,event){
//`this` will change depending on the first
//argument passed to bind
var whatIsThis = this;
var element = event.target;
}
}
//passing obj as the first argument makes `this` refer to
//obj within the function, all other arguments are PREPENDED
//so a,b, and c again will contain 1,2 and 3 respectively.
elements[i].addEventListener('click', obj.cellProp.bind(obj,1,2,3) );
In the case of jQuery, you can also pass data in using an object at the time of setting up the event:
obj = {
cellProp:function(event){
var data = event.data;
console.log(data.five);
}
}
jQuery(elements[i]).click({five:5},this.cellProp);
Try this : You can make use of $(this)
$(".config-cell").on("click", function(){
var clickedCell = $(this);// this refers to current clicked cell which can be used to get other details or write logic around it
});

"this" stops working when argument passed to function

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.
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.
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.
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);
}
function.prototype.call
The call() method calls a function with a given this value and arguments provided individually.
function.prototype.apply
The apply() method calls a function with a given this value and arguments provided as an array (or an array-like object).

jQuery click-function passing parameters

I would like to assign the jQuery click-function for all elements in an array. But additionally, I need to access the array from within the click-function. The source will hopefully make it clearer:
for (var i=0; i<mybox.myarray.length; i++) {
mybox.myarray[i].jqelem.click(function(event, mybox) {
event.preventDefault();
doStuffWithParameter(mybox);
});
}
// mybox is a JavaScript object (not jQuery-element!), myarray is an array, jqelem is a jQueryelement ala $("div.myclass");
The problem seems to be with function(event, mybox), apparently that doesn't work, i.e. mybox is unknown within the function. I think I 'kind of' understand why it cannot work this way, but how can this be achieved?
PS: I'm basically just doing it to save me from typing it manually for all array-elements.
Just remove the (useless) second callback function parameter named mybox.
If mybox is in scope in the outer block, it'll be in scope in the inner callback function too!
Should you need to know the appropriate value of i in the callback then you can do event registration-time binding:
for (var i=0; i<mybox.myarray.length; i++) {
mybox.myarray[i].jqelem.click({i: i}, function(event) {
// use "event.data.i" to access i's value
var my_i = event.data.i;
});
}
The map {i : i} corresponds with the eventData parameter in the jQuery .click() documentation.
When your click handler gets called, the first argument is the event data. jQuery doesn't pass in a second argument.
Update: Using closure to get to mybox object (notice I removed the 2nd argument)
for (var i=0; i<mybox.myarray.length; i++) {
mybox.myarray[i].jqelem.click(function(event) {
event.preventDefault();
// here's the trick to get the correct i
(function(item) {
return function() {
doStuffWithParameter(mybox.myarray[item]);
};
})(i);
// you can access any object from parent scope here
// (except i, to get to right i, you need another trick),
// effectively creating a closure
// e.g. doOtherStuff(myarray)
});
}
Read more on closures here: http://jibbering.com/faq/notes/closures/
and here: How do JavaScript closures work?
You can take help of jquery data attributes
for (var i=0; i<mybox.myarray.length; i++) {
mybox.myarray[i].jqelem.data("arrayIndex", i).click(function(event) {
event.preventDefault();
doStuffWithParameter(mybox.myarray[$(this).data("arrayIndex")]);
});
}

adding onfocus event handler to every element

I'm getting an undefined message on all of my handlers. I want to bind a handler to every element and want to output the value. What is wrong with this code? Thanks!
for (var i = 0; i < document.forms[0].elements.length; i++ ){
document.forms[0].elements[i].onfocus = test(this);
}
function test(ele){
alert(ele.value);
}
You need to assign a function. At the moment you are assigning the return value of test(window) which is undefined.
onfocus = test;
Then reference the element inside the function:
function test(){
alert(this.value);
}
You need to change both the assignment and the function, since the element will no longer be passed in as a parameter, like this:
for (var i = 0; i < document.forms[0].elements.length; i++ ){
document.forms[0].elements[i].onfocus = test;
}
function test(){
alert(this.value);
}
As an event handler, this inside test will refer to the element you're dealing with, so just get the value from that.
The alternative version of your current approach would be the same test method, but with an anonymous function wrapper to pass the element itself as a parameter:
for (var i = 0; i < document.forms[0].elements.length; i++ ){
ddocument.forms[0].elements[i].onfocus = function() { test(this); };
}
function test(ele){
alert(ele.value);
}
As both Nick and David pointed out, the way you assign the event handler is not correct. However, to achieve what you are trying (pass in a context) you can use a delegate function. Like this:
for (var i = 0; i < document.forms[0].elements.length; i++ ){
var ele = document.forms[0].elements[i];
ele.onfocus = delegate(ele, test);
}
function delegate(obj, handler) {
return function () {
handler.call(obj);
}
}
function test() {
alert(this.value);
}
What the delegate function does, is call your handler function setting the context of this. See the documentation for the Function object for further information. For even further reading, I recommend The this keyword and Introduction to events on Quirksmode.

Categories