国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JS魔法堂:定義頁面的Dispose方法——[before]unload事件啟示錄

Chiclaim / 3351人閱讀

摘要:坑無視和是十分特殊的事件,要求事件處理函數(shù)內(nèi)部不能阻塞當(dāng)前線程,而卻恰恰就會阻塞當(dāng)前線程,因此規(guī)范中以明確在和中直接無視這幾個方法的調(diào)用。

前言

?最近實(shí)施的同事報(bào)障,說用戶審批流程后直接關(guān)閉瀏覽器,操作十余次后系統(tǒng)就報(bào)用戶會話數(shù)超過上限,咨詢4A同事后得知登陸后需要顯式調(diào)用登出API才能清理4A端,否則必然會超出會話上限。
?即使在頁面上增添一個登出按鈕也無法保證用戶不會直接關(guān)掉瀏覽器,更何況用戶已經(jīng)習(xí)慣這樣做,增加功能好弄,改變習(xí)慣卻難啊。這時想起N年用過的window.onbeforeunloadwindow.onunload事件。
?本文記錄重拾這兩個家伙的經(jīng)過,以便日后用時少坑。

為網(wǎng)頁寫個Dispose方法

?C#中我們會將釋放非托管資源等收尾工作放到Dispose方法中, 然后通過using語句塊自動調(diào)用該方法。對于網(wǎng)頁何嘗不是有大量收尾工作需要處理呢?那我們是否也有類似的機(jī)制,讓程序變得更健壯呢?——那就靠beforeunloadunload事件了。但相對C#通過using語句塊自動調(diào)用Dispose方法,beforeunloadunload的觸發(fā)點(diǎn)則復(fù)雜不少。
?我們看看什么時候會觸發(fā)這兩個事件呢?

在瀏覽器地址欄輸入地址,然后點(diǎn)擊跳轉(zhuǎn);

點(diǎn)擊頁面的鏈接實(shí)現(xiàn)跳轉(zhuǎn);

關(guān)閉或刷新當(dāng)前頁面;

操作當(dāng)前頁面的Location對象,修改當(dāng)前頁面地址;

調(diào)用window.navigate實(shí)現(xiàn)跳轉(zhuǎn);

調(diào)用window.opendocument.open方法在當(dāng)前頁面加載其他頁面或重新打開輸入流。
?OMG!這么多操作會觸發(fā)這兩兄弟,怎么處理才好啊?沒啥辦法,針對功能需求做取舍咯。對于我的需求就是在頁面的Dispose方法中調(diào)用登出API,經(jīng)過和實(shí)施同事的溝通——只要刷新頁面就觸發(fā)登出。

;(function(exports, $, url){
  exports.dispose = $.proxy($.get, $, url)
}(window, $, "http://pseudo.com/logout"))

那現(xiàn)在剩下的問題就在于到底是在beforeunload還是unload事件處理函數(shù)中調(diào)用dispose方法呢?這里涉及兩點(diǎn)需要探討:

beforeunloadunload的功能定位是什么?

beforeunloadunload的兼容性.

beforeunloadunload的功能定位是什么?

?beforeunload顧名思義就是在unload前觸發(fā),可通過彈出二次確認(rèn)對話框來試圖終斷執(zhí)行unload.
?unload就是正在進(jìn)行頁面內(nèi)容卸載時觸發(fā)的,一般在這里進(jìn)行一些重要的清理善后工作,而這時頁面處于以下一個特殊的臨時狀態(tài):

頁面所有資源(img, iframe等)均未被釋放;

頁面可視區(qū)域一片空白;

UI人機(jī)交互失效(window.open,alert,confirm全部失效);

沒有任何操作可以阻止unload過程的執(zhí)行。(unload事件的Cancelable屬性值為No)

?那么反過來看看beforeunload事件,這時頁面狀態(tài)大致與平常一致:

頁面所有資源均未釋放,且頁面可視區(qū)域效果沒有變化;

UI人機(jī)交互失效(window.open,alert,confirm全部失效);

最后時機(jī)可以阻止unload過程的執(zhí)行.(beforeunload事件的Cancelable屬性值為Yes)

beforeunloadunload的兼容性

?對于移動端瀏覽器而言(Safari, Opera Mobile等)而言不支持beforeunload事件,也許是因?yàn)橐苿佣瞬唤ㄗh干擾用戶操作流程吧。

