As we know, we can put a pop up asking if we are sure about refreshing the page when we click the refresh button on the browser. Usually it is done by adding an event listener on onbeforeunload.
However, in my Aurelia application, I try to do that but it's not working.
let's say this is my app.js ( root ModelView )
export class App {
this.refreshClicked = () =>{ return "Are you sure you want to exit?"; };
attached(){
window.addEventListener("onbeforeunload", this.refreshClicked);
}
But it is not working, however, if we replace the return statement to console.log("clicked") I can see that the listener works without any problem.
Any help?
First of all, if you are adding event handler through window.addEventListener, you don't use on prefix in that case. So it's either:
attached() {
window.addEventListener('beforeunload', this.refreshClicked);
}
or an older, not recommended, syntax:
attached() {
window.onbeforeunload(this.refreshClicked);
}
Second, you'll need to set the returnValue property on Event object and also return the same string value to support more browsers. To learn more about this event (and various browser quirks), check out its MDN page.
Your refreshClicked should look like this:
this.refreshClicked = e => {
const confirmation = 'Are you sure you want to exit?';
e.returnValue = confirmation;
return confirmation;
};
Related
Propose the following situation:
function functionExists(functionName) {
if (typeof window[functionName] == 'function') console.log("It's a function");
}
What would be an equivalent function in nodejs for functionExists where there is no global window variable?
CONCRETE SITUATION:
My concrete situation uses webpack instead of nodejs, but basically the problem is the same. I could use window here, but it would be too complicated to implement everything cleanly, and it isn't advised by webpack to mitigate things out to the window global variable.
Basically, I have a PHP backend, which generates a html <form> adding some options to it via a data attribute. When the page is loaded, my javascript initializes this <form> and gives it a bunch of functionalities (like validation for example). Another thing javascript does with this form, is that it parses the data attribute of it, and instead of the normal page reload submit, it changes the form so it is being submited over an ajax request to the server.
When this submit happens, it is set up, that the button and the whole form gets disabled, until my Ajax script sends back a response. How this is done, is that I have a Project_Form class, which when it is initialized, attaches itself to the jQuery submit event, stops the basic submit event, and runs an inner function which sends an ajax request to an api method. The ajax request is set up, that when a response is received, the same instantiated class will receive this response, so I can continue working with it.
When the form receives the response, it must do something with it. In the most basic situation, it must show a success message to the user, but there are some more complex situation, where for example, it has to make a page redirect (for example a login form). Right now, it is set up, that as a default, it will show a message, but when I define this form in PHP, I have the option to "hijack" this default behaviour, and instead of it, send the ajax response to a custom function, which will resolve the situation specifically.
When I am rendering the form in PHP, I already know where the form should send a success response (to which javascript function), but I can only provide this information to javascript, via a string. So my Project_Form class, should fetch this string, and should try to fetch a function from it which it will use. This is where my problem is coming from.
Unless you specifically KNOW that this is a global function (which is almost never the case in nodejs), functions by default in nodejs are scoped to the module and there is NO way to look them up by string name like you did with the window object in the browser, just like there is no way to look up local variables by name inside a function in Javascript.
In general, don't pass functions by string name. Or, if you have to, then you need to create a lookup table that you can check the function name against.
I'd suggest you explain the real problem you're trying to solve here because passing the function by string name is not how you would generally want to do things.
There is a bit of a hack using eval() that can see if a string represents a function name that is in scope:
// Warning, you must know that the argument f (if it is a string) does not
// contain harmful Javascript code because it will be used with eval()
function isFunction(f) {
// if already a function reference
if (typeof f === "function") {
return true;
// see if string represents a function name somewhere in scope
} else if (typeof f === "string") {
try {
return eval(`typeof ${f} === "function"`);
} catch(e) {
return false;
}
} else {
return false;
}
}
Note: This tests to see if the function is in the scope of the isFunction() function. If you want to test if it's in your current scope, then you need to do the:
eval(`typeof ${f} === "function"`)
inline in your current scope so it runs in the scope you want to do the lookup from.
To ever consider using this, you will HAVE to know that the source of your string is safe and cannot contain harmful code. But, as I said earlier, it's better to design your program differently so you aren't referring to functions by their string name.
And, here's a runnable snippet that shows it in action (also works in a node.js module):
function test() {
console.log("in test");
}
function isFunction(f) {
// if already a function reference
if (typeof f === "function") {
return true;
// see if string represents a function name somewhere in scope
} else if (typeof f === "string") {
try {
return eval(`typeof ${f} === "function"`);
} catch(e) {
return false;
}
} else {
return false;
}
}
console.log(isFunction("test")); // true
console.log(isFunction(test)); // true
console.log(isFunction("notAFunction")); // false
More added after question edit
If you only have the function name as a string and the function that it points to is not a property of some known object, then the only way I know of to turn that string into a function reference is with eval().
You could directly execute it with eval() such as eval(functionName + "()") or you could get a reference to the function with eval("let fn = " + functionName) and then use the newly defined fn variable to call the function.
If you control the various functions that could be referenced (because they're your Javascript), then you can make all those functions be a property of a known object in your Javsacript:
const functionDispatcher = {
function1,
function2,
function3,
function4
}
Then, instead of using eval(), you can reference them off the functionDispatcher object like you would have referenced before with window (except this isn't a global) as in:
functionDispatcher[someFunctionName]();
This would be a preferred option over using eval() since there is less risk of insertion of random code via an unsafe string.
In node.js you can achieve this like:
function functionExists(functionName) {
if(functionName && typeof functionName === "function")
console.log("It is a function");
}
Hope this works for you.
If there a way to perform .click() after the element become visible.
My function chain is built like that:
this.remote.findByXpath("//div[#data-index='blockContainer']/button[text()='Create new']").then(function(element) {
return element.click().end();
})
Sometimes I got error says 'the element is not visible', is it possible to perform click after the element displayed in browser? I know Leadfoot supplies pollUntil to do similar thing but I don't want to execute xpath at browser side, instead of I want to do until at running server side.
To solve my problem I tried following two ways but doesn't help:
I tried to pass Leadfoot Element to browser side script and check if it is visible. But it seems browser side code doesn't recognize leadfoot/element object.
command.find(...).then(function(element) {
return command.then(pollUntil(
function(element) {
if (element.style.display == 'none') return null;
return true;
}, [element], 60000, 500)).then(function(el){
});
}).click().end();
Also tried to customize pollUntil myself but doesn't work as well
function pollVisible(element, timeout) {
var dfd = new Deferred();
var endTime = Number(new Date()) + timeout;
(function poll() {
element.isDisplayed().then(function (displayed) {
if (displayed) {
dfd.resolve();
}
else if (Number(new Date()) < endTime) {
setTimeout(poll, 500);
}
else {
var error = new Error('timed out; final url is ' + url);
dfd.reject(error);
}
});
})();
return dfd.promise;
}
You've probably had an answer to this by now but here's my solution to this just in case you're still unsure or if anyone else comes across this issue.
I'm not sure why you are polling until an element is visible here. What I would do is set the find timeout of your leadfoot/Session as follows:
this.remote.setFindTimeout(60000)
Then when you invoke the this.remote.findByXPath method, it will automatically search for your element for a maximum of 1 minute (in the case of my above example). If it finds the element within that time, it will then proceed to the next step in your code. If it doesn't find the element within that time, the test case will time out.
You can then simplify your code to (for example):
this.remote
.setFindTimeout(60000)
.findByXpath("//div[#data-index='blockContainer']/button[text()='Create new']")
.click()
.end();
Of course there's no need to set the find timeout every time you wish to find an element in the UI. You can set it once somewhere more appropriate (ie. at the beginning of your test) and it will remain in place for the duration of your test. I'm just doing it here as a means of documenting a full example for you.
Hope this helps!
How to identify a tab is reloading, I mean actual page reload?
I see chrome.tabs.onUpdated event, but for this event status is 'loading' even in case of AJAX calls from a webpage.
How can I detect a page is getting reloaded ?
You are right, looks like not possible to recognize AJAX and page reload calls. As workaround you could listen for onunload event for tab webpage. You probably need to check if tabid and url were not changed after that.
But do you really need to know if page reloaded?
It's an old question, but here is my solution that could be in help (without AIAX).
Since the method 'chrome.tabs.get()' return a promise, you can use the 'callback function' to check the current 'tab.status'.
Setting a boolean variable 'waitingForComplete = true', you will run your code, only when the tab will return at status = 'complete'.
background.js
var waitingForComplete = false;
chrome.tabs.onUpdated.addListener((tabId. changeInfo,tab) => {
if(changeInfo.status == 'complete'){
if(waitingForComplete){
waitingForComplete = false;
// runYourCode...
}
}
};
function checkTabStatusComplete(tabId){
chrome.tabs.get(tabId. function(tab){
if(tab.status == 'complete'){
// runYourCode...
} else {
waitingForComplete = true;
}
}
};
I'm not good at JS and I'm having some -I hope- stupid problem I'm not seeing on my code... if you guys could help me out, I'd really appreciate it.
My extension does some stuff with the current tab's URL. It worked ok using the onUpdate event on my background page, setting the tab's URL on a variable and then I used it on a pop-up.
The thing is that if the user starts, selecting different tabs, without updating the URLs my event won't be triggered again... so I'm now also listening to the onSelectionChanged event.
The thing is that there's no "tab" object within the onSelectionChanged event's parameters, so I cannot ask for the tab.url property.
I tried to use the chrome.tabs.getCurrent() method, but obviously I'm doing something wrong... and I reached the limit of my -very little- knowledge.
Here's the code, if you guys could take a look and point me in the right direction, I'll really appreciate it.
<script>
var tabURL = '';
var defaultURLRecognition = [ "test" ];
// Called when the url of a tab changes.
function checkForValidUrl(tabId, changeInfo, tab) {
//THIS IS WHAT'S NOT WORKING, I SUPPOSE
if (tab==undefined) {
chrome.tabs.getCurrent(function(tabAux) {
test = tabAux;
});
}
//
// If there's no URLRecognition value, I set the default one
if (localStorage["URLRecognition"]==undefined) {
localStorage["URLRecognition"] = defaultURLRecognition;
};
// Look for URLRecognition value within the tab's URL
if (tab.url.indexOf(localStorage["URLRecognition"]) > -1) {
// ... show the page action.
chrome.pageAction.show(tabId);
tabURL = tab.url;
}
};
// Listen for any changes to the URL of any tab.
chrome.tabs.onUpdated.addListener(checkForValidUrl);
// Listen for tab selection changes
chrome.tabs.onSelectionChanged.addListener(checkForValidUrl);
</script>
I would do something like this:
function checkForValidUrl(tab) {
//...
}
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
if(changeInfo.status == "loading") {
checkForValidUrl(tab);
}
});
chrome.tabs.onSelectionChanged.addListener(function(tabId, selectInfo){
chrome.tabs.getSelected(null, function(tab){
checkForValidUrl(tab);
});
});
This is the code which i used for window.onbeforeunload
<head>
<script>
window.onbeforeunload = func;
function func()
{
var request = new XMLHttpRequest();
request.open("POST", "exit.php", true);
request.onreadystatechange = stateChanged;
request.send(null);
}
function stateChanged()
{
if (request.readyState == 4 || request.readyState == "complete")
alert("Succes!");
}
</script>
</head>
this works with IE and Mozilla but does not work with Chrome..... please help......
thanks in advance.....
It seems that the only thing you can do with onbeforeunload in recent version of Chrome is to set the warning message.
window.onbeforeunload = function () {
return "Are you sure";
};
Will work. Other code in the function seems to be ignored by Chrome
UPDATE: As of Chrome V51, the returned string will be ignored and a default message shown instead.
Know I'm late to this, but was scratching my head why my custom beforeunload message wasn't working in Chrome and was reading this. So in case anyone else does the same, Chrome from Version 51 onwards no longer supports custom messages on beforeunload. Apparently it's because the feature has been misused by various scams. Instead you get a predefined Chrome message which may or may not suit your purposes. More details at:
https://developers.google.com/web/updates/2016/04/chrome-51-deprecations?hl=en#remove-custom-messages-in-onbeforeload-dialogs
Personally do not think the message they've chosen is a great one as it mentions leaving the site and one of the most common legitimate uses for onbeforeunload is for dirty flag processing/checking on a web form so it's not a great wording as a lot of the time the user will still be on your site, just have clicked the cancel or reload button by mistake.
You should try this:
window.onbeforeunload = function(e) {
e.returnValue = 'onbeforeunload';
return 'onbeforeunload';
};
This works on latest Chrome. We had the same issue the e.returnValue with value of onbeforeunload solved my problem.
Your code should be like this:
<head>
<script>
window.onbeforeunload = function(e) {
e.returnValue = 'onbeforeunload';
func();
return ''onbeforeunload'';
};
function func()
{
var request = new XMLHttpRequest();
request.open("POST", "exit.php", true);
request.onreadystatechange = stateChanged;
request.send(null);
}
function stateChanged()
{
if (request.readyState == 4 || request.readyState == "complete")
alert("Succes!");
}
</script>
</head>
Confirmed this behavior on chrome 21.0.1180.79
this seems to work with the same restritions as XSS, if you are refreshing the page or open a page on same domain+port the the script is executed, otherwise it will only be executed if you are returning a string (or similar) and a dialog will be shown asking the user if he wants to leans or stay in the page.
this is an incredible stupid thing to do, because onunload/onbeforeunload are not only used to ask/prevent page changes.
In my case i was using it too save some changes done during page edition and i dont want to prevent the user from changing the page (at least chrome should respect a returning true or change the page without the asking if the return is not a string), script running time restrictions would be enought.
This is specially annoying in chrome because onblur event is not sent to editing elements when unloading a page, chrome simply igores the curent page and jumps to another. So the only change of saving the changes was the unload process and it now can't be done without the STUPID question if the user wants to change it... of course he wants and I didnt want to prevent that...
hope chrome resolves this in a more elegant way soon.
Try this, it worked for me:
window.onbeforeunload = function(event) {
event.returnValue = "Write something clever here..";
};
Try this. I've tried it and it works. Interesting but the Succes message doesn`t need confirmation like the other message.
window.onbeforeunload = function()
{
if ( window.XMLHttpRequest )
{
console.log("before"); //alert("before");
var request = new XMLHttpRequest();
request.open("POST", "exit.php", true);
request.onreadystatechange = function () {
if ( request.readyState == 4 && request.status == 200 )
{
console.log("Succes!"); //alert("Succes!");
}
};
request.send();
}
}
None of the above worked for me. I was sending a message from the content script -> background script in the before unload event function. What did work was when I set persistent to true (in fact you can just remove the line altogether) in the manifest:
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
The logic is explained at this SO question here.
Current versions of Chrome require setting the event's returnValue property. Simply returning a string from the event handler won't trigger the alert.
addEventListener('beforeunload', function(event) {
event.returnValue = 'You have unsaved changes.';
});
I'm running Chrome on MacOS High Sierra and have an Angular 6 project whithin I handle the window.beforeunload an window.onbeforeunload events. You can do that, it's worked for me :
handleUnload(event) {
// Chrome
event.returnValue = true;
}
It show me an error when I try to put a string in event.returnValue, it want a boolean.
Don't know if it allows custom messages to display on the browser.
<script type="text/javascript">
window.addEventListener("beforeunload", function(e) {
e.preventDefault(); // firefox
e.returnValue = ''; // Chrome
});
</script>