Javascript 彈窗和 window 的方法

2023-02-17 10:56 更新

彈窗(popup)是向用戶顯示其他文檔的最古老的方法之一。

基本上,你只需要運行:

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

……它將打開一個具有給定 URL 的新窗口。大多數(shù)現(xiàn)代瀏覽器都配置為在新選項卡中打開 url,而不是單獨的窗口。

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

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

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

  1. 彈窗是一個獨立的窗口,具有自己的獨立 JavaScript 環(huán)境。因此,使用彈窗打開一個不信任的第三方網(wǎng)站是安全的。
  2. 打開彈窗非常容易。
  3. 彈窗可以導航(修改 URL),并將消息發(fā)送到 opener 窗口(譯注:即打開彈窗的窗口)。

阻止彈窗

在過去,很多惡意網(wǎng)站經(jīng)常濫用彈窗。一個不好的頁面可能會打開大量帶有廣告的彈窗。因此,現(xiàn)在大多數(shù)瀏覽器都會通過阻止彈窗來保護用戶。

如果彈窗是在用戶觸發(fā)的事件處理程序(如 onclick)之外調(diào)用的,大多數(shù)瀏覽器都會阻止此類彈窗。

例如:

// 彈窗被阻止
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);

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

window.open

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

?url ?

要在新窗口中加載的 URL。

?name ?

新窗口的名稱。每個窗口都有一個 ?window.name?,在這里我們可以指定哪個窗口用于彈窗。如果已經(jīng)有一個這樣名字的窗口 —— 將在該窗口打開給定的 URL,否則會打開一個新窗口。

?params ?

新窗口的配置字符串。它包括設置,用逗號分隔。參數(shù)之間不能有空格,例如:?width=200,height=100?。

params 的設置項:

  • 位置:
    • ?left/top?(數(shù)字)—— 屏幕上窗口的左上角的坐標。這有一個限制:不能將新窗口置于屏幕外(offscreen)。
    • ?width/height?(數(shù)字)—— 新窗口的寬度和高度。寬度/高度的最小值是有限制的,因此不可能創(chuàng)建一個不可見的窗口。
  • 窗口功能:
    • ?menubar?(yes/no)—— 顯示或隱藏新窗口的瀏覽器菜單。
    • ?toolbar?(yes/no)—— 顯示或隱藏新窗口的瀏覽器導航欄(后退,前進,重新加載等)。
    • ?location?(yes/no)—— 顯示或隱藏新窗口的 URL 字段。Firefox 和 IE 瀏覽器不允許默認隱藏它。
    • ?status?(yes/no)—— 顯示或隱藏狀態(tài)欄。同樣,大多數(shù)瀏覽器都強制顯示它。
    • ?resizable?(yes/no)—— 允許禁用新窗口大小調(diào)整。不建議使用。
    • ?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);

在這里,大多數(shù)“窗口功能”都被禁用了,并且窗口位于屏幕外。運行它,看看會發(fā)生什么。大多數(shù)瀏覽器都會“修復”奇怪的東西,例如 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);

大多數(shù)瀏覽器會根據(jù)要求顯示上面的示例。

設置中的省略規(guī)則:

  • 如果 ?open? 調(diào)用中沒有第三個參數(shù),或者它是空的,則使用默認的窗口參數(shù)。
  • 如果這里有一個參數(shù)字符串,但是某些 ?yes/no? 功能被省略了,那么被省略的功能則被默認值為 ?no?。因此,如果你指定參數(shù),請確保將所有必需的功能明確設置為 ?yes?。
  • 如果參數(shù)中沒有 ?left/top?,那么瀏覽器會嘗試在最后打開的窗口附近打開一個新窗口。
  • 如果沒有 ?width/height?,那么新窗口的大小將與上次打開的窗口大小相同。

從窗口訪問彈窗

open 調(diào)用會返回對新窗口的引用。它可以用來操縱彈窗的屬性,更改位置,甚至更多操作。

在下面這個示例中,我們從 JavaScript 中生成彈窗:

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

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

這里,我們在其加載完成后,修改其中的內(nèi)容:

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

alert(newWindow.location.href); // (*) about:blank,加載尚未開始

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 處理程序。

同源策略

