popstate HTML 5 event being fired multiple times - javascript

Hi i am using the following code to load a part of page dynamically using jquery
loadNextBackInPage_URL = null;
function callBackFunctionLoadNextBackInPage(data)
{
//alert(data);
$("#left").fadeTo(100,1);
var data = $(data).find( '#left' );
$("#left").html(data);
if(supports_history_api())
{
history.pushState(null, null, loadNextBackInPage_URL);
window.addEventListener("popstate", function(e) {
alert('s');
loadNextBackInPage(location.pathname);
});
}
else
{
}
}
function loadNextBackInPage(url,parm)
{
//alert(url);
loadNextBackInPage_URL = url;
$("#left").fadeTo(100,.2);
$.post(url,parm,callBackFunctionLoadNextBackInPage,'html');
}
The loading part and even changing the browser URL is working. but why is the PoP state function being fired multiple times?
I call loadNextBackInPage() originally through an onclick function.

I got it solved from here in codingforums
think you keep adding "popstate"
listeners over and over ...
Program logic:
Page loaded
onclick will execute loadNextBackInPage()
Start a $.post() Request and fire "callBackFunctionLoadNextBackInPage" on completion
pushState()
Register an event listener for "popstate"
When "popstate" fires, execute loadNextBackInPage() and return to step 2
So step 4 will be executed over and
over which will register new event
listeners. Every time "popstate" fires
all the event listeners will execute
Try to move the addEventListener
method call out of this loop
So from those i derived a workaround and also corrected location.pathname to location.href
The corrected code:
loadNextBackInPage_URL = null;
popEventListnerAdded = false;
function callBackFunctionLoadNextBackInPage(data)
{
//alert(data);
$("#left").fadeTo(100,1);
var data = $(data).find( '#left' );
$("#left").html(data);
if(supports_history_api())
{
history.pushState(null, null, loadNextBackInPage_URL);
if(!popEventListnerAdded) {
window.addEventListener("popstate", function(e) {
loadNextBackInPage(location.href);
});
popEventListnerAdded = true;
}
}
else
{
}
}
function loadNextBackInPage(url,parm)
{
//alert(url);
loadNextBackInPage_URL = url;
$("#left").fadeTo(100,.2);
$.post(url,parm,callBackFunctionLoadNextBackInPage,'html');
}

Related

Detect Close window event function [duplicate]

