2022 年 11 月 4 日

彈出視窗和視窗方法

彈出視窗是最早用來向使用者顯示其他文件的方法之一。

基本上,你只要執行

window.open('https://javascriptinfo.dev.org.tw/')

…它就會開啟一個包含指定 URL 的新視窗。大多數現代瀏覽器都已設定為在新分頁中開啟 URL,而不是在個別視窗中開啟。

彈出視窗從非常久以前就存在了。最初的想法是在不關閉主視窗的情況下顯示其他內容。現在,還有其他方法可以做到這一點:我們可以使用 fetch 動態載入內容,並在動態產生的 <div> 中顯示它。因此,彈出視窗並非我們每天都會使用到的東西。

此外,彈出視窗在行動裝置上很棘手,因為行動裝置無法同時顯示多個視窗。

儘管如此,仍有一些任務仍然使用快顯視窗,例如 OAuth 授權(使用 Google/Facebook/… 登入),原因如下:

  1. 快顯視窗是一個獨立的視窗,擁有自己的獨立 JavaScript 環境。因此,從第三方、不可信賴的網站開啟快顯視窗是安全的。
  2. 開啟快顯視窗非常容易。
  3. 快顯視窗可以導航(變更 URL)並傳送訊息給開啟它的視窗。

快顯視窗封鎖

過去,惡意網站大量濫用快顯視窗。惡意網頁可以開啟大量包含廣告的快顯視窗。因此,現在大多數瀏覽器都會嘗試封鎖快顯視窗並保護使用者。

如果快顯視窗是在使用者觸發的事件處理常式(例如 onclick)之外呼叫的,大多數瀏覽器都會封鎖它們。

例如

// popup blocked
window.open('https://javascriptinfo.dev.org.tw');

// popup allowed
button.onclick = () => {
  window.open('https://javascriptinfo.dev.org.tw');
};

透過這種方式,使用者可以獲得一定程度的保護,避免不需要的快顯視窗,但功能並未完全停用。

window.open

開啟快顯視窗的語法為:window.open(url, name, params)

url
要載入到新視窗中的 URL。
name
新視窗的名稱。每個視窗都有 window.name,我們可以在這裡指定要使用哪個視窗作為快顯視窗。如果已經有具有此名稱的視窗,則指定的 URL 會在該視窗中開啟,否則會開啟一個新視窗。
params
新視窗的組態字串。它包含以逗號分隔的設定。params 中不能有空格,例如:width=200,height=100

params 的設定

  • 位置
    • left/top(數字)– 視窗左上角在螢幕上的座標。有一個限制:新視窗不能定位在螢幕外。
    • width/height(數字)– 新視窗的寬度和高度。最小寬度/高度有限制,因此無法建立一個看不見的視窗。
  • 視窗功能
    • menubar(是/否)– 在新視窗中顯示或隱藏瀏覽器選單。
    • toolbar(是/否)– 在新視窗中顯示或隱藏瀏覽器導覽列(返回、前進、重新整理等)。
    • location(是/否)– 在新視窗中顯示或隱藏 URL 欄位。預設情況下,FF 和 IE 不允許隱藏它。
    • status(是/否)– 顯示或隱藏狀態列。同樣地,大多數瀏覽器都會強制顯示它。
    • resizable(是/否)– 允許停用新視窗的調整大小功能。不建議使用。
    • scrollbars(是/否)– 允許停用新視窗的捲軸。不建議使用。

還有一些較不受支援的瀏覽器特定功能,通常不會使用。請查看 MDN 中的 window.open 以取得範例。

範例:極簡視窗

讓我們開啟一個具有最小功能集的視窗,只是為了看看瀏覽器允許停用哪些功能

let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=0,height=0,left=-1000,top=-1000`;

open('/', 'test', params);

這裡大多數「視窗功能」都被停用,而且視窗被定位在螢幕外。執行它並看看實際發生什麼事。大多數瀏覽器會「修正」一些奇怪的事情,例如零 width/height 和螢幕外的 left/top。例如,Chrome 會以全寬/高開啟這樣的視窗,因此它會佔滿整個螢幕。

讓我們新增正常的定位選項和合理的 widthheightlefttop 座標

let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=600,height=300,left=100,top=100`;

