Ending an Event Driven Function in JavaScript - javascript

I am using JavaScript with HTML5. When the user clicks on a button, an event-driven JavaScript function starts up. When the user clicks on the button again, another instance of this function starts up. So I have two instances of the same function handling a event. However I only want the new instance to be running. How do I end the first instance of the?
An example is a function with the following code
Canvas.paper = Raphael(xOffset,yOffset,imageWidth,imageHeight);
masterBackground = Canvas.paper.rect(0,0,imageWidth,imageHeight);
window.onkeydown=function(e){
// Event handler code
}
document.addEventListener('keydown', function(event) {
// Event handler code
}
masterBackground.mousemove(function(e){
// Event handler code
}

Seems apparent that something asynchronous and long-running is happening.
To prevent concurrent instances from running, just use a flag that is set when one starts so that others can't begin. Then when the current one finishes, reset the flag so that another can start.
// Immediately invoked function, makes a variable and returns the handler
// that uses the variable as a flag.
button.onclick = (function() {
// local variable, only accessible to the returned handler
var running = false;
// This is your event handler.
return function(e) {
if (running === false) {
running = true;
// run your asynchronous operation
// after it's complete, set `running = false;`
}
};
})();

There are several solutions to this, some of them library dependent, but "nicer" to look at:
For example, using jQuery:
<button>Click me</button>
<script>
$('button').on('click', handleButtonClick);
function handleButtonClick() {
$(this).off('click', handleButtonClick); //disable click event
//do various things you don't want duplicated
$(this).on('click', handleButtonClick); //reattach handler
}
</script>
OR:
<button>Click me</button>
<script>
$('button').once('click', handleButtonClick); //attach one-time handler
function handleButtonClick() {
//do various things you don't want duplicated
$(this).once('click', handleButtonClick); //attach one-time handler
}
</script>
Most libraries support similar methods, if you'd rather do it vanilla JS, that is definitely possible of course as well. "am not i am" provided a nice example for that: https://stackoverflow.com/a/15976888/622129

var buttonView = document.getElementById('buttonView');
buttonView.handleEvent = function(event) {
window.alert(this.id);
//this.onclick = null;
};
buttonView.onclick = buttonView.handleEvent;
Try it out: http://jsfiddle.net/KHQ4y/
Edit: I posted this before you supplied your specific code, but you get the idea.

If you want to make sure a function only runs once:
example based on benny's example
function onlyOnce(proc){
return function () {
var result = proc.apply(this,arguments);
proc = function () {};
return result;
}
}

Related

Run a function when another function is called

I'm wondering how to run a function when another function is called. addEventListener only runs events like "click", "mouseover", etc. However, I'd like to listen for a function call.
EXAMPLE:
Function 1 is called. Afterwards, Function 2 runs because it saw that Function 1 was called.
Is there an addEventListener alternative for simple functions and not events? I can't seem to find any.
My goal was to simply run a function everytime a user did something like call for when something was hidden in jQuery or by another JavaScript library or just simply another external JavaScript file with some code I added in.
Introducing a very hacky way
Since what you are trying to achieve is basically hacking some existing system (you shouldn't run into this problem if you have control over both sides and design your code properly).
It looks like your function is declared globally as well. In that case:
1. store the existing function in a variable
2. overwrite that function with your implementation
3. call the function variable at the start
function myFunction(){
//This is the main function
alert('Hello, this is part of the message!');
}
var tempfunc = myFunction;
window.myFunction = function() {
tempfunc();
// do what you need to do in the event listener here
alert('Hello, this is the other part of the message!');
}
EDIT:
The original question had the requirement that the original function cannot be modified, hence my solution. Since they it appears the question has changed.
You will have trigger an event inside myFunction and listen to that event.
function myFunction(){
//This is the main function
alert('Hello, this is part of the message!');
// trigger the event
var event = new CustomEvent("event", { "detail": "Example of an event" });
document.dispatchEvent(event);
}
// handle here;
document.addEventListener("event", function(){
//This is the secondary function
//or the function I need to run after the main function is called
alert('Hello, this is the other part of the message!');
});
// call main
myFunction();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Simple test!</p>
<p>Long story short, you get message 1, but message 2 never pops up!</p>
Is there an addEventListener alternative for simple functions and not
events? I can't seem to find any.
My goal was to simply run a function everytime a user did something
like call for when something was hidden in jQuery or by another
JavaScript library or just simply another external JavaScript file
with some code I added in.
You can use jQuery.Callbacks()
var callbacks = $.Callbacks();
function handleCallback1(message) {
console.log(message, this)
};
function handleCallback2(message) {
if (this.tagName === "DIV") {
this.style.color = "green";
} else {
this.nextElementSibling.style.color = "blue";
}
};
$("button, div").on("click", function() {
callbacks.fireWith(this, ["called from " + this.tagName])
});
callbacks.add(handleCallback1, handleCallback2);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<button>click</button>
<div>click</div>

Javascript : Replace Event Listener

I am listening to an event and want to call different methods. For example, I am listening to animation end event and the code is something like this:
this.inAnimationCallback = function() {
console.log('In');
_this.elem.className = _this.settings.className;
};
this.outAnimationCallback = function() {
console.log('Out');
_this.elem.parentNode.removeChild(_this.elem);
};
this.elem.addEventListener(animationEvent, this.inAnimationCallback);
setTimeout(function() {
_this.elem.addEventListener(animationEvent, _this.outAnimationCallback);
// Call some animation here.
}, 3000);
What happens here is that instead of replacing the method attached to the event, JS adds the method and when animation ends, both methods are called. Console looks like this:
(2) In
Out
I'm writing this answer for those like me, who is just started learning JS. And this thread came up first in google to "js replace event listener"..
Although, I am not disagreeing with the answers to use removeEventListener(), but mozilla warns that this function is not always successful. So use it with care. not willing to go that road i have found two other ways to do it.
Use something like GlobalEventHandlers which is simple as target.onclick = functionRef;. Mozilla even warns:
Only one onclick handler can be assigned to an object at a time.
Within listener function add external function call to action function, and then replace reference to another external action function. For example this code will call firstAction(), then seconAction(), then first again...:
const buttonOne = document.getElementById('buttonOne');
buttonOne.addEventListener('click', listenerFunction);
let doAction = firstAction; //assigning doAction to firstAction
function listenerFunction() {
doAction(); //external function call
}
function firstAction() {
doAction = secondAction; //assigning doAction to secondAction
console.log('first action clicked');
}
function secondAction() {
doAction = firstAction; //assigning doAction to firstAction
console.log('second action clicked');
}
<button type="button" id="buttonOne" name="button">button1</button>
I wrote this answer to broaden solution scope: would have saved at least 6 hours of my time. If I had this in the first place...
You can just remove the event listener before adding the new one :
setTimeout(function() {
_this.elem.removeEventListener(animationEvent, _this.inAnimationCallback);
_this.elem.addEventListener(animationEvent, _this.outAnimationCallback);
// Call some animation here.
}, 3000);

How can I add an event for a one time click to a function?

I would like to add a click event listener to a function but would only like it to happen once. How could i do this?
I would like to stay clear of JQuery as well if it is possible please.
EDITED
As the answers that I am getting for this are fully satisfying my need i thought i may make it a bit more clear with context.
I am writing a function to draw a rectangle, first with one click on a button to initiate the rectangle function. Then there are two click event listeners in the drawRectangle function. These are the events i would like to happen only once in the function. Allowing the user to then create another rectangle if they click on the rectangle initiation button again.
Use modern JavaScript!
EventTarget.addEventListener("click", function() {
// Do something cool
}, {once : true});
A Boolean indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
- MDN web docs
All modern browsers support this feature
Other reference
You have to use removeEventListener once the event is fired once. However, removeEventListener takes a function as argument, which means you need to declare a named function, add it with addEventListener, and have it removing itself. Example:
function foo() {
// do things, then
removeEventListener('click', foo);
}
addEventListener('click', foo);
function one(el, type, fn) {
function handler(event) {
el.removeEventListener(type, handler);
fn(event);
}
el.addEventListener(type, handler);
}
// use it like
one(window, 'resize', function () {
alert("This triggers just once");
});
Example: http://jsfiddle.net/6njpem7x/
The other answers are correct in that this can be achieved with a named function, but you don't need to declare the function separately. You can use a named function expression:
element.addEventListener("click", function handler(event) {
this.removeEventListener("click", handler);
// ...
});
An alternative, though less optimal, approach is to keep around a variable that keeps track whether the handler was executed:
var wasExecuted = false;
element.addEventListener("click", function(event) {
if (wasExecuted) {
return;
}
wasExecuted = true;
// ...
});
The variable needs to be declared outside the handler but within scope, so that its value persists across event triggers.
Combination of addEventListener and removeEventListener:
element.addEventListener("click", clickFunction);
function clickFunction(e) {
console.log("clicked");
element.removeEventListener("click", clickFunction);
}
jsFiddle
something like this
var el = document.getElementById('something');
el.addEventListener('click', doSomething);
function doSomething() {
el.removeEventListener('click', doSomething);
//code
}
Inside event handler you can use universal: e.target.removeEventListener(e.type, arguments.callee)
Or you can make special function for creating "one time" event listeners:
function oneTimeListener(node, type, callback) {
// create event
node.addEventListener(type, function(e) {
// remove event listener
e.target.removeEventListener(e.type, arguments.callee);
// call handler with original context
// as it happens with native addEventListener
return callback.call(this, e);
});
}
oneTimeListener(document.getElementById("myElement"), "click", myHandler);
You can set a cookie after first click:
document.cookie="click=1; expires=.......";
and add condition to listener - if cookie is set, you omit that.
Another simple solution which I'm using is to add a dummy class to the element to which we are listening so that it will not fire again.
const myButton = document.querySelector('#my-button:not(.init)');
myButton.addEventListener('click', (e) => {
e.preventDefault();
myButton.classList.add('init');
});

jQuery's One - Fire once with multiple event types

Is there a way to fire a single function once when any event is raised?
For example, if I have the following function: (demo in jsfiddle)
$('input').one('mouseup keyup', function(e){
console.log(e.type);
});
I'd like to only call the function once, regardless of which event fired it.
But according to the docs for .one():
If the first argument contains more than one space-separated event types, the event handler is called once for each event type.
So, currently the function will fire once for each event type.
Instead of using .one, use .on and remove the binding manually with .off.
$('input').on('mouseup keyup', function(e){
console.log(e.type);
$(this).off('mouseup keyup');
});
Fiddle: http://jsfiddle.net/23H7J/3/
This can be done a little more elegantly with namespaces:
$('input').on('mouseup.foo keyup.foo', function(e){
console.log(e.type);
$(this).off('.foo');
});
This allows us to use a single identifier (foo) to remove any number of bindings, and we won't affect any other mouseup or keyup bindings the element may have.
Fiddle: http://jsfiddle.net/23H7J/41/
Great answers! To wrap them into a function, here's a jQuery extension based off the current answers:
//The handler is executed at most once per element for all event types.
$.fn.once = function (events, callback) {
return this.each(function () {
$(this).on(events, myCallback);
function myCallback(e) {
$(this).off(events, myCallback);
callback.call(this, e);
}
});
};
Then call like this:
$('input').once('mouseup keyup', function(e){
console.log(e.type);
});
Demo in fiddle
Bonus: This has the added benefit of only detaching the handlers for this specific function by passing in the original handler to the off function. Otherwise, you'll need custom namespaces.
Additionally, if you want a handler to fire only once as soon as any of the elements fire any of the events and then immediately detach itself, then just remove the each from the extension like this:
//The handler is executed at most once for all elements for all event types.
$.fn.only = function (events, callback) {
var $this = $(this).on(events, myCallback);
function myCallback(e) {
$this.off(events, myCallback);
callback.call(this, e);
}
return this
};
To fire once and keep firing, the following snippet may be used:
https://jsfiddle.net/wo0r785t/1/
$.fn.onSingle = function(events, callback){
if ("function" == typeof(callback)) {
var t = this;
var internalCallback = function(event){
$(t).off(events, internalCallback);
callback.apply(t, [event]);
setTimeout(function(){
$(t).on(events, internalCallback);
}, 0);
};
$(t).on(events, internalCallback);
}
return $(this);
};
It works by temporarily disabling the events using the internalCallback function, and asynchronously (setTimeout) enabling it again.
Most other suggestions in this thread disable all future events on the target. However, while OP seems satisfied with previous answers, his question does not mention the permanent disabling of events.
You could just add $('input').off(); to the end of your function.
A simpler way to fire once but keep firing on subsequent changes, is to temporarily store the previous value and do a quick comparison: If nothing has changed then don't run the rest of the function.
jQUERY
$('input').on('keyup paste input change', function(e){
var oldValue = $(e.target).data('oldvalue');
var newValue = $(e.target).val();
if (oldValue === newValue) //nothing has changed
return;
$(e.target).data('oldvalue',newValue); //store the newly updated value
//proceed with the rest of the function
});
Note that I'm using .on to attach the events, instead of .one
PURE JAVASCRIPT
For a pure JavaScript solution, listeners need to be added separately, since it's not possible to pass multiple events to .addEventListener.
var selector = document.querySelector('input')
selector.addEventListener('keyup', updateElement);
selector.addEventListener('paste', updateElement);
selector.addEventListener('input', updateElement);
selector.addEventListener('change', updateElement);
function updateElement(e){
var oldValue = e.target.getAttribute('oldvalue');
var newValue = e.target.value;
if (oldValue === newValue) //nothing has changed
return;
console.log (newValue);
e.target.setAttribute('oldvalue', newValue); //store the newly updated value
//proceed with the rest of the function
}
Assuming you want to listen for changes in the input box, I suggest not to use mouseup to detect a possible Right click > paste, since the mouse could be released somewhere else, not necessarily inside the input box. To be on the safe side you better use keyup, paste, input and change, as I did on my code above.

Anyway to use two .onclick event handlers from two different scripts on one button?

Is it possible to use two .onclick event handlers on the same button when the event handlers are stored in two different scripts? I know it would be easy to just call the two functions I need into the same .onclick handler, but I'm curious if there's a way around this.
Suppose I have
script1.js
someButton.onclick = function() {
someFunction1();
}
script2.js
someButton.onclick = function() {
someFunction2();
}
Running these scripts in the html in this order would lead to calling someFunction2() onclick, as I assume the functionality of the button is overwritten by script2.js.
You can use addEventListener and this will add both the event handlers to the element on contrast to replacing the onclick attribute of the element.
someButton.addEventListener('click', function() {
someFunction1();
});
someButton.addEventListener('click', function() {
someFunction2();
});
Fiddle
addEventListener is the way to go here, but if you were to stick with someButton.onclick, I would do this:
var old = someButton.onclick;
someButton.onclick = function (event) {
if (typeof old === 'function') {
old.call(someButton, event); // call the old listener
}
someFunction2(); // run new code
};
It saves a reference to the old onclick function, and makes a new onclick function that will not only call the old onclick function, but run whatever other code you want as well.

Categories