只有在窗口是同源的時,窗口才能自由訪問彼此的內(nèi)容(相同的協(xié)議://domain:port)。

否則,例如,如果主窗口來自于 site.com,彈窗來自于 gmail.com,則處于安全性考慮,這兩個窗口不能訪問彼此的內(nèi)容。有關(guān)詳細信息,請參見 跨窗口通信 一章。

從彈窗訪問窗口

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

如果你運行下面這段代碼,它將用 “Test” 替換 opener(也就是當前的)窗口的內(nèi)容:

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

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

所以,窗口之間的連接是雙向的:主窗口和彈窗之間相互引用。

關(guān)閉彈窗

關(guān)閉一個窗口:win.close()。

檢查一個窗口是否被關(guān)閉:win.closed。

從技術(shù)上講,close() 方法可用于任何 window,但是如果 window 不是通過 window.open() 創(chuàng)建的,那么大多數(shù)瀏覽器都會忽略 window.close()。因此,close() 只對彈窗起作用。

如果窗口被關(guān)閉了,那么 closed 屬性則為 true。這對于檢查彈窗(或主窗口)是否仍處于打開狀態(tài)很有用。用戶可以隨時關(guān)閉它,我們的代碼應該考慮到這種可能性。

這段代碼加載并關(guān)閉了窗口:

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

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

移動和調(diào)整大小

有一些方法可以移動一個窗口,或者調(diào)整一個窗口的大?。?/p>

?win.moveBy(x,y) ?

將窗口相對于當前位置向右移動 x 像素,并向下移動 y 像素。允許負值(向上/向左移動)。

?win.moveTo(x,y) ?

將窗口移動到屏幕上的坐標 (x,y) 處。

?win.resizeBy(width,height) ?

根據(jù)給定的相對于當前大小的 width/height 調(diào)整窗口大小。允許負值。

?win.resizeTo(width,height) ?

將窗口調(diào)整為給定的大小。

還有 window.onresize 事件。

僅對于彈窗

為了防止濫用,瀏覽器通常會阻止這些方法。它們僅在我們打開的,沒有其他選項卡的彈窗中能夠可靠地工作。

沒有最小化/最大化

JavaScript 無法最小化或者最大化一個窗口。這些操作系統(tǒng)級別的功能對于前端開發(fā)者而言是隱藏的。

移動或者調(diào)整大小的方法不適用于最小化/最大化的窗口。

滾動窗口

我們已經(jīng)在 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 事件,可以捕獲到訪問者聚焦到一個窗口和切換到其他地方的時刻。

盡管,在實際中它們被進行了嚴格地限制,因為在過去,惡意網(wǎng)站濫用這些方法。

例如,看下面這段代碼:

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

當用戶嘗試從窗口切換出去(window.onblur)時,這段代碼又讓窗口重新獲得了焦點。目的是將用戶“鎖定”在 window 中。

因此,瀏覽器必須引入很多限制,以禁用此類代碼并保護用戶免受廣告和惡意頁面的侵害。具體則取決于瀏覽器。

例如,移動端瀏覽器通常會完全忽略 window.focus()。并且,當彈窗是在單獨的選項卡而不是新窗口中打開時,也無法進行聚焦。

盡管如此,在某些情況下,此類調(diào)用確實有效且很有用。

例如:

  • 當我們打開一個彈窗時,在它上面執(zhí)行 ?newWindow.focus()? 是個好主意。以防萬一,對于某些操作系統(tǒng)/瀏覽器組合(combination),它可以確保用戶現(xiàn)在位于新窗口中。
  • 如果我們想要跟蹤訪問者何時在實際使用我們的 Web 應用程序,我們可以跟蹤 ?window.onfocus/onblur?。這使我們可以暫停/恢復頁面活動和動畫等。但是請注意,?blur? 事件意味著訪問者從窗口切換了出來,但他們?nèi)匀豢梢杂^察到它。窗口處在背景中,但可能仍然是可見的。

總結(jié)

彈窗很少使用,因為有其他選擇:在頁面內(nèi)或在 iframe 中加載和顯示信息。

如果我們要打開一個彈窗,將其告知用戶是一個好的實踐。在鏈接或按鈕附近的“打開窗口”圖標可以讓用戶免受焦點轉(zhuǎn)移的困擾,并使用戶知道點擊它會彈出一個新窗口。

  • 可以通過 ?open(url, name, params)? 調(diào)用打開一個彈窗。它會返回對新打開的窗口的引用。
  • 瀏覽器會阻止來自用戶行為之外的代碼中的 ?open? 調(diào)用。通常會顯示一條通知,以便用戶可以允許它們。
  • 默認情況下,瀏覽器會打開一個新標簽頁,但如果提供了窗口大小,那么瀏覽器將打開一個彈窗。
  • 彈窗可以使用 ?window.opener? 屬性訪問 opener 窗口(譯注:即打開彈窗的窗口)。
  • 如果主窗口和彈窗同源,那么它們可以彼此自由地讀取和修改。否則,它們可以更改彼此的地址(location),交換消息。

要關(guān)閉彈窗:使用 close() 調(diào)用。用戶也可以關(guān)閉彈窗(就像任何其他窗口一樣)。關(guān)閉之后,window.closed 為 true。

  • ?focus()? 和 ?blur()? 方法允許聚焦/失焦于窗口。但它們并不是一直都有效。
  • ?focus?和 ?blur? 事件允許跟蹤窗口的切換。但是請注意,在 ?blur? 之后,即使窗口在背景狀態(tài)下,窗口仍有可能是可見的。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號