Unbind event in Javascript - javascript

I'm just getting started with A/B test using Dynamic Yield. I'm facing a few issues overwriting events that are fired by Javascript.
Let's for the sake of this example take this function written in the frontend:
$('body')
.on('mouseenter focusin', 'class1', () => {
$('body').trigger('menu:close');
});
Now my question is, how can I overwrite this event on after the whole file is already initialized? As you know this kind of A/B test has to overwrite the code loaded on the page. So for example I'd like to remove this trigger event. Does anyone has any idea on how to proceed? I should write only pure Javascript because at this stage I do not have access to Jquery.
Thanks

Use the .off() method:
$('body').off('mouseenter focusin', 'class1');
Note that this will remove all handlers for these events delegated to this class, not just the ones added by the original code. If you want be more selective you need to use a named function rather than an anonymous function, so you can give the same function later.
function handler() {
$('body').trigger('menu:close');
}
$('body').on('mouseenter focusin', 'class1', handler);
// later
$('body').off('mouseenter focusin', 'class1', handler);
Another solution is to use namespaced events.
$('body').on('mouseenter.test focusin.test', 'class1', () => $('body').trigger('menu:close'););
// later
$('body').off('mouseenter.test focusin.test', 'class1');

You can remove the event by deleting the property Jquery appends to the object:
In my case it's called jQuery360075100310324570631. In code below the event of the button is removed
$("body").on("click", ".class1", () => {
console.log("click!!");
});
function deleteEvent() {
const a = document.getElementsByTagName("body")[0];
const regex = new RegExp(/jQuery\d*/);
for (const key in a) {
if (regex.test(key)) {
delete a[key];
return;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="class1">button</button>
<button class="class2" onclick="deleteEvent()">remove event from button</button>

$('body').off('mouseenter focusin', 'class1');

Related

Why wouldn't the event listener get removed from the tds in this html row? [duplicate]

I have the following code to add eventListener
area.addEventListener('click',function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
},true);
It is working correctly as expected..Later in another function i tried to remove the event listener using the following code
area.removeEventListener('click',function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
},true);
But the even listener is not removed..Why is it happening?Is there any problem with my removeEventListener()?
Note:Here area is something like document.getElementById('myId')
This is because that two anonymous functions are completely different functions. Your removeEventListener's argument is not a reference to the function object that was previously attached.
function foo(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
}
area.addEventListener('click',foo,true);
area.removeEventListener('click',foo,true);
I find that for the windows object, the last param "true" is required.
The remove doesn't work if there is no capture flag.
In a React function component, make sure to define the callback with the useCallback(() => {}) hook. If you fail to do this, the callback will be a different one on every re-render and the removeEventListener method will not work.
const scrollCallback = useCallback(() => { // do sth. });
window.addEventListener("scroll", scrollCallback, true);
window.removeEventListener("scroll", scrollCallback, true);
It looks like no one's covered the part of the DOM specification (that both browsers and Node.js implement) that now gives you a mechanism to remove your event listener without using removeEventListener.
If we look at https://dom.spec.whatwg.org/#concept-event-listener we see that there are a number of properties that can be passed as options when setting up an event listener:
{
type (a string)
callback (an EventListener object, null by default)
capture (a boolean, false by default)
passive (a boolean, false by default)
once (a boolean, false by default)
signal (an AbortSignal object, null by default)
removed (a boolean for bookkeeping purposes, false by default)
}
Now, there's a lot of useful properties in that list, but for the purposes of removing an event listener it's the signal property that we want to make use of (which was added to the DOM level 3 in late 2020), because it lets us remove an event listener by using an AbortController instead of having to bother with keeping a reference to the exact handler function and listener options "because otherwise removeEventListener won't even work properly":
const areaListener = new AbortController();
area.addEventListener(
`click`,
({clientX: x, clientY: y}) => {
app.addSpot(x, y);
app.addFlag = 1;
},
{ signal: areaListener.signal }
);
And now, when it's time to remove that event listener, we simply run:
areaListener.abort()
And done: the JS engine will abort and clean up our event listener. No keeping a reference to the handling function, no making sure we call removeEventListener with the exact same funcation and properties as we called addEventListener: we just cancel the listener with a single, argumentless, abort call.
And of course, also note that if we want to do this "because we only want the handler to fire once", then we don't even need to do this, we can just create an event listener with { once: true } and JS will take care of the rest. No removal code required.
area.addEventListener(
`click`,
() => app.bootstrapSomething(),
{ once: true }
);
You are creating two different functions in both calls. So the second function does not relate in any way to the first one and the engine is able to remove the function. Use a common identifier for the function instead.
var handler = function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
};
area.addEventListener('click', handler,true);
later you can then remove the handler by calling
area.removeEventListener('click', handler,true);
To remove it, store the function in a variable or simply use a named function and pass that function to the removeEventListener call:
function areaClicked(event) {
app.addSpot(event.clientX, event.clientY);
app.addFlag = 1;
}
area.addEventListener('click', areaClicked, true);
// ...
area.removeEventListener('click', areaClicked, true);
If you want to pass local variables to the function called by the event listener, you can define the function inside the function (to get the local variables) and pass the name of the function in the function itself. For example, let's start inside the function that adds the event listener with app as a local variable. You would write a function inside this function such as,
function yourFunction () {
var app;
function waitListen () {
waitExecute(app, waitListen);
}
area.addEventListener('click', waitListen, true);
}
Then you have what you need to remove it when waitExecute is called.
function waitExecute (app, waitListen) {
... // other code
area.removeEventListener('click', waitListen, true);
}
define your Event Handler first,
and then
area.addEventListener('click',handler);
area.removeEventListener('click',handler);
This is what I ended up doing but it's in a route class but should not make much difference, I wanted for the event listener not to accumulate each time afterModel() is called but also needed arguments and scope so that the model is changed each time.
export default class iFrameRoute extends Route {
afterModel(model) {
this.initFrame = function(event) {
alert("I am being called");
window.removeEventListener("message", this.route.test);
}.bind({route: this, data: model});
window.addEventListener("message", this.initFrame );
}
}
I went through this same problem recently. A reasonble solution that I found was remove attribute "onclick" on element from HTMLElement class.
Let's imagine that you already got your component from DOM - using document.getElementById or document.querySelector - you can try that code:
js
const element = document.getElementById("myelement");
element.attributes.removeNamedItem('onclick');
html example
<div onClick="memoryGame.flipCard(this)">
.... // children elements
</div>
I know which this solution it ins't the best, but it works!
I hope I was able to help you.
Cheers!
PS: please, give me a "useful answer"... thanks :D
while adding function store in array and removing pass by map work for me
const [functionObjects, setfunctionObjects] = useState([]);
const addListener = (beforeUnloadListener) =>{
setfunctionObjects([...nano, beforeUnloadListener]);
addEventListener("beforeunload", beforeUnloadListener, {capture: true});
};
const removeListener = (beforeUnloadListener) => {
functionObjects.map((item) => {
removeEventListener("beforeunload", item, {capture: true});});
};
In case of React we can use useRef() to store our listener function in current property. So that in case of re-render and in case of remove listener it will maintain the same reference to the function.
const handleWindowClick = useRef(() => {
console.log("window clicked");
});
// for attaching event listener
window.addEventListener("click", handleWindowClick.current);
// for detaching event listener
window.removeEventListener("click", handleWindowClick.current);
Update 2023
I was using Angular and after numerous tries using AbortController() nothing solved my problem.
Finally renderer2 to the rescue. Here's what I did
mouseMoveListener :any;
mouseUpListener :any;
this.mouseMoveListener = this._renderer.listen("document", "mousemove", (event) => {
this.onMouseMove(event);
});
this.mouseUpListener = this._renderer.listen("document", "mouseup", (event) => {
this.onMouseUp(event);
});
ngOnDestroy(){
this.mouseMoveListener();
this.mouseUpListener();
}

removeEventListener on document in functional component react is not working [duplicate]

I have the following code to add eventListener
area.addEventListener('click',function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
},true);
It is working correctly as expected..Later in another function i tried to remove the event listener using the following code
area.removeEventListener('click',function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
},true);
But the even listener is not removed..Why is it happening?Is there any problem with my removeEventListener()?
Note:Here area is something like document.getElementById('myId')
This is because that two anonymous functions are completely different functions. Your removeEventListener's argument is not a reference to the function object that was previously attached.
function foo(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
}
area.addEventListener('click',foo,true);
area.removeEventListener('click',foo,true);
I find that for the windows object, the last param "true" is required.
The remove doesn't work if there is no capture flag.
In a React function component, make sure to define the callback with the useCallback(() => {}) hook. If you fail to do this, the callback will be a different one on every re-render and the removeEventListener method will not work.
const scrollCallback = useCallback(() => { // do sth. });
window.addEventListener("scroll", scrollCallback, true);
window.removeEventListener("scroll", scrollCallback, true);
It looks like no one's covered the part of the DOM specification (that both browsers and Node.js implement) that now gives you a mechanism to remove your event listener without using removeEventListener.
If we look at https://dom.spec.whatwg.org/#concept-event-listener we see that there are a number of properties that can be passed as options when setting up an event listener:
{
type (a string)
callback (an EventListener object, null by default)
capture (a boolean, false by default)
passive (a boolean, false by default)
once (a boolean, false by default)
signal (an AbortSignal object, null by default)
removed (a boolean for bookkeeping purposes, false by default)
}
Now, there's a lot of useful properties in that list, but for the purposes of removing an event listener it's the signal property that we want to make use of (which was added to the DOM level 3 in late 2020), because it lets us remove an event listener by using an AbortController instead of having to bother with keeping a reference to the exact handler function and listener options "because otherwise removeEventListener won't even work properly":
const areaListener = new AbortController();
area.addEventListener(
`click`,
({clientX: x, clientY: y}) => {
app.addSpot(x, y);
app.addFlag = 1;
},
{ signal: areaListener.signal }
);
And now, when it's time to remove that event listener, we simply run:
areaListener.abort()
And done: the JS engine will abort and clean up our event listener. No keeping a reference to the handling function, no making sure we call removeEventListener with the exact same funcation and properties as we called addEventListener: we just cancel the listener with a single, argumentless, abort call.
And of course, also note that if we want to do this "because we only want the handler to fire once", then we don't even need to do this, we can just create an event listener with { once: true } and JS will take care of the rest. No removal code required.
area.addEventListener(
`click`,
() => app.bootstrapSomething(),
{ once: true }
);
You are creating two different functions in both calls. So the second function does not relate in any way to the first one and the engine is able to remove the function. Use a common identifier for the function instead.
var handler = function(event) {
app.addSpot(event.clientX,event.clientY);
app.addFlag = 1;
};
area.addEventListener('click', handler,true);
later you can then remove the handler by calling
area.removeEventListener('click', handler,true);
To remove it, store the function in a variable or simply use a named function and pass that function to the removeEventListener call:
function areaClicked(event) {
app.addSpot(event.clientX, event.clientY);
app.addFlag = 1;
}
area.addEventListener('click', areaClicked, true);
// ...
area.removeEventListener('click', areaClicked, true);
If you want to pass local variables to the function called by the event listener, you can define the function inside the function (to get the local variables) and pass the name of the function in the function itself. For example, let's start inside the function that adds the event listener with app as a local variable. You would write a function inside this function such as,
function yourFunction () {
var app;
function waitListen () {
waitExecute(app, waitListen);
}
area.addEventListener('click', waitListen, true);
}
Then you have what you need to remove it when waitExecute is called.
function waitExecute (app, waitListen) {
... // other code
area.removeEventListener('click', waitListen, true);
}
define your Event Handler first,
and then
area.addEventListener('click',handler);
area.removeEventListener('click',handler);
This is what I ended up doing but it's in a route class but should not make much difference, I wanted for the event listener not to accumulate each time afterModel() is called but also needed arguments and scope so that the model is changed each time.
export default class iFrameRoute extends Route {
afterModel(model) {
this.initFrame = function(event) {
alert("I am being called");
window.removeEventListener("message", this.route.test);
}.bind({route: this, data: model});
window.addEventListener("message", this.initFrame );
}
}
I went through this same problem recently. A reasonble solution that I found was remove attribute "onclick" on element from HTMLElement class.
Let's imagine that you already got your component from DOM - using document.getElementById or document.querySelector - you can try that code:
js
const element = document.getElementById("myelement");
element.attributes.removeNamedItem('onclick');
html example
<div onClick="memoryGame.flipCard(this)">
.... // children elements
</div>
I know which this solution it ins't the best, but it works!
I hope I was able to help you.
Cheers!
PS: please, give me a "useful answer"... thanks :D
while adding function store in array and removing pass by map work for me
const [functionObjects, setfunctionObjects] = useState([]);
const addListener = (beforeUnloadListener) =>{
setfunctionObjects([...nano, beforeUnloadListener]);
addEventListener("beforeunload", beforeUnloadListener, {capture: true});
};
const removeListener = (beforeUnloadListener) => {
functionObjects.map((item) => {
removeEventListener("beforeunload", item, {capture: true});});
};
In case of React we can use useRef() to store our listener function in current property. So that in case of re-render and in case of remove listener it will maintain the same reference to the function.
const handleWindowClick = useRef(() => {
console.log("window clicked");
});
// for attaching event listener
window.addEventListener("click", handleWindowClick.current);
// for detaching event listener
window.removeEventListener("click", handleWindowClick.current);
Update 2023
I was using Angular and after numerous tries using AbortController() nothing solved my problem.
Finally renderer2 to the rescue. Here's what I did
mouseMoveListener :any;
mouseUpListener :any;
this.mouseMoveListener = this._renderer.listen("document", "mousemove", (event) => {
this.onMouseMove(event);
});
this.mouseUpListener = this._renderer.listen("document", "mouseup", (event) => {
this.onMouseUp(event);
});
ngOnDestroy(){
this.mouseMoveListener();
this.mouseUpListener();
}

Removing event listener with anonymous listener [duplicate]

Is there anyway to remove an event listener added like this:
element.addEventListener(event, function(){/* do work here */}, false);
Without replacing the element?
There is no way to cleanly remove an event handler unless you stored a reference to the event handler at creation.
I will generally add these to the main object on that page, then you can iterate and cleanly dispose of them when done with that object.
You could remove the event listener like this:
element.addEventListener("click", function clicked() {
element.removeEventListener("click", clicked, false);
}, false);
Anonymous bound event listeners
The easiest way to remove all event listeners for an element is to assign its outerHTML to itself. What this does is send a string representation of the HTML through the HTML parser and assign the parsed HTML to the element. Because no JavaScript is passed, there will be no bound event listeners.
document.getElementById('demo').addEventListener('click', function(){
alert('Clickrd');
this.outerHTML = this.outerHTML;
}, false);
<a id="demo" href="javascript:void(0)">Click Me</a>
Anonymous delegated event listeners
The one caveat is delegated event listeners, or event listeners on a parent element that watch for every event matching a set of criteria on its children. The only way to get past that is to alter the element to not meet the criteria of the delegated event listener.
document.body.addEventListener('click', function(e){
if(e.target.id === 'demo') {
alert('Clickrd');
e.target.id = 'omed';
}
}, false);
<a id="demo" href="javascript:void(0)">Click Me</a>
Old Question, but here is a solution.
Strictly speaking you can’t remove an anonymous event listener unless you store a reference to the function. Since the goal of using an anonymous function is presumably not to create a new variable, you could instead store the reference in the element itself:
element.addEventListener('click',element.fn=function fn() {
// Event Code
}, false);
Later, when you want to remove it, you can do the following:
element.removeEventListener('click',element.fn, false);
Remember, the third parameter (false) must have the same value as for adding the Event Listener.
However, the question itself begs another: why?
There are two reasons to use .addEventListener() rather than the simpler .onsomething() method:
First, it allows multiple event listeners to be added. This becomes a problem when it comes to removing them selectively: you will probably end up naming them. If you want to remove them all, then #tidy-giant’s outerHTML solution is excellent.
Second, you do have the option of choosing to capture rather than bubble the event.
If neither reason is important, you may well decide to use the simpler onsomething method.
Yes you can remove an anonymous event listener:
const controller = new AbortController();
document.addEventListener(
"click",
() => {
// do function stuff
},
{ signal: controller.signal }
);
You then remove the event listener like this:
controller.abort();
You may try to overwrite element.addEventListener and do whatever you want.Something like:
var orig = element.addEventListener;
element.addEventListener = function (type, listener) {
if (/dontwant/.test(listener.toSource())) { // listener has something i dont want
// do nothing
} else {
orig.apply(this, Array.prototype.slice.apply(arguments));
}
};
ps.: it is not recommended, but it will do the trick (haven't tested it)
Assigning event handlers with literal functions is tricky- not only is there no way to remove them, without cloning the node and replacing it with the clone- you also can inadvertantly assign the same handler multiple times, which can't happen if you use a reference to a handler. Two functions are always treated as two different objects, even if they are character identical.
Edit: As Manngo suggested per comment, you should use .off() instead of .unbind() as .unbind() is deprecated as of jQuery 3.0 and superseded since jQuery 1.7.
Even though this an old question and it does not mention jQuery I will post my answer here as it is the first result for the searchterm 'jquery remove anonymous event handler'.
You could try removing it using the .off() function.
$('#button1').click(function() {
alert('This is a test');
});
$('#btnRemoveListener').click(function() {
$('#button1').off('click');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="button1">Click me</button>
<hr/>
<button id="btnRemoveListener">Remove listener</button>
However this only works if you've added the listener using jQuery - not .addEventListener
Found this here.
If you're using jQuery try off method
$("element").off("event");
Jquery .off() method removes event handlers that were attached with .on()
With ECMAScript2015 (ES2015, ES6) language specification, it is possible to do with this nameAndSelfBind function that magically turns an anonymous callback into a named one and even binds its body to itself, allowing the event listener to remove itself from within as well as it to be removed from an outer scope (JSFiddle):
(function()
{
// an optional constant to store references to all named and bound functions:
const arrayOfFormerlyAnonymousFunctions = [],
removeEventListenerAfterDelay = 3000; // an auxiliary variable for setTimeout
// this function both names argument function and makes it self-aware,
// binding it to itself; useful e.g. for event listeners which then will be able
// self-remove from within an anonymous functions they use as callbacks:
function nameAndSelfBind(functionToNameAndSelfBind,
name = 'namedAndBoundFunction', // optional
outerScopeReference) // optional
{
const functionAsObject = {
[name]()
{
return binder(...arguments);
}
},
namedAndBoundFunction = functionAsObject[name];
// if no arbitrary-naming functionality is required, then the constants above are
// not needed, and the following function should be just "var namedAndBoundFunction = ":
var binder = function()
{
return functionToNameAndSelfBind.bind(namedAndBoundFunction, ...arguments)();
}
// this optional functionality allows to assign the function to a outer scope variable
// if can not be done otherwise; useful for example for the ability to remove event
// listeners from the outer scope:
if (typeof outerScopeReference !== 'undefined')
{
if (outerScopeReference instanceof Array)
{
outerScopeReference.push(namedAndBoundFunction);
}
else
{
outerScopeReference = namedAndBoundFunction;
}
}
return namedAndBoundFunction;
}
// removeEventListener callback can not remove the listener if the callback is an anonymous
// function, but thanks to the nameAndSelfBind function it is now possible; this listener
// removes itself right after the first time being triggered:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
e.target.removeEventListener('visibilitychange', this, false);
console.log('\nEvent listener 1 triggered:', e, '\nthis: ', this,
'\n\nremoveEventListener 1 was called; if "this" value was correct, "'
+ e.type + '"" event will not listened to any more');
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to prove that deanonymized functions -- even when they have the same 'namedAndBoundFunction'
// name -- belong to different scopes and hence removing one does not mean removing another,
// a different event listener is added:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
console.log('\nEvent listener 2 triggered:', e, '\nthis: ', this);
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to check that arrayOfFormerlyAnonymousFunctions constant does keep a valid reference to
// formerly anonymous callback function of one of the event listeners, an attempt to remove
// it is made:
setTimeout(function(delay)
{
document.removeEventListener('visibilitychange',
arrayOfFormerlyAnonymousFunctions[arrayOfFormerlyAnonymousFunctions.length - 1],
false);
console.log('\nAfter ' + delay + 'ms, an event listener 2 was removed; if reference in '
+ 'arrayOfFormerlyAnonymousFunctions value was correct, the event will not '
+ 'be listened to any more', arrayOfFormerlyAnonymousFunctions);
}, removeEventListenerAfterDelay, removeEventListenerAfterDelay);
})();
//get Event
let obj = window; //for example
let eventStr= "blur"; //for example
let index= 0; //you can console.log(getEventListeners(obj)[eventStr]) and check index
let e = getEventListeners(obj)[eventStr][index];
//remove this event
obj .removeEventListener(eventStr,e.listener,e.useCapture);
THE END :)
i test in chrome 92, worked
How I used options parameter for my customEvent
options Optional
An object that specifies characteristics about the event listener. The available options are:
...
**once**
A boolean value indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
for my custom function that I created, it worked quite nicely.
const addItemOpenEventListener = (item, openItem) => {
document.addEventListener('order:open', ({detail}) => {
if(detail.id === item.id) {
openItem();
}
}, {once: true})
};
el.addItemOpenEventListener(item, () => dispatch(itemOpen)()));
checked my console, seems like it worked (any feedback appreciated!)
The following worked well enough for me. The code handles the case where another event triggers the listener's removal from the element. No need for function declarations beforehand.
myElem.addEventListener("click", myFunc = function() { /*do stuff*/ });
/*things happen*/
myElem.removeEventListener("click", myFunc);

Removing DOM Event Observer

I'm working in a javascript based system with some legacy code (ominous music), and this legacy code adds event listeners like this
foo.addEventListener("click",function(){
//do some stuff
});
Is there a way for me to programmatically remove event listeners that have been added like this? I know about removeEventListener, but's it's not clear from the documentation how (if at all) to remove a listener which a programmer added via an anonymous function.
As far as I can tell, you can't use an anonymous function if you want to call removeEventListener because you need a reference to the same function that you used with addEventListener and the only way to do that is to have a name for the function (e.g. not anonymous).
Similar question and conclusion here: removeEventListener on anonymous functions in JavaScript
Without changing the structure of your code, you can give it a name and then use that name later.
foo.addEventListener("click", function fooClickHandler(){
//do some stuff
});
// then later
foo.removeEventListener("click", fooClickHandler);
You can remove them the same way that you add them by passing in the handler that you created it with. The handler ceases to be an anonymous function at this point though:
var handler = function() {
// do some stuff
};
foo.addEventListener("click", handler);
foo.removeEventListener("click", handler);
You can do some funky stuff like this to remove handlers with anonymous functions although I don't recommend it, but it is possible and you can't access callee in strict mode:
document.addEventListener('click', function(e) {
console.log('click');
if(e.unbind) {
document.removeEventListener('click', arguments.callee);
}
});
var event;
// First actual real event
event = new Event('click');
document.dispatchEvent(event);
// Unbinding event
event = new Event('click');
event.unbind = true;
document.dispatchEvent(event);
// Should not fire
event = new Event('click');
document.dispatchEvent(event);
If you want to get rid of all eventlisteners before adding your own you can use clonenode and remove original. The copy will not have the eventlisteners copied with it.
var fee = foo.cloneNode();
foo.parentNode.replaceChild(fee, foo);
Should look the same but without eventlistener.
Got a fiddle here that proves my point: http://jsfiddle.net/U7w7M/1/
Notice how the formatting stays, the position is the same, but click action is removed after first click

Multiple OnBeforeUnload

I am writing a JS which is used as a plugin. The JS has an onbeforeunload event.
I want suggestions so that my onbeforeunload event doesn't override the existing onbeforeunload event (if any). Can I append my onbeforeunload to the existing one?
Thanks.
I felt this has not been answered completely, because no examples were shown using addEventListener (but The MAZZTer pointed out the addEventListener solution though). My solution is the same as Julian D. but without using jQuery, only native javascript.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Before Unload</title>
</head>
<body>
<p>Test</p>
<script>
window.addEventListener('beforeunload', function (event) {
console.log('handler 1')
event.preventDefault()
event.returnValue = ''
});
window.addEventListener('beforeunload', function (event) {
console.log('handler 2')
});
</script>
</body>
</html>
In this example, both listeners will be executed. If any other beforeunload listeners were set, it would not override them. We would get the following output (order is not guaranteed):
handler 1
handler 2
And, importantly, if one or more of the event listener does event.preventDefault(); event.returnValue = '', a prompt asking the user if he really wants to reload will occur.
This can be useful if you are editing a form and at the same time you are downloading a file via ajax and do not want to lose data on any of these action. Each of these could have a listener to prevent page reload.
const editingForm = function (event) {
console.log('I am preventing losing form data')
event.preventDefault()
event.returnValue = ''
}
const preventDownload = function (event) {
console.log('I am preventing a download')
event.preventDefault()
event.returnValue = ''
}
// Add listener when the download starts
window.addEventListener('beforeunload', preventDownload);
// Add listener when the form is being edited
window.addEventListener('beforeunload', editingForm);
// Remove listener when the download ends
window.removeEventListener('beforeunload', preventDownload);
// Remove listener when the form editing ends
window.removeEventListener('beforeunload', editingForm);
You only need to take care of this if you are not using event observing but attach your onbeforeunload handler directly (which you should not). If so, use something like this to avoid overwriting of existing handlers.
(function() {
var existingHandler = window.onbeforeunload;
window.onbeforeunload = function(event) {
if (existingHandler) existingHandler(event);
// your own handler code here
}
})();
Unfortunately, you can't prevent other (later) scripts to overwrite your handler. But again, this can be solved by adding an event listener instead:
$(window).unload(function(event) {
// your handler code here
});
My idea:
var callbacks = [];
window.onbeforeunload = function() {
while (callbacks.length) {
var cb = callbacks.shift();
typeof(cb)==="function" && cb();
}
}
and
callbacks.push(function() {
console.log("callback");
});
Try this:
var f = window.onbeforeunload;
window.onbeforeunload = function () {
f();
/* New code or functions */
}
You can modify this function many times , without losing other functions.
If you bind using jQuery, it will append the binding to the existing list, so there is no need to worry.
From the jQuery Docs on() method:
As of jQuery 1.4, the same event handler can be bound to an element
multiple times.
function greet(event) { alert("Hello "+event.data.name); }
$("button").on("beforeunload", { name: "Karl" }, greet);
$("button").on("beforeunload", { name: "Addy" }, greet);
You can use different javascript frameworks like jquery or you could probably add a small event add handler to do this. Like you have an object thatcontains a number of functions that you have added and then in the onbefore unload you run the added functions. So when you want to add a new function to the event you add it to your object instead.
something like this:
var unloadMethods = [];
function addOnBeforeUnloadEvent(newEvent) { //new Event is a function
unloadMethods[unloadMethods.length] = newEvent;
}
window.onbeforeunload = function() {
for (var i=0; i<unloadMethods.length; i++) {
if(typeof unloadMethods[i] === "function") {unloadMethods[i]();}
}
}
Those frameworks mentioned use addEventListener internally. If you are not using a framework, use that.
https://developer.mozilla.org/en-US/docs/DOM/element.addEventListener
For older versions of IE you should have a fallback to use attachEvent instead:
http://msdn.microsoft.com/en-us/library/ie/ms536343(v=vs.85).aspx
I liked Marius's solution, but embellished on it to cater for situations where the var f is null, and to return the first string returned by any function in the chain:
function eventBeforeUnload(nextFN){
//some browsers do not support methods in eventAdd above to handle window.onbeforeunload
//so this is a way of attaching more than one event listener by chaining the functions together
//The onbeforeunload expects a string as a return, and will pop its own dialog - this is browser behavior that can't
//be overridden to prevent sites stopping you from leaving. Some browsers ignore this text and show their own message.
var firstFN = window.onbeforeunload;
window.onbeforeunload = function () {
var x;
if (firstFN) {
//see if the first function returns something
x = firstFN();
//if it does, return that
if (x) return x;
}
//return whatever is returned from the next function in the chain
return nextFN();
}
}
In your code where required use it as such
eventBeforeUnload(myFunction);
//or
eventBeforeUnload(function(){if(whatever) return 'unsaved data';);

Categories

Resources