天天看點

彈窗和 window 的方法

彈窗和 window 的方法

彈窗(popup)是向使用者顯示其他文檔的最古老的方法之一。基本上,你隻需要運作:

window.open('http://www.webqdkf.com/')      

它将打開一個具有給定 URL 的新視窗。大多數現代浏覽器都配置為在新頁籤中打開 url,而不是單獨的視窗。

彈窗自古以來就存在。

最初的想法是,在不關閉主視窗的情況下顯示其他内容。目前為止,還有其他方式可以實作這一點:我們可以使用 fetch 動态加載内容,并将其顯示在動态生成的 <div> 中。

彈窗并不是我們每天都會使用的東西。

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

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

彈窗是一個獨立的視窗,具有自己的獨立 JavaScript 環境。是以,使用彈窗打開一個不信任的第三方網站是安全的。

打開彈窗非常容易。

彈窗可以導航(修改 URL),并将消息發送到 opener 視窗(譯注:即打開彈窗的視窗)。

阻止彈窗

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

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

例如:

// 彈窗被阻止
window.open('http://www.webqdkf.com/');


// 彈窗被允許
button.onclick = () => {
  window.open('http://www.webqdkf.com/');
};      

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

如果彈窗是從 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 事件意味着通路者從視窗切換了出來,但他們仍然可以觀察到它。視窗處在背景中,但可能仍然是可見的。

總結

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

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

可以通過 open(url, name, params) 調用打開一個彈窗。它會傳回對新打開的視窗的引用。

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

預設情況下,浏覽器會打開一個新标簽頁,但如果提供了視窗大小,那麼浏覽器将打開一個彈窗。

彈窗可以使用 window.opener 屬性通路 opener 視窗(譯注:即打開彈窗的視窗)。

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

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