I am attempting to set a fairly generic OnBeforeUnload handler:
console.log("WCW - Setting browser warning message");
window.onbeforeunload = function(e) {
e.returnValue = "confirmationMessage";
return e.returnValue;
};
console.log("WCW - onbeforeunload is " +
((window.onbeforeunload ? "set":"still null")));
I understand all I can do is signal the browser to issue a fairly generic warning. In a future iteration I may attempt some self-cleanup before issuing the return.
My problem is that window.onbeforeunload always returns null in the console log after this code runs UNLESS I set a breakpoint on any line (e.g., the first log message) and then just 'continue' when it stops. THEN, it works.
I'm quite sure a "what is wrong" question is pointless, so I'm asking, "do you have any advice on how I can look for what might be the problem?".
I wonder if it's a scope issue? The 2nd log message always says onbeforeunload is non-null... But window is a global, isn't it, and how would a debugger pause fix that?
NOTES:
jQuery is available but using $(window).on('beforeunload' is subject to the same issues as this code
adding a delay (setTimeout) to set the handler does not help; tried 2, 4 & 9 seconds
adding a recursive call to add the handler every 4 seconds while the page is open does not result in the handler being set: window.onbeforeunload returns NULL in the console all the time
periodically, I've seen a jQuery core method attached to the event, but it's rare
Related
I'm running CasperJS with PhantomJS. I have it going to a url and clicking on an element based on XPath. This could happen several times without a problem, until, I suspect there is a delay in the page loading, it can't find the XPath, it throws an error and stops the test. I would like it to continue through the error. I don't want to wait+click any longer than I already am, as there are many clicks going on, and an error can be at a random click, waiting on every click is counter productive.
I have tried putting the whole test into a try catch, it wouldn't catch.
The only handling I could find just gave out more information on the error, still stopped the test.
I would wait for the selector you want to run, with a short timeout. In the success function do your click, in the timeout function report the problem (or do nothing at all).
For instance:
casper.waitForSelector('a.some-class', function() {
this.click('a.some-class');
}, function onTimeout(){
this.echo("No a.some-class found, skipping it.");
},
100); //Only wait 0.1s, as we expect it to already be there
});
(If you were already doing a casper.wait() just before this, then replace that with the above code, and increase the timeout accordingly.)
You cannot catch an error in something that is executed asynchronously. All then* and wait* functions are step functions which are asynchronous.
Darren Cook provides a good reliable solution. Here are two more which may work for you.
casper.options.exitOnError
CasperJS provides an option to disable exiting on error. It work reliably. The complete error with stacktrace is printed in the console, but the script execution continues. Although, this might have adverse effects when you also have other errors on which you may want to stop execution.
try-catch
Using a try-catch block works in CasperJS, but only on synchronous code. The following code shows an example where only the error message is printed without stacktrace:
casper.then(function() {
try {
this.click(selector);
} catch(e){
console.log("Caught", e);
}
});
or more integrated:
// at the beginning of the script
casper.errorClick = function(selector) {
try {
this.click(selector);
} catch(e){
console.log("Caught", e);
return false;
}
return true;
};
// in the test
casper.then(function() {
this.errorClick("someSelector");
});
I have created a Firefox extension, which has iframe in the popup.html.
To pass messages between iframe and main.js, I'm using postMessage method.
The popup is behaving weird when receiving message. I used console.log to get the message which is being received. The message gets console.log that many times, the times extension was clicked.
For e.g. if I had open the extension 4 times, the iframe passes the message only once, but the message gets receied 4 times.
To make you understand I'm writing step by step process:
Open a tab, and click on extension.
A message is passed from iframe to the main.js, which after receiving message, console.logs the same message. Now this message appears once.
Click anywhere to close extension. Click on extension again > message is passed > console.logs the same message twice.
Repeat this process, and it logs the message those many times, the extension is clicked.
Is this a bug with Firefox? Or I'm doing something wrong?
EDITED:
Here is some snippet from my main.js:
if (self.port !== undefined){
self.port.on("show", function(a,b) {
myApp.initialize(a,b);
});
}
var myApp = {
initialize: function (a,b){
window.addEventListener ("message" , receiveMessage, false);
function receiveMessage(event){
console.log("event.data - - - - - - ",event.data);
}
},
goAbc : function(){
self.port.emit("close");
}
}
The code in iframe is:
$(document).ready(function(){
parent.postMessage("getData", "*");
});
EDITED: One more question:
If I'm calling "myApp.goAbc()" somewhere in the code, it says, self.port is undefined. What's the issue here? Should I use something else in place of self.port?
With each "show", you call myApp.initialize, which addEventListener a new event listener. So the first time, there is one, the next time there will be another, additional one (as you call addEventListener again) for a total of two, and so on.
Instead, you need to call addEventListener exactly once and not on each "show".
I need to call a JavaScript/jQuery function which has a few lines of code in it, on a PHP page when the user closes his window/tab or navigates away by clicking a link. I've tried the onbeforeunload function but only the return "blah blah blah;" part executes and everything else is ignored. I've also tried the .unload method from jQuery but for some reason this code doesn't run.
$(window).unload(function() {
alert('blah blah blah');
});
Please suggest alternatives. Thanks..
Here is a simple working example. Whatever you return from the unload callback will be displayed in a browser popup confirmation.
Working example sending Ajax request before unload
http://jsfiddle.net/alexflav23/hujQs/7/
The easiest way to do this:
window.onbeforeunload = function(event) {
// do stuff here
return "you have unsaved changes. Are you sure you want to navigate away?";
};
in jQuery:
$(window).on("beforeunload", function() {
$.ajax("someURL", {
async: false,
data: "test",
success: function(event) {
console.log("Ajax request executed");
}
});
return "This is a jQuery version";
});
Look into the Network tab of the browser. You will see how the request is being sent as you wanted to do. Just send the appropriate data.
Bear in mind all operations triggered must be synchronous, so you can only make synchronous ajax requests for instance. However, the above is not entirely reliable for any purpose.
Opt for periodic back-up of user data to localStorage and sync with the server automatically . Keep window.onbeforeunload just as an extra precaution, but not as a main mechanism. It's well known to cause problems.
This is an old question, but I wanted to share an alternative approach that has the benefit of working with high consistency:
Establish a WebSocket connection to the server, and when the client navigates away the WebSocket connection will be closed. Server-side, you can detect the closed connection in a callback and run whatever code you need on the server.
Executing Javascript on page unload is often unreliable (as discussed in the other answer) because it's inherently at odds with the user's intention. This method will always work, although it is admittedly quite a bit more cumbersome to implement.
This does change the context of your "run before leaving" code from client-side to server-side, but I imagine for most cases the difference is inconsequential. Anything you want to run client-side before the client leaves your page is probably not going to change anything the client sees, so it's probably fine to run it server side. If there is specific data you need from the client you can send it through the WebSocket to the server.
The only situation I can think of off the top of my head where this might cause unexpected behavior is if the user loses the WS connection without actually navigating away, e.g. they lose internet or put their computer to sleep. Whether or not that's a big deal is probably highly dependent on what kind of code you're trying to execute.
In many projects of mine, the mentioned methods here are instable. The only thing that works for me is to bind the event as original attribute on the body element.
<body onunload="my_function_unload()">
jQuery method:
$('body').attr('onunload', 'my_function_unload()');
From an iframe:
<body onunload="window.parent.my_function_unload()">
jQuery method:
$('<iframe />').load(function(){
$body = $(this).contents().find('body');
$body.attr('onunload', 'window.parent.my_function_unload()');
}
Also, important, no arguments in the attribute, and the function must be in the global window scope, otherwise nothing happens.
For example, common mistake If your my_function_unload() are wrapped inside a ;( function( $ ) {... OR $(document).ready(function(){... AS my_function_unload() must be outside that private scope. And dont forget to use jQuery instead of $ prefix then. (Working with Wordpress for example)
This is kind of a pain, as Chrome, at least in Version 92.0.4515.131, seems to be clamping the security screws on what you can get away with in beforeunload. I'm unable to make a synchronous ajax request, for example.
If there's any chance the user will be back to your site and you can wait until then to deal with their having closed a window (which does work in my use case), know that setting cookies does currently seem to be fair game during the beforeunload event. Even works when I close the browser. Covers most anything but power cycling the computer, it appears.
Here's a reference example (with getCookie stolen from this SO question):
function setCookie(name, value) {
document.cookie =
'{0}={1};expires=Fri, 31 Dec 9999 23:59:59 GMT;path=/;SameSite=Lax'
.replace("{0}", name)
.replace("{1}", value);
}
// https://stackoverflow.com/a/25490531/1028230
function getCookie(cookieName) {
var b = document.cookie.match('(^|;)\\s*' + cookieName + '\\s*=\\s*([^;]+)');
return b ? b.pop() : '';
}
window.addEventListener('beforeunload', function (e) {
console.log('cookie value before reset: ' + getCookie('whenItHappened'));
var now = +new Date();
console.log("value to be set: " + now);
setCookie('whenItHappened', now);
return "some string if you want the 'are you sure you want to leave' dialog to appear";
});
I'm currently working with a large pre-existing codebase that may have one or more setInterval timers running all the time, from various plug-ins or libraries. This basically makes it impossible to try to use Break on Next to debug what happens when I click on an element.
Problem: As soon as I click Break on Next, the browser debugger (tried with Firebug and Chrome) stops in one of the setInterval functions before I have a chance to interact with the page to really debug the event that I want.
Specific problem: I have checkboxes that stay unchecked when unchecked, no matter how many times I click on them. I've removed the ID and class names as well to no avail and appear to have no event handlers attached.
Note: not using jQuery
This may brake other things, but what if you try to monkey-patch-out those calls like this:
window.setInterval = function() { console.log("setInterval", arguments); };
window.setTimeout = function() { console.log("setTimeout", arguments); };
If you find that some of timeouts/intervals are actually needed to reproduce your problem, you could try letting them through. Than the code could be something like:
window.oldSetTimeout = window.setTimeout;
window.setTimeout = function() {
if (arguments[0] == "code you want to allow") {
oldSetTimeout.apply(null, arguments);
} else {
console.log("setTimeout", arguments);
}
};
Note: I wouldn't be suprised it monkey-patching setTimeout does not work cross-browser, but it works on FF 18.0
This question already has answers here:
How to terminate the script in JavaScript?
(25 answers)
Closed 7 years ago.
Is it possible in some way to stop or terminate JavaScript in a way that it prevents any further JavaScript-based execution from occuring, without reloading the browser?
I am thinking of a JavaScript equivalent of exit() in PHP.
Short answer:
throw new Error("Something went badly wrong!");
If you want to know more, keep reading.
Do you want to stop JavaScript's execution for developing/debugging?
The expression debugger; in your code, will halt the page execution, and then your browser's developer tools will allow you to review the state of your page at the moment it was frozen.
Do you want to stop your application arbitrarily and by design?
On error?
Instead of trying to stop everything, let your code handle the error. Read about Exceptions by googling. They are a smart way to let your code "jump" to error handling procedures without using tedious if/else blocks.
After reading about them, if you believe that interrupting the whole code is absolutely the only option, throwing an exception that is not going to be "caught" anywhere except in your application's "root" scope is the solution:
// creates a new exception type:
function FatalError(){ Error.apply(this, arguments); this.name = "FatalError"; }
FatalError.prototype = Object.create(Error.prototype);
// and then, use this to trigger the error:
throw new FatalError("Something went badly wrong!");
be sure you don't have catch() blocks that catch any exception; in this case modify them to rethrow your "FatalError" exception:
catch(exc){ if(exc instanceof FatalError) throw exc; else /* current code here */ }
When a task completes or an arbitrary event happens?
return; will terminate the current function's execution flow.
if(someEventHappened) return; // Will prevent subsequent code from being executed
alert("This alert will never be shown.");
Note: return; works only within a function.
In both cases...
...you may want to know how to stop asynchronous code as well. It's done with clearTimeout and clearInterval. Finally, to stop XHR (Ajax) requests, you can use the xhrObj.abort() method (which is available in jQuery as well).
You can make a JavaScript typo :D (thinking outside the box here)
thisFunctionDoesNotExistAndWasCreatedWithTheOnlyPurposeOfStopJavascriptExecutionOfAllTypesIncludingCatchAndAnyArbitraryWeirdScenario();
Or something like:
new new
Something like this might work:
function javascript_abort()
{
throw new Error('This is not an error. This is just to abort javascript');
}
Taken from here:
http://vikku.info/codesnippets/javascript/forcing-javascript-to-abort-stop-javascript-execution-at-any-time/
I do:
setTimeout(function() { debugger; }, 5000)
this way I have 5 seconds to interact with UI and then in stops. Las time I used was when I needed to leave custom tooltip visible, to do some styling changes.
No.
Even if you throw an exception, it will only kill the current event loop. Callbacks passed to setTimeout or DOM/XMLHttpRequest event handlers will still run when their time comes.
I am using
return false;
if I want to abort from JavaScript from running further downwards.
If you're in a function you can exit it using return; but that doesn't stop execution of the parent function that called that function.
You can call return early in a function, and at least that function will stop running. You can also just use throw '' to cause an error and stop the current process. But these won't stop everything. setTimeout and setInterval can make delayed functions and functions that run on a time interval, respectively. Those will continue to run. Javascript events will also continue to work as usual.
I know this is old, but I wanted to do this and I have found, in my opinion, a slightly improved solution of the throw answers. Just temporary supress the error messages and reactivate them later using setTimeout :
setTimeout(function() {
window.onerror = function(message, url, lineNumber) {
return false;
};
}, 50); // sets a slight delay and then restores normal error reporting
window.onerror = function(message, url, lineNumber) {
return true;
};
throw new Error('controlledError');
Define a variable inside the JavaScript function, set this variable to 1 if you want ot execute the function and set it to 0 if you want to stop it
var execute;
function do_something()
{
if (execute == 1)
{
// execute your function
}
else
{
// do nothing
}
}
The process is tedious, but in Firefox:
Open a blank tab/window to create a new environment for the script
from the current page
Populate that new environment with the script to execute
Activate the script in the new environment
Close (that is, kill) that new environment to ...
stop or terminate JavaScript this [in a] way to [that it] prevent[s] any further
JavaScript-based execution from occuring, without reloading the browser
Notes:
Step 4 only stops execution of JavaScript in that environment and not the scripts of any other windows
The original page is not reloaded but a new tab/window is loaded with the script
When a tab/window is closed, everything in that environment is gone: all remnants, partial results, code, etc.
Results must migrate back to the parent or another window for preservation
To rerun the code, the above steps must be repeated
Other browsers have and use different conventions.