Event Handler as a name vs anonymous functions - javascript

While implementing a closure function, I have noticed that if I provide a "named function" as an event handler then it gets executed straightaway when the event gets attached to the buttons. However, if I keep the function inline as an anonymous function then it doesn't execute straightaway and fires only on the
event happens. Please can anyone explain this behaviour?
var buttons = document.getElementsByTagName('button');
//function buttonHandler(buttonName){
//// return function(){
//// console.log(buttonName);
//// }
// alert("hello");
//}
var buttonHandler = function(name){
alert(name);
}
for(var i = 0; i < buttons.length; i += 1) {
var button = buttons[i];
var buttonName = button.innerHTML;
button.addEventListener('click', buttonHandler(buttonName));
button.addEventListener('click', function(buttonName){
alert("hi");
});
}
Many Thanks!

This has nothing to do with the function being named. This is about your code explicitly calling the function.
If you put (some,arguments) after a function then you will call it.
!foo() // calls the function
!function () { }(); // Also calls the function
So:
button.addEventListener('click', buttonHandler(buttonName));
Calls buttonHandler, passing buttonName as an argument to it
Calls addEventListener, passing "click" and the return value of 1 to it
buttonHandler has no return statement, so that return value of undefined which isn't a useful thing to pass to addEventListener.
The commented out version of buttonHandler returns a function. That would be a useful thing to pass to addEventListener.

As pointed out in the answers above the code
button.addEventListener('click', buttonHandler(buttonName));
is making direct call the function so if you only need to pass parameters to the handler function you may use an anonymous function instead of directly calling the function as recommended here (https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
I updated my code as below
button.addEventListener('click', function (){ buttonHandler(buttonName)}, false);

Related

How do you pass event to a setTimeout

I currently have:
var content = document.querySelectorAll(".content");
var myButtons = document.querySelectorAll(".more-button");
function enableContent(evt){
// remove class d-none so it can appear
content[evt.currentTarget.param].className = content[evt.currentTarget.param].className.replace(/\bd-none\b/g, "");
}
for(var i=0; i<myButtons.length; i++){
myButtons[i].addEventListener("click", enableContent);
myButtons[i].param = i;
}
Each myButton has it's own content. I'm trying to make content appear slowly when myButton is clicked like so:
for(var i=0; i<myButtons.length; i++){
myButtons[i].addEventListener("click", function(){setTimeout(enableContent, 2000, evt)}));
myButtons[i].param = i;
}
But it doesn't work, there is a problem with the parameter evt. How do I pass in evt to setTimeout?
I would suggest making solution easier by setting the setTimeout inside the enableConent directly
function enableContent(evt){
// remove class d-none so it can appear
setTimeout( () => {
content[evt.currentTarget.param].className =
content[evt.currentTarget.param].className.replace(/\bd-none\b/g, "");
}, 2000);
}
But if you need to learn why the event is not being passed to the enableContent event handler we need to know how setTimeout and function binding works.
setTimeout take a callback (fancy word for passing a function to another function) and a delay time(that guarantees at least x time before calling the callback function.
so using setTimeout should be used like
setTimeout( enableContent, 2000 );
yet this code won't pass the event so we need to bind the function enableContent with the event object so when the event handler is called the function know and access the event object properly
myButtons[i].addEventListener("click", function(event){
setTimeout( enableContent.bind(event), 2000);
});
binding in js has many types, implicit, explicit, hard, & new binding. using bind function bind the enalbeConent with the event and return a new function that is hard bound with the event object.
You need to pass the event (evt) to the function itself, and I think maybe you are moving the wrong way using the setTimeout, maybe you don't need to use a setTimeout? You can use css animation or transition with *-delay properties?
But if you are using setTimeout, you need to clear timeout after every click using the clearTimeout method:
myButtons[i].addEventListener("click", function(evt) {
if(evt.currentTarget.timer) {
clearTimeout(e.currentTarget.timer);
}
evt.currentTarget.timer = setTimeout(enableContent, 2000);
}));
Example: https://jsfiddle.net/6kcq2ma8/
Easy peasy!
window.addEventListener("load", (event)=> {
let setTimeoutHandler = (event) =>{
console.log(event);
}
setTimeout(()=>{
setTimeoutHandler(event);
}, 1000);
});

How to create a javascript functional 'class' so that I can access a 'method' from outside and inside the function

I am creating a function that handles a bunch of stuff around pagenating and sorting a table. It contains a key function that submits the db query and updates the display table.
I want to be able to access that inner function/method from both inside the function and also from outside on the object created.
testFunction = function() {
keyMethod = function() {
console.log('ya got me');
};
document.getElementById('test').addEventListener('click', function (e) {
keyMethod();
});
keyMethod();
};
myTest = new testFunction();
myTest.keyMethod();
testFunction = function() {
this.keyMethod = function() {
console.log('ya got me');
};
document.getElementById('test').addEventListener('click', function (e) {
// would have to use bind here which then messes up trying to
// find the correct target etc.
keyMethod();
});
this.keyMethod();
};
myTest= new DrawShape();
myTest.keyMethod();
Creating it the first way means that the keyMethod function is available everywhere within the testFunction but I cant call it from outside.
Creating it the second way means I can do myTest.keyMethod but I then cant call it from within an inner function without using bind everywhere.
Is there a better way..?
You could replace the function provided as callback with an arrow function or use bind the function first like you already said.
testFunction = function() {
this.keyMethod = function() {
console.log('ya got me');
};
// Replace callback by simply providing the function to call.
// This works as long as you don't use the `this` keyword inside the
// provided function.
document.getElementById('test').addEventListener('click', this.keyMethod);
// If your callback method does use the `this` keyword you can either use an
// arrow function or bind the function up front.
document.getElementById('test').addEventListener('click', event => this.keyMethod());
document.getElementById('test').addEventListener('click', this.keyMethod.bind(this));
this.keyMethod();
};
console.log("constructor output:");
myTest = new testFunction();
console.log(".keyMethod() output:");
myTest.keyMethod();
console.log("click event output:");
<button id="test">test</button>

