1. 程式人生 > 其它 >彈窗和 window 的方法

彈窗和 window 的方法

彈窗(popup)是向用戶顯示其他文件的最古老的方法之一。基本上,你只需要執行:

window.open('https://JavaScript.info/')

它將開啟一個具有給定 URL 的新視窗。大多數現代瀏覽器都配置為在新選項卡中開啟 url,而不是單獨的視窗。

彈窗自古以來就存在。最初的想法是,在不關閉主視窗的情況下顯示其他內容。目前為止,還有其他方式可以實現這一點:我們可以使用fetch動態載入內容,並將其顯示在動態生成的<div>中。彈窗並不是我們每天都會使用的東西。

並且,彈窗在移動裝置上非常棘手,因為移動裝置無法同時顯示多個視窗。

但仍然有一些任務在使用彈窗,例如進行 OAuth 授權(使用 Google/Facebook/… 登陸),因為:

彈窗是一個獨立的視窗,具有自己的獨立JavaScript環境。因此,使用彈窗開啟一個不信任的第三方網站是安全的。
開啟彈窗非常容易。
彈窗可以導航(修改 URL),並將訊息傳送到 opener 視窗(譯註:即開啟彈窗的視窗)。

阻止彈窗

在過去,很多惡意網站經常濫用彈窗。一個不好的頁面可能會開啟大量帶有廣告的彈窗。因此,現在大多數瀏覽器都會通過阻止彈窗來保護使用者。

如果彈窗是在使用者觸發的事件處理程式(如onclick)之外呼叫的,大多數瀏覽器都會阻止此類彈窗。

例如:

// 彈窗被阻止
window.open('https://javascript.info');

// 彈窗被允許
button.onclick = () => {
  window.open('https://javascript.info');
};

這種方式可以在某種程度上保護使用者免受非必要的彈窗的影響,但是並沒有完全阻止該功能。

如果彈窗是從onclick開啟的,但是在setTimeout之後,該怎麼辦?這有點棘手。

試試執行一下這段程式碼:

// 3 秒後開啟彈窗
setTimeout(() => window.open('http://google.com'), 3000);

這個彈窗在 Chrome 中會被開啟,但是在 Firefox 中會被阻止。

……如果我們減少延遲,則彈窗在 Firefox 中也會被開啟:

// 1 秒後開啟彈窗
setTimeout(() => window.open('http://google.com'), 1000);

區別在於 Firefox 可以接受 2000ms 或更短的延遲,但是超過這個時間 —— 則移除“信任”。所以,第一個彈窗被阻止,而第二個卻沒有。

window.open

開啟一個彈窗的語法是window.open(url, name, params):

url:要在新視窗中載入的 URL。
name:新視窗的名稱。每個視窗都有一個window.name,在這裡我們可以指定哪個視窗用於彈窗。如果已經有一個這樣名字的視窗 —— 將在該視窗開啟給定的 URL,否則會開啟一個新視窗。
params:新視窗的配置字串。它包括設定,用逗號分隔。引數之間不能有空格,例如:width=200,height=100。

params的設定項:

位置:

left/top(數字)—— 螢幕上視窗的左上角的座標。這有一個限制:不能將新視窗置於螢幕外(offscreen)。
width/height(數字)—— 新視窗的寬度和高度。寬度/高度的最小值是有限制的,因此不可能建立一個不可見的視窗。

視窗功能:

menubar(yes/no)—— 顯示或隱藏新視窗的瀏覽器選單。
toolbar(yes/no)—— 顯示或隱藏新視窗的瀏覽器導航欄(後退,前進,重新載入等)。
location(yes/no)—— 顯示或隱藏新視窗的 URL 欄位。Firefox 和 IE 瀏覽器不允許預設隱藏它。
status(yes/no)—— 顯示或隱藏狀態列。同樣,大多數瀏覽器都強制顯示它。
resizable(yes/no)—— 允許禁用新視窗大小調整。不建議使用。
scrollbars(yes/no)—— 允許禁用新視窗的滾動條。不建議使用。

還有一些不太受支援的特定於瀏覽器的功能,通常不使用。通常不使用這些功能。更多示例請見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為零以及脫離螢幕(offscreen)的left/top設定。例如,Chrome 打開了一個全width/height的視窗,使其佔滿整個螢幕。

讓我們新增正常的定位選項和合理的width、height、left和top座標:

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呼叫中沒有第三個引數,或者它是空的,則使用預設的視窗引數。
如果這裡有一個引數字串,但是某些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,載入尚未開始

newWindow.onload = function() {
  let html = `<div>Welcome!</div>`;
  newWindow.document.body.insertAdjacenthtml('afterbegin', html);
};

請注意:在剛剛進行了window.open的時候,新視窗還沒有載入完成。我們可以通過(*)行中的alert證實這一點。因此,我們需要等待onload以對新視窗進行更改。我們也可以對newWin.document使用DOMContentLoaded處理程式。

同源策略:
只有在視窗是同源的時,窗口才能自由訪問彼此的內容(相同的協議://domain:port)。
否則,例如,如果主視窗來自於site.com,彈窗來自於gmail.com,則處於安全性考慮,這兩個視窗不能訪問彼此的內容。

從彈窗訪問視窗

彈窗也可以使用window.opener來訪問 opener 視窗。除了彈窗之外,對其他所有視窗來說,window.opener均為null。

如果你執行下面這段程式碼,它將用 “Test” 替換 opener(也就是當前的)視窗的內容:

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()。因此,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 無法最小化或者最大化一個視窗。這些作業系統級別的功能對於前端開發者而言是隱藏的。
移動或者調整大小的方法不適用於最小化/最大化的視窗。

滾動視窗

我們已經在Window 大小和滾動一章中討論過了滾動視窗。

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()是個好主意。以防萬一,對於某些作業系統/瀏覽器組合(combination),它可以確保使用者現在位於新視窗中。

如果我們想要跟蹤訪問者何時在實際使用我們的 Web 應用程式,我們可以跟蹤window.onfocus/onblur。這使我們可以暫停/恢復頁面活動和動畫等。但是請注意,blur事件意味著訪問者從視窗切換了出來,但他們仍然可以觀察到它。視窗處在背景中,但可能仍然是可見的。

https://www.houdianzi.com/ logo設計公司

總結

彈窗很少使用,因為有其他選擇:在頁面內或在 iframe 中載入和顯示資訊。

如果我們要開啟一個彈窗,將其告知使用者是一個好的實踐。在連結或按鈕附近的“開啟視窗”圖示可以讓使用者免受焦點轉移的困擾,並使使用者知道點選它會彈出一個新視窗。

可以通過open(url, name, params)呼叫開啟一個彈窗。它會返回對新開啟的視窗的引用。

瀏覽器會阻止來自使用者行為之外的程式碼中的open呼叫。通常會顯示一條通知,以便使用者可以允許它們。

預設情況下,瀏覽器會開啟一個新標籤頁,但如果提供了視窗大小,那麼瀏覽器將開啟一個彈窗。

彈窗可以使用window.opener屬性訪問 opener 視窗(譯註:即開啟彈窗的視窗)。

如果主視窗和彈窗同源,那麼它們可以彼此自由地讀取和修改。否則,它們可以更改彼此的地址(location),交換訊息。

要關閉彈窗:使用close()呼叫。使用者也可以關閉彈窗(就像任何其他視窗一樣)。關閉之後,window.closed為true。

focus()和blur()方法允許聚焦/失焦於視窗。但它們並不是一直都有效。

focus和blur事件允許跟蹤視窗的切換。但是請注意,在blur之後,即使視窗在背景狀態下,視窗仍有可能是可見的。