防數(shù)據(jù)丟失機(jī)制——二次確認(rèn)

?當(dāng)用戶正在編輯狀態(tài)時,若因誤操作離開頁面而導(dǎo)致數(shù)據(jù)丟失常作為例外處理。處理方式大概有3種:

丟了就丟唄,然后就是誰用誰受罪了;

簡單粗暴——偵測處于編輯狀態(tài)時,監(jiān)聽beforeunload事件作二次確定,也就是將責(zé)任拋給用戶;

自動保存,甚至做到Work in Progress(參考john papa的分享John Papa-Progressive Savingr-NG-Conf)
?這里我們選擇方式2,彈出二次確定對話框。想到對話框自然會想到window.confirm,然后很自然地輸入以下代碼

window.addEventListener("beforeunload", function(e){
  var msg = "Do u want to leave?
Changes u made may be lost."
  if (!window.confirm(msg)){
    e.preventDefault()
  }
})

然后刷新頁面發(fā)現(xiàn)啥都沒發(fā)生,接著直接蒙了。。。。。。

坑1: 無視window.alert/confirm/prompt/showModalDialog

?beforeunloadunload是十分特殊的事件,要求事件處理函數(shù)內(nèi)部不能阻塞當(dāng)前線程,而window.alert/confirm/prompt/showModalDialog卻恰恰就會阻塞當(dāng)前線程,因此H5規(guī)范中以明確在beforeunloadunload中直接無視這幾個方法的調(diào)用。

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

在chrome/chromium下會報(bào)"Blocked alert/prompt/confirm() during beforeunload/unload."的JS異常,而firefox下則連異常都懶得報(bào)。
?既然不給用window.confirm,那么如何彈出二次確定對話框呢?其實(shí)beforeunload事件已經(jīng)為我們準(zhǔn)備好了。只要改成

window.onbeforeunload = function(){
  var msg = "Do u want to leave?
Changes u made may be lost."
  return msg
}

?通過DOM0 Event Model的方式監(jiān)聽beforeunload事件時,只需返回值不為undefined或null,即會彈出二次確定對話框。而IE和Chrome/Chromium則以返回值作為對話框的提示信息,F(xiàn)irefox4開始會忽略返回值僅顯式內(nèi)置的提示信息.
?太不上道了吧,還在用DOM0 Event Model:( 那我們來看看DOM2 Event Model是怎么一個玩法

// Microsoft DOM2-ish Event Model
window.attachEvent("onbeforeunload", function(){
  var msg = "Do u want to leave?
Changes u made may be lost."
  var evt = window.event
  evt.returnValue = msg
})

對于巨硬獨(dú)有的DOM2 Event Model,我們通過設(shè)置window.event.returnValue為非null或undefined來實(shí)現(xiàn)彈出窗的功能(注意:函數(shù)返回值是無效果的)
那么標(biāo)準(zhǔn)的DOM2 Event Model呢?我記得window.event.returnValue是 for ie only的,但事件處理函數(shù)的返回值又木有效果,那只能想到event.preventDefault()了,但event.preventDefault()沒有帶入?yún)⒌闹剌d,那么是否意味通過標(biāo)準(zhǔn)DOM2 Event Model的方式就不支持自定義提示信息呢?

window.addEventListeners("beforeunload", function(e){
  e.preventDefault()
})

在FireFox上成功彈出對話框,但Chrome/Chromium上卻啥都沒發(fā)生。。。。。。

坑2: HTMLElement.addEventListener事件綁定

?event.preventDefault()這一玩法就FireFox支持,Chrome這次站到IE的隊(duì)列上了。綜合起來的玩法是這樣的

;(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 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?
Changes u made may be lost.")
window.onbeforeunload = dispose
window.attachEvent("onbeforeunload", dispose)
window.addEventListener("beforeunload", dispose)
坑3: 尊重用戶的選擇

?有辦法阻止用戶關(guān)閉或刷新頁面嗎?沒辦法,二次確定已經(jīng)是對用戶操作的最大限度的干擾了。

問題未解決——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)

