mailto link (in chrome) is triggering window.onbeforeunload - can i prevent this?
Asked Answered
S

9

21

Possibly related to How to open mailto link in Chrome with Window.open without creating a new tab?

Hi all. I have a form page where i've put a window.onbeforeunload confirm, to stop people navigating away and losing their changes by accident:

window.onbeforeunload = function(){
  if(changed)
    return "You have unsaved changes.  Do you really want to leave this page without saving?";
};  

where changed is a variable i set to true whenever the user makes any changes. That's all fine. However, i've also added some mailto links to the page, like so:

<a class="button button-alt" href="mailto:[email protected]">Report a problem</a>

Even though the mailto isn't navigating away from the page (it's opening the users default mail app), it's still triggering the onbeforeunload event, prompting the confirm box, which is annoying. I can get round it setting target="_blank" on the link, but then the user is left sitting in an empty tab.

Can i set the mailto link to not trigger the onbeforeunload event? I thought of a horribly hacky way of doing it by having another temporary javascript variable which causes the onbeforeunload confirm to not trigger, but it seems kind of dirty. I'll do it anyway while i wait for a response but does anyone have a nicer solution?

thanks, max

Skaggs answered 16/3, 2012 at 15:54 Comment(0)
T
12

Building off of epascarello's solution, the following JQuery code should do the trick:

    var ignore_onbeforeunload = false;
    $('a[href^=mailto]').on('click',function(){
        ignore_onbeforeunload = true;
    });

    window.onbeforeunload = function() {
        if (!ignore_onbeforeunload){
            return "Halt! you are not supposed to leave!";
        }
        ignore_onbeforeunload = false;
    };
Torpedo answered 19/2, 2015 at 17:39 Comment(4)
Really? you answer a post 3 years later? lol Only difference is adding the onclick with an unobtrusive event...lolGreatnephew
Relies on jQuery for no reason, relies on global variable. John Kurlak's answer below is much nicer. It uses a hidden iframe, totally removing the need for JavaScript, let alone jQuery. Can then self-contain the fix in the template file.Califate
What about browsers who don't call onbeforeunload on click of such link? Seems like closing will be skipped in this case.Fortunio
The question was how to avoid degrading the experience when it is triggered. When it's not supported is exactly the desired experience.Torpedo
P
29

A really simple fix to this is to do something like this:

<a href="mailto:[email protected]" target="hidden-iframe">Email me</a>
<iframe name="hidden-iframe" style="visibility:hidden;position:absolute;"></iframe>

(And of course, move the styles to their own stylesheet instead of inlining them.)

Performance answered 12/3, 2015 at 17:49 Comment(5)
I'm late to the party but just came across this same issue. This is by far the better solution. The others rely on lots of ugly JavaScript/jQuery. This way you can self-contain the fix within the html template, using zero JavaScript.Califate
@Oranges13: Sure it does. What version are you using?Performance
This was working for me in Chrome until I added "frame-src" CSP which broke itRedshank
Thanks @JohnKurlak This is an awesome solution. This is just a late response from someone that just benefited from this old post. Thank you.Brazilein
this is the fix for Blazor applications experiencing a disconnect issue with 'tel:' links for example tooLoincloth
T
12

Building off of epascarello's solution, the following JQuery code should do the trick:

    var ignore_onbeforeunload = false;
    $('a[href^=mailto]').on('click',function(){
        ignore_onbeforeunload = true;
    });

    window.onbeforeunload = function() {
        if (!ignore_onbeforeunload){
            return "Halt! you are not supposed to leave!";
        }
        ignore_onbeforeunload = false;
    };
Torpedo answered 19/2, 2015 at 17:39 Comment(4)
Really? you answer a post 3 years later? lol Only difference is adding the onclick with an unobtrusive event...lolGreatnephew
Relies on jQuery for no reason, relies on global variable. John Kurlak's answer below is much nicer. It uses a hidden iframe, totally removing the need for JavaScript, let alone jQuery. Can then self-contain the fix in the template file.Califate
What about browsers who don't call onbeforeunload on click of such link? Seems like closing will be skipped in this case.Fortunio
The question was how to avoid degrading the experience when it is triggered. When it's not supported is exactly the desired experience.Torpedo
G
5

