Define the Dispose method for the page: [Before]unload event Apocalypse

Source: Internet
Author: User

Preface

Recently implemented colleagues reported that the user approval process directly close the browser, the operation more than 10 after the system reported that the number of user sessions exceeded the limit, consulting 4 A colleague after the login need to explicitly call the logout API to clean up the 4 a end, otherwise it will inevitably exceed the session limit.

Even adding a logout button on the page does not guarantee that the user will not directly switch off the browser, not to mention the user has been accustomed to do so, to increase the function of good, change habits but difficult ah. This reminds me of the window.onbeforeunload and Window.onunload events that have been used in n years.

This document records the passing of these two fellows so that they can be spent less pits in the future.

Write a Dispose method for a webpage

In C # We will put the finishing touches such as releasing unmanaged resources into the Dispose method, and then call the method automatically through a using statement block. Is there a lot of finishing touches to the Web page? Do we have a similar mechanism to make the program more robust? -that depends on the beforeunload and unload events. But relative to C # automatically calls the Dispose method through a using statement block, the trigger points for beforeunload and unload are much more complex.

Let's see when the two events will be triggered.

    1. Enter the address in the browser address bar and click Jump;

    2. Click on the page link to achieve the jump;

    3. Close or refresh the current page;

    4. Manipulate the Location object of the current page and modify the current page address;

    5. Call Window.navigate implementation Jump;

    6. Call the window.open or Document.open method to load another page or reopen the input stream on the current page.

omg! so many operations will trigger the two brothers, how to deal with the good ah? There is no way, for functional requirements to make a choice. The requirement for me is to call the logout API in the Dispose method of the page, and to pass through and implement the communication of the colleague--just refresh the page and trigger the logout.

;(function (exports, $, url) {

Exports.dispose = $.proxy ($.get, $, URL)

} (window, $, "http://pseudo.com/logout"))

So the rest of the question is whether to call the Dispose method in the Beforeunload or Unload event handler function? Here are two points to explore:

    1. What are the functional positioning of beforeunload and unload?

    2. Compatibility of beforeunload and unload.

What are the functional positioning of beforeunload and unload?

Beforeunload, as the name implies, is triggered before unload, and you can attempt to terminate execution unload by popping up two confirmation dialogs.

Unload is triggered when the content of the page is being unloaded, and there are some important cleanup tasks here, and the page is in one of the following special temporary states:

    1. All resources on the page (IMG, IFRAME, etc.) have not been released;

    2. The visible area of the page is blank;

    3. UI human-Computer Interaction failure (window.open,alert,confirm all failed);

    4. There is no action to prevent the execution of the unload process. (The cancelable attribute value for the Unload event is no)

So in turn look at the Beforeunload event, when the page state is roughly the same as usual:

    1. Page All resources are not released, and the page visual area effect has not changed;

    2. UI human-Computer Interaction failure (window.open,alert,confirm all failed);

    3. The last opportunity can prevent the execution of the unload process. (The Cancelable property value of the Beforeunload event is yes)

Compatibility of Beforeunload and unload

For mobile browsers (Safari, Opera Mobile, etc.), the Beforeunload event is not supported, perhaps because the mobile side does not recommend interfering with the user's operating process.

Data Loss Prevention mechanism--two confirmations