I want to capture the browser window/tab close event.
I have tried the following with jQuery:
jQuery(window).bind(
"beforeunload",
function() {
return confirm("Do you really want to close?")
}
)
But it works on form submission as well, which is not what I want. I want an event that triggers only when the user closes the window.
The beforeunload event fires whenever the user leaves your page for any reason.
For example, it will be fired if the user submits a form, clicks a link, closes the window (or tab), or goes to a new page using the address bar, search box, or a bookmark.
You could exclude form submissions and hyperlinks (except from other frames) with the following code:
var inFormOrLink;
$('a').on('click', function() { inFormOrLink = true; });
$('form').on('submit', function() { inFormOrLink = true; });
$(window).on("beforeunload", function() {
return inFormOrLink ? "Do you really want to close?" : null;
})
For jQuery versions older than 1.7, try this:
var inFormOrLink;
$('a').live('click', function() { inFormOrLink = true; });
$('form').bind('submit', function() { inFormOrLink = true; });
$(window).bind("beforeunload", function() {
return inFormOrLink ? "Do you really want to close?" : null;
})
The live method doesn't work with the submit event, so if you add a new form, you'll need to bind the handler to it as well.
Note that if a different event handler cancels the submit or navigation, you will lose the confirmation prompt if the window is actually closed later. You could fix that by recording the time in the submit and click events, and checking if the beforeunload happens more than a couple of seconds later.
Maybe just unbind the beforeunload event handler within the form's submit event handler:
jQuery('form').submit(function() {
jQuery(window).unbind("beforeunload");
...
});
For a cross-browser solution (tested in Chrome 21, IE9, FF15), consider using the following code, which is a slightly tweaked version of Slaks' code:
var inFormOrLink;
$('a').live('click', function() { inFormOrLink = true; });
$('form').bind('submit', function() { inFormOrLink = true; });
$(window).bind('beforeunload', function(eventObject) {
var returnValue = undefined;
if (! inFormOrLink) {
returnValue = "Do you really want to close?";
}
eventObject.returnValue = returnValue;
return returnValue;
});
Note that since Firefox 4, the message "Do you really want to close?" is not displayed. FF just displays a generic message. See note in https://developer.mozilla.org/en-US/docs/DOM/window.onbeforeunload
window.onbeforeunload = function () {
return "Do you really want to close?";
};
My answer is aimed at providing simple benchmarks.
HOW TO
See #SLaks answer.
$(window).on("beforeunload", function() {
return inFormOrLink ? "Do you really want to close?" : null;
})
How long does the browser take to finally shut your page down?
Whenever an user closes the page (x button or CTRL + W), the browser executes the given beforeunload code, but not indefinitely. The only exception is the confirmation box (return 'Do you really want to close?) which will wait until for the user's response.
Chrome: 2 seconds.
Firefox: ∞ (or double click, or force on close)
Edge: ∞ (or double click)
Explorer 11: 0 seconds.
Safari: TODO
What we used to test this out:
A Node.js Express server with requests log
The following short HTML file
What it does is to send as many requests as it can before the browser shut downs its page (synchronously).
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
function request() {
return $.ajax({
type: "GET",
url: "http://localhost:3030/" + Date.now(),
async: true
}).responseText;
}
window.onbeforeunload = () => {
while (true) {
request();
}
return null;
}
</script>
</body>
</html>
Chrome output:
GET /1480451321041 404 0.389 ms - 32
GET /1480451321052 404 0.219 ms - 32
...
GET /hello/1480451322998 404 0.328 ms - 32
1957ms ≈ 2 seconds // we assume it's 2 seconds since requests can take few milliseconds to be sent.
For a solution that worked well with third party controls like Telerik (ex.: RadComboBox) and DevExpress that use the Anchor tags for various reasons, consider using the following code, which is a slightly tweaked version of desm's code with a better selector for self targeting anchor tags:
var inFormOrLink;
$('a[href]:not([target]), a[href][target=_self]').live('click', function() { inFormOrLink = true; });
$('form').bind('submit', function() { inFormOrLink = true; });
$(window).bind('beforeunload', function(eventObject) {
var returnValue = undefined;
if (! inFormOrLink) {
returnValue = "Do you really want to close?";
}
eventObject.returnValue = returnValue;
return returnValue;
});
I used Slaks answer but that wasn't working as is, since the onbeforeunload returnValue is parsed as a string and then displayed in the confirmations box of the browser. So the value true was displayed, like "true".
Just using return worked.
Here is my code
var preventUnloadPrompt;
var messageBeforeUnload = "my message here - Are you sure you want to leave this page?";
//var redirectAfterPrompt = "http://www.google.co.in";
$('a').live('click', function() { preventUnloadPrompt = true; });
$('form').live('submit', function() { preventUnloadPrompt = true; });
$(window).bind("beforeunload", function(e) {
var rval;
if(preventUnloadPrompt) {
return;
} else {
//location.replace(redirectAfterPrompt);
return messageBeforeUnload;
}
return rval;
})
Perhaps you could handle OnSubmit and set a flag that you later check in your OnBeforeUnload handler.
Unfortunately, whether it is a reload, new page redirect, or browser close the event will be triggered. An alternative is catch the id triggering the event and if it is form dont trigger any function and if it is not the id of the form then do what you want to do when the page closes. I am not sure if that is also possible directly and is tedious.
You can do some small things before the customer closes the tab. javascript detect browser close tab/close browser but if your list of actions are big and the tab closes before it is finished you are helpless. You can try it but with my experience donot depend on it.
window.addEventListener("beforeunload", function (e) {
var confirmationMessage = "\o/";
/* Do you small action code here */
(e || window.event).returnValue = confirmationMessage; //Gecko + IE
return confirmationMessage; //Webkit, Safari, Chrome
});
https://developer.mozilla.org/en-US/docs/Web/Reference/Events/beforeunload?redirectlocale=en-US&redirectslug=DOM/Mozilla_event_reference/beforeunload
jQuery(window).bind("beforeunload", function (e) {
var activeElementTagName = e.target.activeElement.tagName;
if (activeElementTagName != "A" && activeElementTagName != "INPUT") {
return "Do you really want to close?";
}
})
If your form submission takes them to another page (as I assume it does, hence the triggering of beforeunload), you could try to change your form submission to an ajax call. This way, they won't leave your page when they submit the form and you can use your beforeunload binding code as you wish.
As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers. Users of older versions of jQuery should use .delegate() in preference to .live()
$(window).bind("beforeunload", function() {
return true || confirm("Do you really want to close?");
});
on complete or link
$(window).unbind();
Try this also
window.onbeforeunload = function ()
{
if (pasteEditorChange) {
var btn = confirm('Do You Want to Save the Changess?');
if(btn === true ){
SavetoEdit();//your function call
}
else{
windowClose();//your function call
}
} else {
windowClose();//your function call
}
};
My Issue: The 'onbeforeunload' event would only be triggered if there were odd number of submits(clicks). I had a combination of solutions from similar threads in SO to have my solution work. well my code will speak.
<!--The definition of event and initializing the trigger flag--->
$(document).ready(function() {
updatefgallowPrompt(true);
window.onbeforeunload = WarnUser;
}
function WarnUser() {
var allowPrompt = getfgallowPrompt();
if(allowPrompt) {
saveIndexedDataAlert();
return null;
} else {
updatefgallowPrompt(true);
event.stopPropagation
}
}
<!--The method responsible for deciding weather the unload event is triggered from submit or not--->
function saveIndexedDataAlert() {
var allowPrompt = getfgallowPrompt();
var lenIndexedDocs = parseInt($('#sortable3 > li').size()) + parseInt($('#sortable3 > ul').size());
if(allowPrompt && $.trim(lenIndexedDocs) > 0) {
event.returnValue = "Your message";
} else {
event.returnValue = " ";
updatefgallowPrompt(true);
}
}
<!---Function responsible to reset the trigger flag---->
$(document).click(function(event) {
$('a').live('click', function() { updatefgallowPrompt(false); });
});
<!--getter and setter for the flag---->
function updatefgallowPrompt (allowPrompt){ //exit msg dfds
$('body').data('allowPrompt', allowPrompt);
}
function getfgallowPrompt(){
return $('body').data('allowPrompt');
}
Just verify...
function wopen_close(){
var w = window.open($url, '_blank', 'width=600, height=400, scrollbars=no, status=no, resizable=no, screenx=0, screeny=0');
w.onunload = function(){
if (window.closed) {
alert("window closed");
}else{
alert("just refreshed");
}
}
}
var validNavigation = false;
jQuery(document).ready(function () {
wireUpEvents();
});
function endSession() {
// Browser or broswer tab is closed
// Do sth here ...
alert("bye");
}
function wireUpEvents() {
/*
* For a list of events that triggers onbeforeunload on IE
* check http://msdn.microsoft.com/en-us/library/ms536907(VS.85).aspx
*/
window.onbeforeunload = function () {
debugger
if (!validNavigation) {
endSession();
}
}
// Attach the event keypress to exclude the F5 refresh
$(document).bind('keypress', function (e) {
debugger
if (e.keyCode == 116) {
validNavigation = true;
}
});
// Attach the event click for all links in the page
$("a").bind("click", function () {
debugger
validNavigation = true;
});
// Attach the event submit for all forms in the page
$("form").bind("submit", function () {
debugger
validNavigation = true;
});
// Attach the event click for all inputs in the page
$("input[type=submit]").bind("click", function () {
debugger
validNavigation = true;
});
}`enter code here`
Following worked for me;
$(window).unload(function(event) {
if(event.clientY < 0) {
//do whatever you want when closing the window..
}
});

How is the parameter involved in order to let function one "know" that it shall react to what happens in function two?

The following code actually works, but I don't understand why. How come that when I pass the "event"-parameter to the function zaehle(), the function actually "knows" that it is supposed to react on what happens in the setup function?
I just can't see what connnects the zaehle() and the setup() function or how the parameter that I pass to zaehle() would be involved.
I hope I could make the question clear. If not I'll gladly try to explain it somehow else. It really bugs me and I feel like I can't go on studying until I get it.
<body>
<div id="eins">0</div>
<div id="zwei">0</div>
<div id="drei">0</div>
<div id="vier">0</div>
<div id="funf">0</div>
</body>
JS
var mouseoverZaehler = 0;
function zaehle(event) {
mouseoverZaehler++;
event.target.innerHTML = mouseoverZaehler;
}
function setup() {
document.getElementById("eins").addEventListener("mouseover", zaehle);
document.getElementById("zwei").addEventListener("mouseover", zaehle);
document.getElementById("drei").addEventListener("mouseover", zaehle);
document.getElementById("vier").addEventListener("mouseover", zaehle);
document.getElementById("funf").addEventListener("mouseover", zaehle);
}
window.addEventListener("load", setup);
Here is what happens step by step:
Page loads
setup function is called (because of window.addEventListener("load", setup))
Each element in setup function gets a mouseover event listener attached to it and when it fires zaehle function is called (because of document.getElementById("number").addEventListener("mouseover", zaehle))
You move your mouse over any of the elements
zaehle function gets called - mouseoverZaehler is incremented and innerHTML of the targeted element is set to the updated value of mouseoverZaehler
Check out addEventListener docs for further details.
The addEventListener calls in your setup function tell the browser that when a mouseover event occurs on the relevant element, it should call the function you're giving it (zaehle, in your case). It's the browser that passes the argument to zaehle, later, when calling it.
You could imagine addEventListener, conceptually, as putting that handler function on a list for the event on the element:
// VERY conceptual, leaves out a lot of details
function addEventListener(eventName, handler) {
this.events[eventName].handlers.push(handler);
}
...and then later, when the event occurs, the browser creates an event object and calls those handlers:
// Again, VERY conceptual, leaves out a lot of details
var event = /*...*/;
element.events[eventName].handlers.forEach(function(handler) {
handler.call(element, event);
});
Here's a working analogue of what's going on:
function FakeElement () {
this.events = Object.create(null);
}
FakeElement.prototype.addEventListener = function(eventName, handler) {
var eventEntry = this.events[eventName];
if (!eventEntry) {
eventEntry = this.events[eventName] = {
handlers: []
};
}
eventEntry.handlers.push(handler);
};
FakeElement.prototype.trigger = function(eventName) {
var event = {type: eventName}; // "Browser" creates the event
var eventEntry = this.events[eventName];
var handlers = eventEntry && eventEntry.handlers;
if (handlers) {
handlers.forEach(function(handler) {
handler.call(this, event); // "Browser" calls handler, passing
}); // the event into it
}
};
// Using it:
function zaehle(event) {
console.log("zaehle got event: " + event.type);
}
var e = new FakeElement();
e.addEventListener("mouseover", zaehle);
console.log("added handler for mouseover to element");
// Simulate the event occurring
var timer = setInterval(function() {
e.trigger("mouseover");
}, 500);
setTimeout(function() {
clearInterval(timer);
}, 3000);
You have registered your callback/function zaehle() for mouseover event. So when that event occurs for a specific div, browser calls the callback with event object which contains information about the event and the target i.e event occurred on which element.

how to get jquery to run repeatedly

I currently have a piece of jquery code that looks for a specific URL (with an anchor at the end) and runs a function if it has a match. The code only runs once, if this is the first URL loaded. Is it possible to have the following code running until it has a match?
$(document).ready(function(){
var url = "https://s3-eu-west-1.amazonaws.com/datahealthcheck16-test/index.html#backup-section-3";
$(function(){
if (location.href==url){
paintLine();
}
})
});
It only runs the first time, because changing the hash does not fire the DOM ready handler again, it does however fire the hashchange event.
$(window).on('hashchange', function() {
if ( window.location.hash === '#backup-section-3' ) {
paintLine();
}
}).trigger('hashchange'); // fire on first load as well
Note that the window is always available, and does not need a DOM ready handler
you can use setTimeout() function to run your function, for example every second:
$(document).ready(function(){
var url = "https://s3-eu-west-1.amazonaws.com/datahealthcheck16-test/index.html#backup-section-3";
function test() {
if (location.href == url) {
paintLine();
} else {
setTimeout(test, 1000);
}
}
test();
});
but what is your idea, behind your code? I sure there is more convenient ways to do your task.
using adeneo's answer:
here is what matches your code:
$(document).ready(function(){
var url = "https://s3-eu-west-1.amazonaws.com/datahealthcheck16-test/index.html#backup-section-3";
$(function(){
if (location.href==url){
paintLine();
}
});
$(window).on('hashchange', function() {
if ( location.href == url ) {
paintLine();
}
});
});

event listeners compounding after each page refresh

So I used this tutorial to build a custom right click menu for some graphs: https://www.sitepoint.com/building-custom-right-click-context-menu-javascript/
It's working great, but I have one problem with the listeners. Specifically, when contextListeners and clickListeners functions are called, event listeners are added when the page loads. However, in my application, I have a button which redraws these graphs and reloads the page that the custom right click menu is attached to, and every time, new listeners are created. As a result, the function associated with the right click menu is called twice if I load two different graphs, and thrice if I load it three times. Here's the function that keeps getting called, and keeps adding listeners
function contextListener() {
document.addEventListener("contextmenu", function(e) {
taskItemInContext = clickInsideElement(e, taskItemClassName);
if (taskItemInContext) {
e.preventDefault();
toggleMenuOn();
positionMenu(e);
} else {
taskItemInContext = null;
toggleMenuOff();
}
});
}
and in a later function I tried:
document.removeEventListener("contextmenu", function(e) {
console.log("removed");
});
but that doesn't seem to do the trick.
i don`t understand you question well ,but you can try this :
function contextListener() {
if(!document.handAddcontext){
document.handAddcontext = true
document.addEventListener("contextmenu", function(e) {
taskItemInContext = clickInsideElement(e, taskItemClassName);
if (taskItemInContext) {
e.preventDefault();
toggleMenuOn();
positionMenu(e);
} else {
taskItemInContext = null;
toggleMenuOff();
}
});
}
}

How to trigger a custom javascript event in IE8?

I'm trying to fire a custom event on IE8 and fiddling a solution together from here and here. But I cannot get it to work...
I'm using jquery mobile with requireJS and google analytics. So I'm tracking the JQM pageshow event. However since requireJS loads scripts async, my binding to pageshow needs to be made in a javascript "wrapper", otherwise it will produce an error, because neither jquery nor jquery mobile will have been loaded by the time the snippet is parsed.
So I'm doing including this at the end of every page:
if (document.addEventListener) {
document.addEventListener("jqmReady",function(){trigAnalytics("jqmReady");alert("FF detected")},false);
} else if ( document.attachEvent ) {
document.attachEvent("jqmReady", function(){trigAnalytics("jqmReady");alert("IE detected")});
}
And when detected, I'm firing my analytics snippet with the pageshow binding:
var trigAnalytics = function( trigger ){
$(document).on('pageshow','div:jqmData(role="page").basePage', function (event, ui) {
var url = location.href;
try {
hash = location.hash;
if (hash && hash.length > 1) {
_gaq.push(['_trackPageview', hash.substr(1)]);
_gaq.push(['_setCustomVar', 1, 'id_external', ########, 1 ]);
} else {
_gaq.push(['_trackPageview', url]);
_gaq.push(['_setCustomVar', 1, 'id_external', ########, , 1 ]);
_gaq.push(['b._trackPageview', url]);
}
} catch(err) { }
});
if (typeof _gaq !== "undefined" && _gaq !== null) {
$(document).ajaxSend(function(event, xhr, settings){
_gaq.push(['_trackPageview', settings.url]);
_gaq.push(['b._trackPageview', settings.url]);
});
}
};
So to kick of the event chain, I need to trigger jqmReady when JQM is ready. JQM uses their mobileinit event to indicate just that. So inside my application controller init, I'm binding to it like so:
$(document).bind("mobileinit", function () {
// non-IE OK
if (document.createEvent) {
evt = document.createEvent("Event");
evt.initEvent("jqmReady", true, true);
document.dispatchEvent(evt);
} else if (document.createEventObject) {
// MSIE (NOT WORKING)
document.documentElement.evt = 0; // an expando property
document.documentElement.attachEvent("jqmReady", function () {
document.documentElement.evt = document.documentElement.evt + 1;
});
}
});
I have tried just triggering $(window).trigger('jqmReady'), because when mobileinit triggers, jquery is available. However it seems events created in addEventListener can not be triggered like this, so I need a javascript-only solution to trigger a custom event in IE.
Question:
Can someone give me a pointer on how to trigger a javascript custom event for IE8 correctly?
Ok, I finally understand... here is how it works:
1) setting the listener for jqmReady on the page being loaded
// non-IE: just create a listener for the custom event "jqmReady"
if (document.addEventListener) {
document.addEventListener("jqmReady",function(){trigAnalytics("jqmReady");alert("FF detected")},false);
// IE8
} else if ( document.attachEvent ) {
// create a custom property name jqmReady and set it to 0
document.documentElement.jqmReady = 0;
// since IE8 does not allow to listen to custom events,
// just listen to onpropertychange
document.documentElement.attachEvent("onpropertychange", function(event) {
// if the property changed is the custom jqmReady property
if (event.propertyName == "jqmReady") {
trigAnalytics("jqmReady");
alert("gotcha")
// remove listener, since it's only used once
document.documentElement.detachEvent("onpropertychange", arguments.callee);
}
});
}
So on IE8 I'm not listening for custom jqmReady. Instead I listen for onpropertychange for my custom property jqmReady
2) Then on mobileinit I'm triggering like this:
// non-IE
if (document.createEvent) {
evt = document.createEvent("Event");
evt.initEvent("jqmReady", true, true);
document.dispatchEvent(evt);
} else if (document.createEventObject) { // MSIE
// just change the property
// this will trigger onpropertychange
document.documentElement.jqmReady++;
};
Nice idea (credit to http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/), maybe someone else can find a use for it.
For anyone else interested, I've wrapped up this code into a static javascript object
function Event () {
}
Event.listen = function (eventName, callback) {
if(document.addEventListener) {
document.addEventListener(eventName, callback, false);
} else {
document.documentElement.attachEvent('onpropertychange', function (e) {
if(e.propertyName == eventName) {
callback();
}
});
}
}
Event.trigger = function (eventName) {
if(document.createEvent) {
var event = document.createEvent('Event');
event.initEvent(eventName, true, true);
document.dispatchEvent(event);
} else {
document.documentElement[eventName]++;
}
}
usage:
Event.listen('myevent', function () {
alert('myevent triggered!');
});
Event.trigger('myevent');
Demo:
http://jsfiddle.net/c5CuF/

Categories