JavaScript eventhandler: problem with execution of handler that got parameters - javascript

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.

Related

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>

Event Handler as a name vs anonymous functions

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);

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;
})())

Event never firing?

I am trying to add some event listener to document, but for some reason it looks like the click event is never fired, because the callback is never called:
function NotJquery(element) {
this.element = element;
return this;
}
NotJquery.prototype.eventFired = function(event, callback) {
console.log('in NotJquery prototype');
return function (event, callback) {
element.addEventListener(event, callback, true);
};
};
var $ = function(element) {
return new NotJquery(element);
};
function Test() {
}
Test.prototype.addListener = function() {
console.log('in Test prototype');
$(document).eventFired('click', function() {
console.log('click event fired');
});
};
(function() {
var test= new Test();
test.addListener();
}());
Both the messages: "in Test prototype" and "in NotJquery prototype" are logged in the console, but when I click somewhere in my document the message "click event fired" is not output in the console. I do not see what is wrong with my code. Anybody has an idea to get it working?
http://jsfiddle.net/Nn3tZ/1/
Your client code is expecting something like this:
NotJquery.prototype.eventFired = function(event, callback) {
this.element.addEventListener(event, callback, false);
};
Working demo: http://jsfiddle.net/Nn3tZ/2/
element is not defined within your eventFired function (but that's not the only problem). Here's a minimal update:
NotJquery.prototype.eventFired = function(event, callback) {
var self = this; // <== Change 1 of 2
console.log('in NotJquery prototype');
return function () {
self.element.addEventListener(event, callback, true);
// ^-- Change 2 of 2
};
};
Unlike some other languages (Java, C#), this is not optional when referring to a property on the current object. Separately, the function you're creating within eventFired won't have the right this, so that's why I've stowed it away as self and then used self within the generated function (which is a closure).
Separately, you're passing event and callback into eventFired but then also declaring them on the generated function (it's not at all clear to me why you're generating a function there at all), so the ones you pass into eventFired are never used.
More reading (on my anemic blog):
Mythical Methods
You must remember this
Closures are not complicated

Common callback for all instances of a class in Javascript

I've got a custom Javascript class that generates a JQuery mousedown callback. The mousedown callback is on $(document) and should really only be set for the first new instance, and not for any subsequent ones. I've got something like the following:
function myclass(arg){
this.arg = arg;
$(document).mousedown(function(down_event){
// start action
}).mouseup(function(){
// stop action
})
}
I'd like those callbacks to only register once in the event that multiple myclass instances are crated, and not at all if none are created.
You should use a variable to flag if the events have already been registered and register them only if they haven't already been registered.
An example of this:
var registered = false; // the 'flag' variable, default to false (i.e. not yet registered)
function myclass(arg){
this.arg = arg;
if (!registered) { // check the 'flag' variable if events have been registered yet
registered = true; // set the 'flag' variable as events will be registered this time
$(document).mousedown(function(down_event){
// start action
}).mouseup(function(){
// stop action
})
}
}
There's a jQuery function for that. Use .one() to bind a handler to the first instance of an event raised on an element (in this case document).
function myclass(arg){
this.arg = arg;
$(document)
.one('mousedown.yourEvent', downHandler)
.one('mouseup.yourEvent', upHandler);
}
function downHandler(e) {
// start action
}
function upHandler(e) {
// stop action
//ensure event dead forever
$(document).on('.yourEvent', function() { return false; });
}
Updated. The changes (using named handlers rather than anonymous functions, putting events in a specific namespace) are to ensure that new instances of myclass don't rebind to the event if they are created after the first one has finished being unbound.
Here are a couple of possible options.
Option 1: A global var
function myclass(arg){
this.arg = arg;
if (!window._myClassCreatedAlready) {
$(document).mousedown(function(down_event){
// start action
}).mouseup(function(){
// stop action
})
}
window._myClassCreatedAlready = true;
}
Option 2: jQuery Data
function myclass(arg){
this.arg = arg;
if (!$.data(document, "mousedownset")) {
$(document).mousedown(function(down_event){
// start action
}).mouseup(function(){
// stop action
})
}
$.data(document, "mousedownset", true);
}

Categories

Resources