?當(dāng)我以為這樣就能交功課時,卻發(fā)現(xiàn)登出url響應(yīng)狀態(tài)編碼為302,而響應(yīng)頭Location指向另一個域的資源,并且不存在Access-Control-Allow-Origin等CORS響應(yīng)頭信息,而XHR對象不支持Cross-domain Redirection,因此登出失效。
?以前只知道XHR無法執(zhí)行Cross-domain資源的讀操作(支持寫操作),但只以為僅僅是不支持respose body的讀操作而已,沒想到連respose header的讀操作也不支持。那怎么辦呢?既然讀操作不行那采用嵌套Cross-domain資源總行吧。然后有了以下的填坑過程:

第一想到的就是嵌套iframe來實(shí)現(xiàn),當(dāng)iframe的實(shí)例化成本太高了,導(dǎo)致iframe還沒來得及發(fā)送請求就已經(jīng)完成unload過程了;

于是想到了通過script發(fā)起請求, 因?yàn)閞espose body的內(nèi)容不是有效腳本,因此會報(bào)腳本解析異常,若設(shè)置type="text/tpl"等內(nèi)容時還不會發(fā)起網(wǎng)絡(luò)請求;另外iframe、script等html元素均要加入DOM樹后才能發(fā)起網(wǎng)絡(luò)請求;

最后想到HTMLImageElement,只要設(shè)置src屬性則馬上發(fā)起網(wǎng)絡(luò)請求,而且返回非法內(nèi)容導(dǎo)致解析失敗時還是默默忍受,特別適合這次的任務(wù):)

?于是得到下面的版本

;(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導(dǎo)致性能下降?

?現(xiàn)在我們都明白如何利用[before]unload來做資源釋放等善后工作了。
?但請記住一點(diǎn):由于[before]unload事件會降低頁面性能,因此僅由于需要做重要的善后或不可逆的清理工作時才監(jiān)聽這兩個事件。
?以前,當(dāng)我們從頁面A跳轉(zhuǎn)到頁面B時,頁面A的所有資源將被釋放(銷毀DOM對象,回收J(rèn)S對象, 釋放解碼后的Image資源等);后來各大瀏覽器廠商分別采用bfcache/page cache/fast history navigation機(jī)制,將頁面A的狀態(tài)保存到緩存中,當(dāng)通過瀏覽器的后退/前進(jìn)按鈕跳轉(zhuǎn)時馬上從緩存中恢復(fù)頁面,而不是重新實(shí)例化。以下情況將不被緩存起來:

監(jiān)聽unloadbeforeunload事件;

響應(yīng)頭Cache-Control: no-store;

對于采用HTTPS協(xié)議的響應(yīng)頭,滿足以下一個或以上:
3.1. Cache-Control: no-cache

3.2. Pragma: no-cache
3.3. 存在Expires超期的

發(fā)生跳轉(zhuǎn)時,頁面存在未加載完的資源

旗下iframe存在上述情況的

頁面在iframe中渲染,當(dāng)用戶修改iframe.src加載其他文檔到該iframe時

?因此若執(zhí)行不可逆的清理工作時,對于現(xiàn)代瀏覽器而言我們應(yīng)該訂閱pagehide事件,而不是unload事件,以便利用Page Cache機(jī)制。
事件發(fā)生順序:load->pageshow->pagehide->unload
pageshowpagehide的事件對象存在一個persisted屬性,為true時表示從cache中恢復(fù),false表示重新實(shí)例化。
?經(jīng)簡單測試發(fā)現(xiàn)chrome默認(rèn)沒有啟用該特性,而Firefox則默認(rèn)啟用。實(shí)驗(yàn)代碼:

// 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)
})

next.html
// next.html
window.addEventListener("load", function(){
  console.log("next.load")
})
window.addEventListener("pageshow", function(e){
  console.log("next.pageshow.persisted:" + e.persisted)
})

運(yùn)行環(huán)境:FireFox
操作步驟:1.首先訪問index.html,2.然后點(diǎn)擊鏈接跳轉(zhuǎn)到next.html,3.然后點(diǎn)擊瀏覽器的回退按鈕跳轉(zhuǎn)到index.html,4.最后點(diǎn)擊瀏覽器的前進(jìn)按鈕跳轉(zhuǎn)到next.html。
輸出結(jié)果:

// 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

?看到頁面是從bfcache恢復(fù)而來的,所以JS對象均未回收,因此window.test值依然有效。另外load僅在頁面初始化后才會觸發(fā),因此從bfcache中恢復(fù)頁面時并不會觸發(fā)。
?假如在index.html上訂閱了unloadbeforeunload事件,那么該頁面將不會保存到bfcache。
?另外通過jQuery.ready來監(jiān)聽頁面初始化事件時,不用考慮bfcache的影響,因?yàn)樗鼛臀覀兲幚砗昧?)