open('/', 'test', params);

大部分瀏覽器都會如預期地顯示上述範例。

遺漏設定的規則

  • 如果 open 呼叫中沒有第 3 個參數,或為空值,則會使用預設的視窗參數。
  • 如果有一串參數,但遺漏了一些 yes/no 功能,則會假設遺漏的功能為 no 值。因此,如果您指定參數,請務必明確將所有必要的功能設定為 yes。
  • 如果參數中沒有 left/top,瀏覽器會嘗試在最後開啟的視窗附近開啟新的視窗。
  • 如果沒有 width/height,則新視窗的大小會與最後開啟的視窗相同。

從視窗存取快顯視窗

open 呼叫會傳回一個指向新視窗的參考。它可用於操作其屬性、變更位置,甚至更多。

在此範例中,我們從 JavaScript 產生快顯視窗內容

let newWin = window.open("about:blank", "hello", "width=200,height=200");

newWin.document.write("Hello, world!");

在此,我們在載入後修改內容

let newWindow = open('/', 'example', 'width=300,height=300')
newWindow.focus();

alert(newWindow.location.href); // (*) about:blank, loading hasn't started yet

newWindow.onload = function() {
  let html = `<div style="font-size:30px">Welcome!</div>`;
  newWindow.document.body.insertAdjacentHTML('afterbegin', html);
};

請注意:在 window.open 之後,新視窗尚未載入。第 (*) 行中的 alert 顯示了這一點。因此,我們會等到 onload 來修改它。我們也可以為 newWin.document 使用 DOMContentLoaded 處理常式。

同源政策

