二刷《高程》第八章BOM筆記整理。css
若是要在Web中使用JavaScript,那麼BOM(瀏覽器對象模型)纔是真正核心,BOM提供不少對象,用於訪問瀏覽器的功能,這些功能與任何網頁內容無關。html
window對象web
BOM的核心對象,表示瀏覽器的一個實例,它既是經過JavaScript訪問瀏覽器窗口的一個接口,又是ECMAScript規定的Global對象。這意味着在網頁中定義的任何一個對象,變量和函數都以window做爲其Global對象,所以有權訪問parseInt()等全局方法。
(1).全局做用域:拋開全局變量會成爲window對象的屬性不談,定義全局變量與在window對象上直接定義屬性仍是有點差異:全局變量不能經過delete操做符刪除,而直接在window對象上定義的屬性能夠。chrome
var age = 11; window.name = 'xx'; //在<IE9時拋錯,其餘全部瀏覽器返回false delete window.age; //false window.age; // 11 //在<IE9時拋錯,其餘全部瀏覽器返回true delete window.name; //true window.name; // undefined
緣由:當configurable特性被設置爲false時候表示不容許經過delete刪除屬性,而直接在對象上定義的屬性它的這個特性爲true。
<IE9中的表現是這樣的:
當嘗試訪問未聲明的變量會拋出錯誤,可是經過查詢window對象,能夠知道某個可能未聲明的變量是否存在。跨域
var a = b; //拋錯,嘗試訪問未聲明的變量b var a = window.b; //正常,嘗試訪問window.b這是一次屬性查詢到的值是undefiend
Windows Mobile平臺的IE瀏覽器不容許經過window.property = value之類的形式,直接在window對象上建立新的屬性或方法。但是在全局做用域中聲明的全部的變量和函數照樣會變成window對象的成員。
(2).窗口關係及框架:若是頁面中包含框架,則每一個框架都擁有本身的window對象,而且保存在frames集合中,能夠經過數值索引(從0開始,從左至右,從上到下)或者框架名稱來訪問相應的window對象。
每一個window對象都有一個name屬性,其中包含框架的名稱但Chrome下並不能經過主window直接訪問子集框架的window上的屬性(除過個別特殊屬性好比top,parent)這會致使跨域,以下
對於以下頁面:數組
<html> <head> <title>Frameset Example</title> </head> <frameset rows="160, *"> <frame src="frame.html" name="topFrame"> <frameset cols="50%, 50%"> <frame src="anotherframe" name="leftFrame"> <frame src="yetanotherframe" name="rightFrame"> </frameset> </frameset> </html>
frameset可定義一個框架集,它被用來組織多個窗口的(框架)。每一個框架有獨立的文檔,<frameset>標籤不能與<body>標籤一塊兒使用,但<noframes>標籤可放在body中,cols表示框架集中的列的數目和尺寸。rows表示行的數目和尺寸,單位爲px,%,*
以上代碼建立了一個框架集,其中一個框架居上,兩個框架居下,可經過window.frames[0]或window.frames["topFrame"]引用上方框架,也可以使用top而非window來引用這些框架(如top.frames[0])。瀏覽器
//訪問topFrame的window的幾種方式 window.frames[0]; window.frames["topFrame"]; top.frames[0]; top.frames["topFrame"]; frames[0]; frames["topFrame"] //剩下兩個frames的window對象訪問相似
<html> <head> <title>Frameset Example</title> </head> <frameset rows="100, *"> <frame src="frame.html" name="topFrame"> <frameset cols="50%, 50%"> <frame src="anthorframe.html" name="leftFrame"> <frame src="anotherframeset.html" name="rightFrame"> </frameset> </frameset> </html>
這個框架集中的一個框架rightFrame包含了另外一個框架集,該框架集的代碼以下:緩存
<html> <head> <title>Framset Example</title> </head> <frameset cols="50%, 50%"> <frame src="one.html" name="one"> <frame src="two.html" name="two"> </frameset> </html>
瀏覽器在加載完第一個框架後會繼續將第二個框架加載到rightFrame中,在one中訪問parent,那麼就是rightFrame。
在topFrame中,parent指向的是top,由於topFrame的直接上層框架就是最外層框架。
注意除非最高層窗口是經過window.open()打開的,不然其window對象的name屬性不會包含任何值。(??)安全
在使用框架狀況下,瀏覽器中會存在多個Global對象,在每一個框架中定義的全局變量會自動成爲框架中window對象的屬性,因爲每一個window對象都包含原生類型的構造函數,所以每一個框架都有一套本身的構造函數,這些構造函數一一對應但並不相等。top.frames[0].Object!==top.Object,這個問題會影響到對跨框架傳遞的對象使用instanceof操做符。以前有驗證過,見http://www.cnblogs.com/venoral/p/5232676.html
(3).窗口位置:用來操做和修改window對象位置的屬性和方法有不少,除了FF在screenX和screenY屬性中提供相同的窗口位置信息(Safari和Chrome也同時支持這兩個屬性),其餘多數瀏覽器提供了screenLeft,screenTop屬性用來表示窗口相對於屏幕屏幕左邊和上邊的位置。跨瀏覽器代碼爲:
服務器
var leftPos = (typeof window.screenLeft == 'number') ? window.screenLeft : window.screenX; var topPos = (typeof window.screenTop == 'number') ? window.screenTop : window.screenY;
在IE中screenTop和screenLeft保存的是從屏幕上邊和左邊到由window對象表示的頁面可見區的距離,即若是window對象是最外層對象,並且瀏覽器窗口緊貼屏幕最上端(y軸座標爲0),那麼screenTop的值就是位於頁面可見區上方的瀏覽器工具欄的像素高度,在Chrome和FF和Safari中,screenY或screenTop中保存的是整個瀏覽器窗口相對於屏幕的座標值,即在窗口的y軸座標爲0時返回0。
FF,Safari,Chrome始終返回頁面中每一個框架集的top.screenX和top.screenY的值,IE則會給出框架相對於屏幕邊界的精確座標值。
moveTo和moveBy這兩個方法不適用於框架,只能對最外層的window對象使用。
(4).窗口大小:跨瀏覽器肯定一個窗口大小,IE9+,FF,Safari,Opera,Chrome有四個屬性:innerWidth,innerHeight,outerWidth,outerHeight
outerWidth,outerHeight表示瀏覽器窗口自己大小(包括工具欄),innerWidth,innerHeight表示瀏覽器視口大小。
<=IE8沒有取得當前瀏覽器窗口的尺寸的屬性,不過它經過DOM提供頁面可視化區域相關信息。
在IE,FF,Chrome,Safari,Opera中,document.documentElement.clientWidth和document.documentElement.clientHeight中保存了頁面視口的信息。在IE6中這些屬性必須在標準模式下才有效,若是在混雜模式就需經過document.body.clientWidth和document.body.clientHeight取得相同信息。
取得頁面視口的大小:
var pageWidth = window.innerWidth, pageHeight = window.innerHeight; //<=IE8 if(typeof pageWidth != 'number'){ if(document.compatMode == 'CSS1Compat'){ pageWidth = document.documentElement.clientWidth; pageHeight = document.documentElement.clientHeight; }else{ pageWidth = document.body.clientWidth; pageHeight = document.body.clientHeight; } }
對於移動設備,window.innerWidth和window.innerHeight保存着可見視口,即屏幕上可見頁面區域大小。移動IE不支持這些屬性但經過document.documentElement.clientWidth和document.documentElement.clientHeight提供相同信息,隨着頁面縮放這些值也會相應變化。
在其餘移動瀏覽器中,document.documentElement度量的是佈局視口,即渲染後頁面的實際大小(與可見視口不一樣,可見視口只是整個頁面中的一小部分)。移動IE瀏覽器把佈局視口的信息保存在document.body.clientWidth和document.body.clientHeight中,這些值不會隨着頁面縮放而變化(??)。
resizeTo和resizeBy方法不適用於框架只能對最外層window對象使用。
(5).導航和打開窗口:
window.open(url, 窗口目標, 一個特性字符串, 新頁面是否取代瀏覽器歷史記錄中當前加載頁面的布爾值):能夠導航到一個特定的url,也能夠打開一個新的瀏覽器窗口。一般只需傳遞第一個參數,最後一個參數只在不打開新窗口的狀況下使用。第二個參數是已有窗口或框架的名稱,那麼就會在具備該名稱的窗口或框架中加載第一個參數指定的URL
//等同於<a href="http://www.wrox.com" target="topFrame"></a> window.open(「http://www.wrox.com/」, "topFrame");
若是有一個名叫"topFrame"的窗口或框架就會在該窗口或框架加載這個URL,不然就會新建一個窗口並將其命名爲」topFrame「,此外第二個參數能夠是」_self「,"_parent","_top","_blank"
測試了一下在框架中window.open()會有跨域的限制:
開了本地服務器試試:主頁面和test.html的域名都爲localhost
這是以前:
當執行了window.open('test.html', 'two')後:name爲」two「的框架內容載入」test.html「,控制檯返回"two"的window對象。(我這裏不清楚爲何這個路徑必須是相對的才能載入」test.html「頁面,執行window.open("localhost:3000/test.html", "two")雖然不會報錯還會返回two的window對象,但two框架就是不會載入新頁面。當加上協議名後才能成功window.open('http://localhost:3000/test.html', 'two')。
var blocked = false; try{ var wroxWin = window.open("http://www.baidu.com", "_blank"); if(wroxWin == null){ blocked = true; } }catch(e){ blocked = true; } if(blocked){ console.log("The popup was blocked!"); }
(6).間歇調用和超時調用:
setTimeout():第一個參數能夠是一個包含JS代碼的字符串(就和在eval()函數中使用的字符串同樣)但傳遞字符串可能致使性能損失不建議,也能夠是一個函數。第二個參數是一個表示等待多長時間的毫秒數,但通過該時間後指定的代碼不必定會執行,JS是一個單線程序的解釋器,所以必定時間只能執行一段代碼,爲了控制要執行的代碼,就有一個JS任務隊列,這些任務會按照將它們添加到隊列的順序執行。第二個參數能夠理解成告訴JS再過多長時間把當前任務添加到隊列中,若是隊列是空的,那麼添加的代碼會當即執行,若是隊列不爲空那麼就要前面的代碼執行完後再執行。
setTimeout和setInterval()會返回一個數值ID,這個ID是計劃執行代碼的惟一標識符,要取消還沒有執行的超時調用計劃,能夠調用clearTimeout和clearInterval,只要在指定的時間還沒有過去以前調用clearTimeout或clearInterval就能夠徹底取消超時調用(在還沒加到執行隊列中就取消)。超時調用的代碼都是在全局做用域中執行的,所以函數中的this的值在非嚴格模式下指向window對象,嚴格模式下是undefiend(但是經測試嚴格模式下並非啊仍然會返回window...)。
var num = 0, max = 10, intervalId = null; function incrementNumber(){ num++; //若是執行次數達到了max設定的值,則取消後續還沒有執行的調用 if(num == max){ clearInterval(intervalId); console.log('done'); } } intervalId = setInterval(incrementNumber, 500);
var num = 0, max = 10; function incrementNumber(){ num++; if(num == max){ return; }else{ setTimeout(incrementNumber, 500); } } setTimeout(incrementNumber, 500);
可見在設置超時調用時不必跟蹤超時調用ID,由於每次執行代碼以後,若是再也不設置另外一次超時調用,調用就會中止。
(7).系統對話框:與在瀏覽器中顯示的網頁沒有關係,也不包含HTML,它們的外觀由操做系統及(或)瀏覽器設置決定,不是由css決定。經過這幾個方法打開的對話框都是同步和模態的,顯示這些對話框的時候代碼會中止執行,關掉這些對話框後代碼又恢復執行。
alert():顯示一些用戶沒法控制的消息,例如錯誤消息。用戶只能在看完消息後關閉對話框。
confirm():確認對話框除了顯示OK按鈕外,還會顯示一個Cancel(取消)按鈕,點擊「確認」返回true,點擊「取消」或右上角的X按鈕返回false
prompt(str1, str2):提示框,用於提示用戶輸入文本,提示框中除了顯示OK和Cancel按鈕以外,還會顯示一個文本輸入域,以供用戶在其中輸入內容。兩個參數:要顯示給用戶的文本提示和文本輸入域的默認值(能夠是一個空字符串)。點擊「肯定」返回用戶所填的字符串(若是沒填則返回""),點擊「取消」或右上角的X按鈕返回null。
下面兩個對話框方法是異步顯示的,Chrome的對話框計數器不會將它們計算在內,因此它們也不會受用戶禁用後續對話框顯示的影響。
print():顯示打印對話框,會調出瀏覽器自帶的打印的程序。
find():顯示查找對話框。
Location對象
提供與當前窗口加載的文檔有關的信息,還提供了一些導航功能。它既是window對象的屬性又是document對象的屬性(只能說window.location和document.location指向的是同一個對象,即location對象)。location對象的用戶不僅表如今它保存着當前文檔的信息,還表如今它將URL解析爲獨立的片斷,讓開發人員能夠經過不一樣的屬性訪問這些片斷。
原型鏈繼承關係爲:window.location.__proto__->Location.prototype->Object.prototype
hash:返回URL中的hash(#後跟零或多個字符),若是URL不包含散列,則返回空字符串。
host:返回服務器名稱和端口號(若是有)
hostname:返回不帶端口號的服務器名稱
href:返回當前加載頁面的完整URL,location.toString()也返回這個。
pathname:返回URL中目錄和(或)文件名
port:返回URL中指定的端口號,若是URL中不包含端口號,則這個屬性返回空字符串。
protocol:返回頁面使用的協議,一般是http:或https:
search:返回URL查詢字符串,這個字符串以問號開頭
(1).查詢字符串參數:解析查詢字符串,返回包含全部參數的一個對象
function getQueryStringArgs(){ var argdecos = {}; //取得查詢字符串並去掉開頭問號 var qs = location.search.length > 0 ? location.search.substring(1) : ""; //取得每一項 var items = qs.length ? qs.split("&") : [], item = null, name = null, value= null, i=0, len = items.length; for(i= 0; i< len; i++){ item = items[i].split("="); name = decodeURIComponent(item[0]); value = decodeURIComponent(item[1]); if(name.length){ args[name] = value; } } return args; }
(2).位置操做
location對象能夠經過不少方式來改變瀏覽器位置
assign(url):能夠打開新URL並在瀏覽器的歷史記錄中生成一條記錄,若是是將location.href或window.location設置爲一個URL值,也會以該值調用assign()方法。
window.location = "http://www.baidu.com"; location.href = "http://www.baidu.com"; //經常使用 //等價於 location.assign("http://www.baidu.com");
修改location對象的其餘屬性也能夠改變當前加載的頁面,經過將hash,search,hostname,pathname,port屬性設置爲新值來改變URL,每次修改location的屬性(hash除外),頁面都會以新的URL從新加載。
在IE8,FF1,Safari2+,Opera9+,Chrome中,修改hash值會在瀏覽器歷史記錄中生成一條新紀錄,在IE早期版本中,hash屬性不會在用戶單擊」後退「,」前進「按鈕時被更新,只會在用戶單擊包含hash的URL時纔會更新。
當經過上述任何一種方式修改URL後,瀏覽器歷史記錄會生成一條新紀錄,所以用戶經過單擊」後退「按鈕都會導航到前一個頁面。要禁用這種行爲,可使用replace(url)方法,參數是要導航到的URL,結果雖然會致使瀏覽器位置改變,但不會在歷史記錄中生成新紀錄,在調用replace方法後用戶不能回到前一個頁面(即便後退按鈕沒有被禁用,但點擊後回到的是被調用replace方法的那個頁面的上一個頁面)。
reload():從新加載當前顯示頁,不傳參數頁面會以最有效的方式從新加載,即若是頁面自上次請求以來並無改變過,頁面就會從瀏覽器緩存中從新加載。若是要強制從服務器從新加載,須要給參數傳遞true(經測試也沒有效果呀..仍舊是304??)。
位於reload後面的代碼有可能會也可能不會執行,這要取決於網絡延遲或系統資源等因素。Chrome下測試是先運行後面的代碼再執行reload。
navigator對象
識別客戶端瀏覽器,navigator對象是全部支持JS瀏覽器所共有的,IE中window.clientInformation和Opera中的window.opera。
Chrome和IE中window.clientInformation == window.navigator,FF沒有該屬性。
navigator.__proto__ -> Navigator.prototype -> Obeject.prototype
navigator.appCodeName:瀏覽器名稱,一般都是Mozilla,即便在非Mozilla瀏覽器中也是如此。
navigator.userAgent:瀏覽器用戶代理字符串,chrome爲:
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"
(1).檢測插件:在非IE中可使用navigator.plugins(並非說IE不能訪問navigator.plugins,能訪問但返回的對象length屬性爲0),返回PluginArray類型的實例。
navigator.plugins.__proto__ -> PluginArray.prototype -> Object.prototype
plugins集合能夠繼承refresh(arg)方法,用於刷新plugins以反映最新安裝的插件,參數arg表示是否應該從新加載頁面的一個布爾值,true爲從新加載包含插件的全部頁面,不然只更新plugins集合不從新加載頁面。
navigator.plugins的每一項是Plugin類型的實例,
navigator.plugins.__proto__ -> Plugin.prototype -> Object.prototype
name:插件的名字
description:插件的描述
filename:插件的文件名
length:插件所處理的MIME類型的數量
在檢測插件時須要循環迭代每一個插件並將插件的name與給定名字進行比較:
function hasPlugin(name){ name = name.toLowerCase(); for(var i=0; i<navigator.plugins.length; i++){ if(navigator.plugins[i].name.toLowerCase().indexOf(name) > -1) return true; } return false; }
navigator.mimeTypes:在瀏覽器中註冊的MIME類型數組,是MimeTypeArray類型實例對象
navigator.mimeTypes.__proto__ -> MimeTypeArray.prototype -> Obejct.prototype
navigator.mimeTypes的每一項都是MimeType類型的實例
navigator.mimeTypes[0].__proto__ -> MimeType.prototype -> Obejct.prototype
description:MIME類型描述
enabledPlugin:回指插件對象
suffixes:與MIME類型對應的文件擴展名字符串
type:完整MIME類型字符串的type
檢測IE中插件:由於IE不支持Netspace式的插件,在IE中檢測插件的惟一方式就是使用專有的ActiveXObejct類型,並嘗試建立一個特定插件的實例,IE是以COM對象的方式實現插件的,而COM對象使用惟一標識符來標識的,所以想要檢測特定插件就必須知道其COM標識符,Flash的標識符是ShockwaveFlash.ShockwaveFlash,知道惟一標識符後就能夠編寫相似下面函數來檢測IE中是否安裝相應插件:
function hasIEPlugin(name){ try{ new ActiveXObject(name); return true; }catch(ex){ return false; } } hasIEPlugin("ShockwaveFlash.ShockwaveFlash");
hasIEPlugin("QuickTime.QuickTime");
鑑於IE和非IE檢測插件方法差異太大,所以典型作法就是針對每一個插分別建立檢測函數:
//檢測全部瀏覽器的Flash function hasFlash(){ var result = hasPlugin("Flash"); if(!result){ result = hasIEPlugin("ShockwaveFlash.ShockwaveFlash"); } return result; } //檢測全部瀏覽器QuickTime function hasQuickTime(){ var result = hasPlugin("QuickTime"); if(!result){ result = hasIEPlugin("QuickTime.QuickTime"); } return result; }
(2).註冊處理程序:
FF2爲navigator對象新增了registerContentHandler()和registerProtocolHandler()方法(這兩方法在H5中定義的),可讓一個站點指明它能夠處理特定類型的信息。隨着RSS閱讀器和在線電子郵件程序興起,註冊處理程序就爲像使用桌面應用程序同樣默認使用這些在線應用程序提供了一種方式。
registerContentHandler(要處理的MIME類型, 能夠處理該MIME類型的頁面URL, 應用程序名稱):好比將一個站點註冊爲處理RSS源的處理程序
navigator.registerContentHandler("application/rss+xml", "http://www.somereader.com?feed=%s", "Some Reader");
第一個參數是RSS源的MIME類型,第二個參數是應該接收RSS源URL的URL,其中%s表示RSS源URL,由瀏覽器自動插入,當下次請求RSS源時,瀏覽器就會打開指定的URL,而相應的web應用程序將以適當方式來處理該請求。
<=FF4版本只容許在registerContentHandler()方法中使用單個MIME類型:application/rss+xml,application/atom+xml,application/vnd.mozilla.maybe.feed,這三個MIME類型做用都同樣即爲RSS或ATOM新聞源(feed)註冊處理程序。
registerProtocolHandler(要處理的協議例如mailto或ftp, 處理該協議的頁面的URL, 應用程序名稱)方法:將一個應用程序註冊爲默認的郵件客戶端,該例子註冊了一個mailto協議的處理程序,該程序指向一個基於web的電子郵件客戶端,第二個參數仍然是處理相應請求的URL,%s表示原始的請求。
navigator.registerProtocolHandler("mailto", "http://www.someemailclient.com?cmd=%s", "Some Mail Client");
screen對象
代表客戶端的能力,包括瀏覽器窗口外部的顯示器信息,如像素寬度和高度等。每一個瀏覽器中的screen對象都包含着各不相同的屬性
screen.__proto__ -> Screen.prototype -> Obeject.prototype
screen.availWidth:屏幕的像素寬度減系統部件寬度以後的值
screen.avaliHeight:屏幕的像素高度減系統部件高度以後的值
screen.avaliLeft:未被系統部件佔用的最左側的像素值
screen.avaliTop:未被系統部件佔用最上方的像素值
height:屏幕的像素高度
width:屏幕的像素寬度
history對象
保存用戶上網歷史記錄,從窗口被打開那一刻算起。由於history是window對象的屬性,所以每一個瀏覽器窗口,每一個標籤頁乃至每一個框架都有本身的history對象與特定的window對象關聯。處於安全考慮開發人員沒法得知用戶瀏覽過的URL,但能夠藉助用戶訪問過的頁面列表,一樣能夠在不知道實際URL的狀況下實現後退和前進。
雖然不經常使用,但在建立自定義的」後退「和」前進「按鈕,檢測當前頁面是否是用戶歷史記錄中的第一個頁面還得用。
history.__proto__ -> History.prototype -> Object.prototype
length:保存着歷史記錄的數量,包括全部歷史記錄,全部向前和向後的記錄。對於加載到窗口,標籤頁或框架中的第一個頁面而言,history.length等於0,可用此來判斷用戶是否一開始就打開了你的頁面。
history.go(args):在用戶的歷史記錄中任意跳轉,可向前也可向後,參數爲向前或向後跳轉頁面數的一個整數值。負數表示向後跳轉(相似單擊瀏覽器後退按鈕),正數表示向前跳轉(相似單擊瀏覽器前進按鈕),參數也能夠是字符串,此時瀏覽器會跳轉到歷史記錄中包含該字符串的第一個位置——可能後退,也可能前進,具體要看哪一個位置更近。若是記錄中不包含該字符串,什麼也不作。
history.go("baidu.com"); //調到最近的」baidu.com「頁面
history.back():後退一頁
history.forward(): 前進一頁
當頁面URL改變時,就會生成一條歷史記錄,包括hash的變化
參考
《JavaScript高級程序設計》