Learning Objectives
- 說明 window 是瀏覽器的全域物件:全域的 var 變數與函式宣告會成為 window 的屬性,let、const 不會
- 使用 alert、confirm、prompt 對話框並說明回傳值,認識 open、close 等視窗方法的限制
- 用 location、history、navigator、screen 取得瀏覽器與網址的資訊,並用 URL、URLSearchParams 解析網址
- 使用 document 的常用屬性:title、URL、forms、links、images,並說明為什麼不要用 document.write
window:瀏覽器分頁本身
前幾課一直在用 document,它其實是 window.document。window 物件代表載入網頁的那個瀏覽器視窗(分頁),
是瀏覽器裡 JavaScript 的全域物件,可以把它的成員分成三類:
| 類別 | 例子 | 內容 |
|---|---|---|
| 環境物件 | location、history、navigator、screen |
網址、瀏覽紀錄、瀏覽器、螢幕等外部環境的資訊 |
| JavaScript 物件 | Math、JSON、console,以及你在最外層宣告的變數與函式 |
語言內建的物件與全域的名稱 |
| Document 物件 | document |
目前載入的網頁(DOM 樹) |
因為 window 是全域物件,使用它的成員時可以省略 window.:alert('嗨') 就是 window.alert('嗨'),
innerWidth 就是 window.innerWidth。
全域變數與 window
在瀏覽器裡,寫在最外層的宣告與 window 的關係如下:
| 寫法 | 是全域變數嗎 | 會變成 window 的屬性嗎 |
|---|---|---|
var siteName = 'Uedu' |
是 | 會:window.siteName 是 'Uedu' |
function hello() {} |
是 | 會:window.hello 就是這個函式 |
let version = 3、const MAX = 10 |
是 | 不會:window.version 是 undefined |
count = 1(沒有宣告就指定) |
不是宣告 | 一般模式下會自動變成 window 的屬性;嚴格模式('use strict')下直接出現 ReferenceError |
全域的名稱越多,越容易和別人的程式(或瀏覽器新增的屬性)撞名,造成難以察覺的錯誤。實務上盡量少用全域變數,
宣告一律用 let、const,變數放進函式或模組裡。
對話框是 window 最常用的方法:
| 方法 | 畫面 | 回傳 |
|---|---|---|
alert('訊息') |
訊息+「確定」 | undefined |
confirm('確定刪除?') |
訊息+「確定」「取消」 | 按確定是 true,按取消是 false |
prompt('你的名字?', '預設值') |
訊息+輸入框 | 輸入的內容(一定是字串);按取消是 null |
對話框會讓整個頁面停住,直到使用者按下按鈕,所以正式的網站多半改用頁面裡的對話框元件(Uedu 用的是 SweetAlert2)。
這一課的沙箱不會真的跳出視窗:confirm 一律當作按了「確定」,prompt 回傳你寫的預設值,內容都顯示在主控台。
想一想:window 與對話框
瀏覽器的網頁最外層寫了 var a = 1; 與 let b = 2;,window.a 與 window.b 分別是什麼?
下面的程式會印出什麼?(第一行模擬使用者在 prompt 輸入 5:prompt 回傳的是字串)
const n = '5';
console.log(n + 1);
使用者在 prompt 的對話框按了「取消」,prompt 回傳什麼?
開啟與控制視窗
| 方法 | 作用 |
|---|---|
const w = window.open(網址, 名稱, 設定) |
開啟新視窗或分頁,回傳新視窗的 window 物件(被擋下時是 null) |
w.close() |
關閉視窗;一般只能關閉用 window.open 開出來的視窗 |
focus()/blur() |
讓視窗取得、失去焦點 |
moveTo(x, y)/moveBy(dx, dy) |
把視窗移到某個位置、移動一段距離 |
resizeTo(寬, 高)/resizeBy(dw, dh) |
調整視窗大小 |
window.open 的第三個參數是一串設定,例如 'width=500,height=500,left=100,top=100'。
這些方法在現在的瀏覽器裡都受到限制:
- 瀏覽器會擋下不是由使用者操作觸發的彈出視窗,
window.open最好寫在按鈕的點擊事件裡。 resizeTo這類方法只對用window.open開出來的視窗有效,不能調整使用者自己開的分頁,在手機上可能完全沒有作用。
所以這些方法看得懂即可,這一課的沙箱也不允許開新視窗。
location、history、navigator 與 screen
location:目前網頁的網址。以 https://uedu.tw:8443/codelab/javascript/j18-window?tab=js#recap 為例:
| 屬性 | 值 |
|---|---|
location.href |
完整網址;指定新值就會前往該網址 |
location.protocol |
'https:' |
location.hostname |
'uedu.tw' |
location.port |
'8443'(使用預設連接埠時是空字串) |
location.host |
'uedu.tw:8443'(主機名稱加連接埠) |
location.pathname |
'/codelab/javascript/j18-window' |
location.search |
'?tab=js'(查詢字串,包含問號) |
location.hash |
'#recap'(包含 #) |
location.origin |
'https://uedu.tw:8443'(通訊協定+主機名稱+連接埠) |
方法:location.assign(網址) 前往新網址(與設定 href 相同)、location.replace(網址) 前往但不留下上一頁紀錄、location.reload() 重新載入。
history:這個分頁的瀏覽紀錄。history.back() 上一頁、history.forward() 下一頁、history.go(-2) 往回兩頁、history.length 紀錄筆數。
基於隱私,程式只能前後移動,讀不到使用者看過哪些網址。
navigator:瀏覽器的資訊,例如 navigator.language(介面語言,如 'zh-TW')、navigator.onLine(是否連上網路)、
navigator.userAgent(瀏覽器識別字串,可以偽造,不適合拿來判斷瀏覽器種類)。
screen:使用者螢幕的資訊,例如 screen.width、screen.height。網頁本身可以使用的區域則是 window.innerWidth、window.innerHeight。
這一課的沙箱網頁網址是 about:srcdoc,沒有主機名稱與路徑,示範不了 location。
location 的屬性名稱和 JavaScript 的 URL 物件相同,下面改用 new URL(網址) 拆解任意網址。
用 URL 物件拆解網址
URL 物件的各個部分在右邊顯示為空的大括號(它們是讀取時才計算的屬性),所以範例把每個部分存進變數。一步一步播放,比較 hostname 與 host、port 的值,以及最後一個網址為什麼沒有連接埠。
讀取查詢字串:URLSearchParams
網址問號後面的 ?q=closure&page=2 叫做查詢字串(query string),常用來把搜尋關鍵字、頁碼傳給網頁。
用字串的 split 自己拆很容易出錯(例如中文與空白會被編碼成 %E4%B8%AD),請用 URLSearchParams:
const params = new URLSearchParams(location.search); // 在網頁裡讀目前網址的查詢字串
const params2 = new URL(某個網址).searchParams; // 或從 URL 物件取得
| 方法 | 作用 |
|---|---|
params.get('page') |
取得值(一律是字串);沒有這個參數回傳 null |
params.getAll('tag') |
同名參數出現好幾次時,取得全部的值(陣列) |
params.has('sort') |
有沒有這個參數 |
params.set('page', '3')/params.delete('tag') |
修改、刪除參數 |
for (const [key, value] of params) |
逐一取得每個參數 |
讀取與修改查詢字串
注意第 7 行印出的 typeof page:查詢字串裡的數字讀出來是字串,計算前要先轉成數字。第 19、20 行修改參數之後,url.search 也跟著改變。
想一想:網址與查詢字串
下面的程式會印出什麼?
const u = new URL('https://shop.example.com/list?page=2#top');
console.log(u.search, u.hash);
下面的程式會印出什麼?
const u = new URL('http://localhost:8080/app');
console.log(u.host);
下面的程式會印出什麼?
const p = new URLSearchParams('?page=2&size=10');
console.log(p.get('page') + 1);
在網頁裡執行 location.href = 'https://uedu.tw/codelab' 會發生什麼事?
實作:從網址算出金額
購物網站把商品、數量與單價放在查詢字串裡。請用 searchParams 讀出 item、qty、price,印出「pen x 3 = 75」這樣的一行(記得數量與單價要先轉成數字再相乘)。
document 的常用屬性
瀏覽器讀入網頁時,依內容建立 DOM,document 就是它的根。除了前幾課的 getElementById、querySelector,document 還有這些常用屬性:
| 屬性 | 內容 |
|---|---|
document.title |
網頁標題(<title> 的文字),可以讀也可以改 |
document.URL |
網頁的完整網址(唯讀) |
document.forms |
頁面上所有的 <form>(HTMLCollection) |
document.links |
所有有 href 的 <a> 與 <area> |
document.images |
所有的 <img> |
document.getElementsByName('email') |
所有 name 是 email 的元素 |
投影片還介紹了 document.anchors,它只收錄有 name 屬性的 <a>,已經被列為不建議使用的功能。
不要用 document.write。它把字串直接寫進正在載入的文件;如果在網頁載入完成之後才呼叫(例如寫在按鈕的事件裡),
它會先清掉整份文件再重寫,整個頁面就不見了。要加入內容,請用前幾課的 createElement、append 或 textContent。
重點精華回顧
window是瀏覽器的全域物件,使用它的成員時可以省略window.;最外層的var變數與函式宣告會變成 window 的屬性,let、const不會。- 對話框:
alert回傳undefined、confirm回傳true/false、prompt回傳字串或null(按取消)。 open、close、moveTo、resizeTo受到瀏覽器限制:彈出視窗要由使用者操作觸發,調整大小只對程式開出來的視窗有效。location存放目前網址(href、hostname、pathname、search、hash…),指定href會前往新網址;history前後移動;navigator是瀏覽器資訊;screen是螢幕資訊。- 用
new URL(網址)拆解網址,用URLSearchParams讀取查詢字串;讀出的值一律是字串。 document的常用屬性:title、URL、forms、links、images;不要使用document.write。