Data loss is often treated as an exception when the user is editing a state, leaving the page due to an erroneous operation. There are about 3 ways of handling it:

    1. If you lose it, you lose it, and then you are the one who suffers it.

    2. Simple rough-detection in the editing state, monitoring Beforeunload event two times to determine, that is, the responsibility to the user;

    3. Save automatically, even do work in Progress (refer to John Papa's share John Papa-progressive savingr-ng-conf)

Here we choose Mode 2, which pops up two times to determine the dialog box. Think of a dialog box naturally think of window.confirm, and then naturally enter the following code

Window.addeventlistener (' Beforeunload ', function (e) {

var msg = "Do U-Want to leave?\nchanges u made is lost."

if (!window.confirm (msg)) {

E.preventdefault ()

}

})

Then refresh the page to find nothing has happened, and then directly blindfolded ...

Pit 1: Ignoring Window.alert/confirm/prompt/showmodaldialog

Beforeunload and unload are very special events that require that the event handler inside cannot block the current thread.

While Window.alert/confirm/prompt/showmodaldialog is exactly blocking the current thread, the H5 specification explicitly ignores the calls to these methods in Beforeunload and unload.

Since, the HTML5 specification states, calls to window.showModalDialog (), Window.alert (), Window.confirm () and Window.prompt () methods May is ignored during this event. (onbeforeunload#notes) [Https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload#Notes]

"Blocked alert/prompt/confirm () during Beforeunload/unload" will be reported under Chrome/chromium. JS exception, and Firefox under the even exception is not bothered to report.

Since not to use window.confirm, then how to pop up two times OK dialog box? In fact, the Beforeunload incident has been prepared for us. Just change it.

Window.onbeforeunload = function () {

var msg = "Do U-Want to leave?\nchanges u made is lost."

Return msg

}

When listening to beforeunload events by DOM0 event model, only the return value is not undefined or null, which will pop up two OK dialog boxes. In the case of IE and chrome/chromium, the return value is used as the prompt for the dialog box, and Firefox4 begins to ignore the hint information that the return value is only explicitly built in.

It's not a good way to go, still using the DOM0 Event Model: ( So let's see how the DOM2 Event model is a play

Microsoft dom2-ish Event Model

Window.attachevent (' onbeforeunload ', function () {

var msg = "Do U-Want to leave?\nchanges u made is lost."

var evt = window.event

Evt.returnvalue = Msg

})

For the DOM2 Event Model, which is unique to the jumbo, we implement the popup function by setting the Window.event.returnValue to be non-null or undefined (note: The function return value is ineffective)

What about the standard DOM2 Event model? I remember window.event.returnValue is for IE only, but the return value of the event handler function is also effective, that can only think of Event.preventdefault (), but Event.preventdefault () Without overloading with incoming parameters, does it mean that custom cue information is not supported by the standard DOM2 Event model?

Window.addeventlisteners (' Beforeunload ', function (e) {

E.preventdefault ()

})

Successful pop-up dialog on Firefox, but nothing happened on chrome/chromium ...

Pit 2:htmlelement.addeventlistener Event Bindings

Event.preventdefault () This play on Firefox support, Chrome this time to stand in the queue of IE. That's the way it's played together.

;(function (Exports) {

Exports.gendispose = Gendispose

/**

* @param {function| String} [Fnbody]-executed within the Dispose method when it's data type is Function

* As return value of the Dispose method when it's data type is String

* @param {String} [returnmsg]-as return value of Dispose method

* @returns {Function}-Dispose method

*/

function Gendispose (fnbody, returnmsg) {

var args = Getargs (arguments)

return function (e) {

Args.fnbody && Args.fnbody ()

if (E = e | | window.event) {

Args.returnmsg && e.preventdefault && e.preventdefault ()

E.returnvalue = args.returnmsg

}

Return args.returnmsg

}

}

function Getargs (args) {

var ret = {fnbody:void 0, returnmsg:args[1]},

typeofArg0 = typeof Args[0]

if ("string" = = = typeofArg0) {

Ret.returnmsg = Args[0]

}

else if ("function" = = = = typeofArg0) {

Ret.fnbody = Args[0]

}

RETRN ret

}

} (Window))

Uses

var dispose = gendispose ("Do u want to leave?\nchanges u made could be lost.")

Window.onbeforeunload = Dispose

Window.attachevent (' onbeforeunload ', Dispose)

Window.addeventlistener (' Beforeunload ', Dispose)

Pit 3: Respect the user's choice

Is there a way to prevent users from closing or refreshing the page? No way, two times to determine the user's operation has been the maximum interference.

Problem not resolved--cross-domain redirection

;(function (Exports) {

Exports. Logout = Logout

function Logout (URL) {

if (this instanceof Logout); else return new Logout (URL)

This.url = URL

}

Logout.prototype.exec = function () {

var xhr = new XMLHttpRequest ()

Xhr.open ("GET", This.url, False)

Xhr.send ()

}

} (Window))

var url = "Http://pseudo.com/logout",

Logout = new Logout (URL)

var dispose = $.proxy (logout.exec, logout)

var prefix = ' on '

(Window.attachevent | | (prefix= ", Window.addeventlistener)) (prefix + ' unload ', Dispose)

When I thought I could do my homework, I found out that the Logout URL response status code is 302, and the response header location points to the resource of the other domain, and there is no cors response header information such as Access-control-allow-origin. The XHR object does not support Cross-domain redirection, so logout fails.

Previously only know XHR unable to perform the read operation of the Cross-domain resource (support write operation), but only thought is not support respose body read operation, did not think even respose header read operation also does not support. What about that? Since the read operation is not possible, the use of nested Cross-domain resources head row. Then there is the following pits process:

    1. The first thought is nested IFRAME to achieve, when the instantiation of the IFRAME cost is too high, causing the IFRAME has not been enough time to send the request has completed the Unload process;

    2. Then think of the request through script, because the content of respose body is not a valid script, it will report the script parsing exception, if set type= "Text/tpl" and other content will not initiate the network request, the other IFrame, HTML elements such as script must be added to the DOM tree to initiate a network request;

    3. Finally think of htmlimageelement, as long as the set SRC attribute is immediately launched network request, and return illegal content causes parsing failure or silently endure, especially suitable for this task:)

Then get the following version

;(function (Exports) {

Exports. Logout = Logout

function Logout (URL) {

if (this instanceof Logout); else return new Logout (URL)

This.url = URL

}

Logout.prototype.exec = function () {

var img = Image? New Image (): Document.createelement ("IMG")

IMG.SRC = This.url

}

} (Window))

[Before]unload cause performance degradation?

Now we all know how to use [Before]unload] to do the cleanup of resources.

But keep in mind that because the [Before]unload event degrades page performance, you only listen to these two events because of important aftercare or irreversible cleanup work.

Previously, when we jumped from page A to page B, all the resources of page A were freed (destroying the DOM object, retrieving the JS object, releasing the decoded image resource, etc.); Later, the major browser vendors used Bfcache/page Cache/fast history The navigation mechanism, which saves the state of page A to the cache, recovers the page from the cache as soon as it jumps through the browser's back/forward button instead of instantiating it again. The following conditions will not be cached:

    1. Monitor unload or Beforeunload events;

    2. Response Head Cache-control:no-store;

    3. For response headers that use the HTTPS protocol, one or more of the following is true:

      1. 3.1. Cache-control:no-cache

      2. 3.2. Pragma:no-cache

      3. 3.3. There are expires overdue

    4. When a jump occurs, the page has resources that are not loaded

    5. This is the case with an IFRAME

    6. The page is rendered in the IFRAME when the user modifies iframe.src to load other documents into the IFRAME

    7. So if you do irreversible cleanup, we should subscribe to the Pagehide event instead of the Unload event for modern browsers to take advantage of the page cache mechanism.

Event Occurrence Order: Load->pageshow->pagehide->unload

The event object for Pageshow and Pagehide has a persisted property that is true when recovering from the cache and false for re-instantiation.

The simple test found that Chrome does not have this feature enabled by default, and Firefox is enabled by default. Experiment Code:

Index.html

Window.addeventlistener (' Load ', function () {

Console.log ("Index.load")

Window.test = True

})

Window.addeventlistener (' Pageshow ', function (e) {

Console.log ("index.pageshow.persisted:" + e.persisted)

Console.log ("index.test:" + window.test)

})

<a href= "./next.html" >next.html</a>

Next.html

Window.addeventlistener (' Load ', function () {

Console.log ("Next.load")

})

Window.addeventlistener (' Pageshow ', function (e) {

Console.log ("next.pageshow.persisted:" + e.persisted)

})

Operating Environment: FireFox

Operation steps: 1. First visit index.html,2. Then click on the link to jump to next.html,3. Then click the browser's fallback button to jump to index.html,4. Finally click the Forward button of the browser to jump to next.html.

Output Result:

1

Index.load

Index.pageshow.persisted:false

Index.test:true

2

Next.load

Next.pageshow.persisted:false

3

Index.pageshow.persisted:true

Index.test:true

4

Next.pageshow.persisted:true

See the page is restored from Bfcache, so the JS object is not recycled, so the window.test value is still valid. In addition, load is triggered only after the page is initialized, so it is not triggered when the page is restored from Bfcache.

If you subscribe to the unload or Beforeunload event on index.html, the page will not be saved to Bfcache.

In addition, through Jquery.ready to listen to the page initialization event, do not consider the impact of Bfcache, because it helps us handle:)

Define the Dispose method for the page: [Before]unload event Apocalypse

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.