只有來自相同來源(相同的 protocol://domain:port)的視窗才能自由存取彼此的內容。

否則,例如,如果主視窗來自 site.com,而快顯視窗來自 gmail.com,則基於使用者的安全性考量,這是不可能的。有關詳細資訊,請參閱章節 跨視窗通訊

從快顯視窗存取視窗

快顯視窗也可以使用 window.opener 參考存取「開啟者」視窗。除了快顯視窗之外,所有視窗的 window.opener 都是 null

如果您執行以下程式碼,它會將開啟者(目前)視窗的內容替換為「測試」

let newWin = window.open("about:blank", "hello", "width=200,height=200");

newWin.document.write(
  "<script>window.opener.document.body.innerHTML = 'Test'<\/script>"
);

因此,視窗之間的連線是雙向的:主視窗和快顯視窗彼此都有參考。

關閉快顯視窗

要關閉視窗:win.close()

要檢查視窗是否已關閉:win.closed

技術上來說,close() 方法可供任何 window 使用,但如果 window 不是使用 window.open() 建立的,則大部分瀏覽器會忽略 window.close()。因此,它只會對快顯視窗有效。

如果視窗已關閉,closed 屬性會為 true。這有助於檢查快顯視窗(或主視窗)是否仍開啟。使用者可以隨時關閉它,而我們的程式碼應考慮這種可能性。

此程式碼會載入然後關閉視窗

let newWindow = open('/', 'example', 'width=300,height=300');

newWindow.onload = function() {
  newWindow.close();
  alert(newWindow.closed); // true
};

移動和調整大小

有方法可以移動/調整視窗大小

win.moveBy(x,y)
將視窗相對於目前位置向右移動 x 像素,向下移動 y 像素。允許負值(向左/向上移動)。
win.moveTo(x,y)
將視窗移動到螢幕上的座標 (x,y)
win.resizeBy(width,height)
根據給定的 width/height 相對於目前大小調整視窗。允許負值。
win.resizeTo(width,height)
將視窗調整為給定的尺寸。

也有 window.onresize 事件。

僅限快顯視窗

為了防止濫用,瀏覽器通常會封鎖這些方法。它們僅在我們開啟的快顯視窗中可靠運作,而且沒有其他分頁。

無法最小化/最大化

JavaScript 無法最小化或最大化視窗。這些作業系統層級功能對前端開發人員是隱藏的。

移動/調整大小的方法無法用於最大化/最小化的視窗。

捲動視窗

我們已經在 視窗大小和捲動 章節中討論過捲動視窗。

win.scrollBy(x,y)
將視窗向右捲動 x 像素,向下捲動 y 像素,相對於目前的捲動。允許負值。
win.scrollTo(x,y)
將視窗捲動到給定的座標 (x,y)
elem.scrollIntoView(top = true)
將視窗捲動,使 elem 出現在頂部(預設)或底部(elem.scrollIntoView(false))。

也有 window.onscroll 事件。

將焦點集中/移開視窗

理論上,有 window.focus()window.blur() 方法可以將焦點集中/移開視窗。還有 focus/blur 事件,允許在訪客將焦點集中在視窗上並切換到其他地方時捕捉到這個時刻。

不過,在實務上它們受到嚴格限制,因為過去有惡意網頁濫用它們。

例如,看看這段程式碼

window.onblur = () => window.focus();

當使用者嘗試切換出視窗(window.onblur)時,它會將焦點拉回視窗。目的是將使用者「鎖定」在 window 中。

因此,瀏覽器必須引入許多限制來禁止此類程式碼,並保護使用者免於廣告和惡意網頁的侵害。它們取決於瀏覽器。

例如,行動瀏覽器通常會完全忽略 window.focus()。此外,當快顯視窗在獨立分頁中開啟,而不是新視窗中開啟時,集中焦點也不會運作。

儘管如此,在某些使用案例中,此類呼叫確實有效且有用。

例如

  • 當我們開啟一個彈出視窗時,在視窗上執行 newWindow.focus() 可能會是一個好主意。萬一在某些作業系統/瀏覽器組合中,它可以確保使用者現在位於新視窗中。
  • 如果我們想要追蹤訪客實際使用我們網路應用程式的時間,我們可以追蹤 window.onfocus/onblur。這讓我們可以暫停/恢復頁面中的活動、動畫等。但請注意,blur 事件表示訪客已從視窗中切換出去,但他們仍然可能觀察到它。視窗在背景中,但仍然可能可見。

摘要

很少使用彈出視窗,因為有其他替代方案:在頁面中或在 iframe 中載入和顯示資訊。

如果我們要開啟一個彈出視窗,一個好習慣是通知使用者。連結或按鈕附近的「開啟視窗」圖示將允許訪客在焦點轉移時存活下來,並記住兩個視窗。

  • 可以使用 open(url, name, params) 呼叫開啟彈出視窗。它會傳回新開啟視窗的參考。
  • 瀏覽器會封鎖使用者動作以外的程式碼中的 open 呼叫。通常會出現一個通知,以便使用者可以允許它們。
  • 瀏覽器預設開啟一個新分頁,但如果提供了大小,則它將是一個彈出視窗。
  • 彈出視窗可以使用 window.opener 屬性存取開啟它的視窗。
  • 如果主視窗和彈出視窗具有相同的來源,則它們可以自由地讀取和修改彼此。否則,它們可以彼此變更位置,並交換訊息

若要關閉彈出視窗:使用 close() 呼叫。使用者也可以關閉它們(就像任何其他視窗一樣)。之後 window.closedtrue

  • 方法 focus()blur() 允許聚焦/取消聚焦視窗。但它們並非總是有效。
  • 事件 focusblur 允許追蹤進出視窗的切換。但請注意,即使在 blur 之後,視窗在背景狀態下仍然可見。
教學課程地圖

留言

留言前請先閱讀此內容…
  • 如果您有改進建議 - 請提交 GitHub 問題或提交拉取請求,而不是留言。
  • 如果您無法理解文章中的某個部分,請詳細說明。
  • 若要插入幾行程式碼,請使用 <code> 標籤,若要插入多行程式碼,請將它們包在 <pre> 標籤中,若要插入超過 10 行的程式碼,請使用沙盒 (plnkrjsbincodepen…)