Add a flag and see if it is flipped, set the flag on the link click.

var ignore = false
window.onbeforeunload = function() {
    if (changed && !ignore) {
        return "You have unsaved changes.  Do you really want to leave this page without saving?";
    } else {
        ignore = false;
    }
}

And the link

<a class="button button-alt" href="mailto:[email protected]" onclick="ignore=true">Report a problem</a>

It would be better to add the onclick with JavaScript code.

Greatnephew answered 16/3, 2012 at 16:7 Comment(2)
@MaxWilliams, what about browsers who don't call onbeforeunload on click of such link? Seems like closing will be skipped in this case.Fortunio
@Greatnephew what is the changed variable? (line 3)Serrulation
M
4

I managed to solve this problem inside the onbeforeunload event by checking event.target. This way you dont have to use outer variables or any extra bindings.

window.onbeforeunload = function (event) {

    var activeElement = $(event.target.activeElement);
    // console.log('onbeforeunload', activeElement);

    var isMailto = activeElement && activeElement.is('[href^=mailto:]');
    var isTel = activeElement && activeElement.is('[href^=tel:]');

    if(!isMailto && !isTel) {
        // logic when link is "normal"
    }
};
Melanoid answered 14/11, 2019 at 15:27 Comment(0)
S
2

Another option, also a bit hacky but a bit more generic way to make the beforeunload ignore certain types of links:

In the beforeunload, inspect the link that caused the beforeunload, and if it is not a http or https link, don't bother the user.

Unfortunately it is not easy to inspect the link that caused the beforeunload, so this is where it gets a little hacky with an onclick handler and global variable.

var lastClicked = null;
window.onclick = function(e){
    e = e || window.event;
    lastClicked = e.target
}
window.onbeforeunload = function(e){
    if (changed && lastClicked && lastClicked.href 
        && lastClicked.href.substring(0,4) == 'http')
        return "You have unsaved changes";
}

Edit: Almost forgot, this answer comes from here: https://mcmap.net/q/658955/-how-to-access-javascript-href-event-that-triggered-beforeunload

Strohbehn answered 22/8, 2012 at 5:36 Comment(0)
G
2

Some other solution with pure JS and using the focused element:

window.addEventListener("beforeunload", () => {
  const isMailTo = document.activeElement.protocol === "mailto:";

  if (!isMailTo) {
    // do your stuff
  }
});
Gmt answered 23/3, 2020 at 12:16 Comment(0)
S
2

Based on John Kurlak answer, you can simply use :

<a href="mailto:[email protected]" target="_blank">Email me</a>
Shillong answered 8/12, 2020 at 12:30 Comment(0)
C
0

Based on Theiaz answer, you can add a Boolean global var as protectUnload followed by a regexp to allow all external protocols except links ( http(s) ):

window.onbeforeunload = function (event) {
    return (protectUnload && (typeof document.activeElement.protocol !== "undefined" && /^http(s)?:$/.test(document.activeElement.protocol))) || null; // return null == no confirm
};

It does not prevent for being kick out from the page using location.href but for in that case you can add 1 more boolean to the formula to always be protected for unloading the page (setting it to true before location.href's new value).

Contumacy answered 26/3, 2021 at 11:43 Comment(1)
Adding boolean global variables is usually not the strongest answer. Containing your JS to an IIFE or similarly scoped properties is the right way. The other answers in this nearly 9 year old post handle the question well.Dolorisdolorita
M
0

Set the target of the anchor to a hidden iframe burried in the page. <a href="mailto:[email protected]" target="hiddenIframe".

If the mailto is called via a javascript function (from a button or anchor or whatever) you could even create the iframe on the fly, set its src to the mailto, then remove it in a timeout.

Either way, the current page is not affected, and no need for pop-up tabs or windows.

Mountbatten answered 23/12, 2021 at 16:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.