Don't have to pass in argument in addEventListener?

Why don't I have to pass in an argument when adding playSound(e) to addEventListener? How does the event automatically get passed in?
function playSound(e) {
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
if (!audio) return; //stop the function from running
audio.currentTime = 0; // rewinds to the start
audio.play();
key.classList.add("playing");
}
window.addEventListener('keydown', playSound);
The code works as is
Think about it this way:
function addEventListener(event, handler) {
const eventObject = new Event();
switch(event) {
case "keydown":
handler(eventObject);
}
}
This is a simplified version obviously, but the function you pass in is called with an argument by addEventListener
First you defined the function. In usual scenarios we will call that function some where. But in this case you want to call it when a keydown event is triggered. For that you have passed that function to keydown event as a callback. Something like below is happening behind the scenes.
function keydown(callback) {
var e = getEvent(); // here it will get you the event object.
callback(e); // here your function playSound is called.
}

JavaScript Self Invoking Function on addEventListener

I'm not being able to make a function of an Event Listener self invoke and the listener to work.
The following code executes the function, but the Event Listener don't work:
window.addEventListener("resize", (function () {
document.getElementById("divMenu").innerHTML = document.getElementById("divTop").offsetWidth
})())
The function will set a needed (dynamic since the beginning) CSS style essential to the website formatting. The "resize" function MUST be executed at load.
Is it possible to make this, or should i create a separate Self Invoking Function and call it on the Event Listener?
When you immediately invoke the function, it's return value is put in it's place (window.addEventListener('resize', undefined)). Instead, define your function outside of the event listener then add it and call it.
function onResize() {
document.getElementById('divMenu').innerHTML = document.getElementById("divTop").offsetWidth;
}
window.addEventListener('resize', onResize);
onResize();
Technically, you could make this work using a self-invoking function but it's a bit screwy and I wouldn't suggest it.
window.addEventListener('resize', (function onResize() {
document.getElementById('divMenu').innerHTML = document.getElementById("divTop").offsetWidth;
// Works because it returns a function
return onResize;
})());
Your IIF returns undefined, but eventlistener must be a function, or link to function. Add return to your IIF or pass function:
Anonymous function:
window.addEventListener("resize", function () {
document.getElementById("divMenu").innerHTML = document.getElementById("divTop").offsetWidth
}))
IIF, that returns a function
window.addEventListener("resize", (function () {
return function(){
document.getElementById("divMenu").innerHTML = document.getElementById("divTop").offsetWidth
}
})())
Edited (invoke assignment on startup):
window.addEventListener("resize", (function () {
function set_innerHtml(){
document.getElementById("divMenu").innerHTML = document.getElementById("divTop").offsetWidth
}
set_innerHtml();
return set_innerHtml;
})())

JavaScript eventhandler: problem with execution of handler that got parameters

I have this problem:
I have defined an event handler which requires parameters on it.
var handler = function(p1, p2){
//some code
}
Then I add the event handler to an object inside a function.
function foo(p1, p2){
//Some code
obj.addEventListener('click', handler(p1, p2), false)
}
As you already know the code above is not correct. It wont listen to the event. Instead it will execute the function instantly. Now to fix this I can just erase handler(p1, p2) and insert function(){ handler(p1, p2) } instead. But the problem is that I have an another function that I want to remove the event listener, which is not possible with the latter solution.
function koo(){
//Some code
obj.removeEventListener('click', handler, false)
}
How do I fix this?
I think you'll need to create a closure:
var createHandler = function(p1, p2){
return function (event) {
//some code that uses p1 and p2
};
};
var handler;
...and now you can assign the handler like so, while still having access to p1 and p2:
function foo(p1, p2){
handler = createHandler(p1, p2);
//Some code
obj.addEventListener('click', handler, false);
}
function koo(){
//Some code
obj.removeEventListener('click', handler, false);
handler = null;
}
Note that handler is now a global variable.
Update: I just realized that in your case, this could be simplified by merging createHandler and foo as follows:
var handler; // we need this to be accessible both to foo and koo
function foo(p1, p2){
handler = function(event) {
// some code that uses p1 and p2
};
//Some code
obj.addEventListener('click', handler, false);
}
function koo(){
//Some code
obj.removeEventListener('click', handler, false);
handler = null;
}
I don't think you want to pass arguments there - because it is a callback, and you are never sure what those variables will be.
Can you do...
(function() {
var p1 = 'a',
p2 = 'b',
obj = document.getElementById('my-object');
var handleClick = function(event) {
// Access to p1 and p2
// Access to `event` object containing info about the event
}
obj.addEventListener('click', handleClick, false);
// If you want to remove it
obj.removeEventListener('click', handleClick, false);
})();
May I ask why you want to have arguments on it? Do you intend to call it from a non click triggered way as well?
Isn't it just this:
function foo(){
//Some code
obj.addEventListener('click', handler, false)
}
Pass the function instead of calling it.

Categories

Resources