總結(jié)

若有紕漏望請指正,謝謝!
尊重原創(chuàng),轉(zhuǎn)載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/5647649.html 肥子John^_^

感謝

window-onbeforeunload-not-working
beforeunload
unload
prompt-to-unload-a-document
webkit page cache i - the basics
webkit page cache ii - the unload event
pagehide
pageshow
Redirects Do’s and Don’ts
cross-browser-onload-event-and-the-back-button
Using_Firefox_1.5_c aching#New_browser_events

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/79905.html

相關(guān)文章

  • CSS魔法:改變單選框顏色就這么吹毛求疵!

    摘要:前言是否曾經(jīng)被業(yè)務(wù)提出能改改這個單選框的顏色吧讓它和主題顏色搭配一下吧,然后苦于原生不支持換顏色,最后被迫自己手?jǐn)]一個湊合使用。設(shè)置為的樣式行為特征單選框的行為特征,明顯就是選中與否,及選中狀態(tài)的改變事件,因此我們必須保持對外提供事件。 前言 ?是否曾經(jīng)被業(yè)務(wù)提出能改改這個單選框的顏色吧!讓它和主題顏色搭配一下吧!,然后苦于原生不支持換顏色,最后被迫自己手?jǐn)]一個湊合使用。若拋開inpu...

    freecode 評論0 收藏0
  • CSS魔法:一起玩透偽元素和Content屬性

    摘要:前言繼上篇魔法堂稍稍深入偽類選擇器記錄完偽類后,我自然而然要向偽元素伸出魔掌的啦。和的注意事項(xiàng)默認(rèn)必須設(shè)置屬性,否則一切都是無用功默認(rèn),就是和的內(nèi)容無法被用戶選中的偽元素和偽類結(jié)合使用形如。 前言 ?繼上篇《CSS魔法堂:稍稍深入偽類選擇器》記錄完偽類后,我自然而然要向偽元素伸出魔掌的啦^_^。本文講講述偽元素以及功能強(qiáng)大的Contet屬性,讓我們可以通過偽元素更好地實(shí)現(xiàn)更多的可能! ...

    DevTalking 評論0 收藏0
  • CSS魔法:稍稍深入偽類選擇器

    摘要:前言過去零零星星地了解和使用和等偽類偽元素選擇器,最近看書時發(fā)現(xiàn)這方面有所欠缺,于是決定稍微深入學(xué)習(xí)一下,以下為偽類部分的整理。偽類偽類選擇器實(shí)質(zhì)上是讓設(shè)計(jì)師可以根據(jù)元素特定的狀態(tài),設(shè)置不同的視覺效果。也就是符合以下選擇器的元素均支持狀態(tài)。 前言 ?過去零零星星地了解和使用:link、::after和content等偽類、偽元素選擇器,最近看書時發(fā)現(xiàn)這方面有所欠缺,于是決定稍微深入學(xué)習(xí)...

    tanglijun 評論0 收藏0
  • CSS魔法:display:none與visibility:hidden恩怨情仇

    摘要:不耽誤表單提交數(shù)據(jù)雖然我們無法看到的元素,但當(dāng)表單提交時依然會將隱藏的元素的值提交上去。讓元素在見面上不可視,但保留元素原來占有的位置。不過由于各瀏覽器實(shí)現(xiàn)效果均有出入,因此一般不會使用這個值。繼承父元素的值。 前言 ?還記得面試時被問起請說說display:none和visibility:hidden的區(qū)別嗎?是不是回答完display:none不占用原來的位置,而visibilit...

    selfimpr 評論0 收藏0
  • JS魔法:那些困擾你DOM集合類型

    摘要:五的子類對象會返回一個集合對象,集合內(nèi)存儲類型的元素。七的子類初看很有可能以為集合元素就是單選表單元素,其實(shí)可以存儲任意類型的表單元素。八的子類開始,將返回子類的對象,其行為特征和一致。但在前,我們應(yīng)該先了解清楚的類型的特征。 一、前言                            大家先看看下面的js,猜猜結(jié)果會怎樣吧! 可選答案: ①. 獲取id屬性值為id的節(jié)點(diǎn)元素 ②...

    468122151 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<