Javascript 彈窗和 window 的方法

2023-02-17 10:56 更新

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

基本上,你只需要運(yùn)行:

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

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

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

并且,彈窗在移動(dòng)設(shè)備上非常棘手,因?yàn)橐苿?dòng)設(shè)備無(wú)法同時(shí)顯示多個(gè)窗口。

但仍然有一些任務(wù)在使用彈窗,例如進(jìn)行 OAuth 授權(quán)(使用 Google/Facebook/… 登陸),因?yàn)椋?/p>

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

阻止彈窗

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

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

例如:

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

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

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

如果彈窗是從 onclick 打開的,但是在 setTimeout 之后,該怎么辦?這有點(diǎn)棘手。

試試運(yùn)行一下這段代碼:

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

這個(gè)彈窗在 Chrome 中會(huì)被打開,但是在 Firefox 中會(huì)被阻止。

……如果我們減少延遲,則彈窗在 Firefox 中也會(huì)被打開:

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

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

window.open

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

?url ?

要在新窗口中加載的 URL。

?name ?

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

?params ?

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

params 的設(shè)置項(xiàng):

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

還有一些不太受支持的特定于瀏覽器的功能,通常不使用。通常不使用這些功能。更多示例請(qǐng)見 MDN 中的 window.open。

示例:一個(gè)最簡(jiǎn)窗口

讓我們打開一個(gè)包含最小功能集的新窗口,來(lái)看看哪些功能是瀏覽器允許禁用的:

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ù)“窗口功能”都被禁用了,并且窗口位于屏幕外。運(yùn)行它,看看會(huì)發(fā)生什么。大多數(shù)瀏覽器都會(huì)“修復(fù)”奇怪的東西,例如 width/height 為零以及脫離屏幕(offscreen)的 left/top 設(shè)置。例如,Chrome 打開了一個(gè)全 width/height 的窗口,使其占滿整個(gè)屏幕。

讓我們添加正常的定位選項(xiàng)和合理的 widthheight、left 和 top 坐標(biāo):

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ù)瀏覽器會(huì)根據(jù)要求顯示上面的示例。

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

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

從窗口訪問(wèn)彈窗

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

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

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

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

這里,我們?cè)谄浼虞d完成后,修改其中的內(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);
};

請(qǐng)注意:在剛剛進(jìn)行了 window.open 的時(shí)候,新窗口還沒有加載完成。我們可以通過(guò) (*) 行中的 alert 證實(shí)這一點(diǎn)。因此,我們需要等待 onload 以對(duì)新窗口進(jìn)行更改。我們也可以對(duì) newWin.document 使用 DOMContentLoaded 處理程序。

同源策略

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

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

從彈窗訪問(wèn)窗口

彈窗也可以使用 window.opener 來(lái)訪問(wèn) opener 窗口。除了彈窗之外,對(duì)其他所有窗口來(lái)說(shuō),window.opener 均為 null。

如果你運(yùn)行下面這段代碼,它將用 “Test” 替換 opener(也就是當(dāng)前的)窗口的內(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)閉一個(gè)窗口:win.close()。

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

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

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

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

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

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

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

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

?win.moveBy(x,y) ?

將窗口相對(duì)于當(dāng)前位置向右移動(dòng) x 像素,并向下移動(dòng) y 像素。允許負(fù)值(向上/向左移動(dòng))。

?win.moveTo(x,y) ?

將窗口移動(dòng)到屏幕上的坐標(biāo) (x,y) 處。

?win.resizeBy(width,height) ?

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

?win.resizeTo(width,height) ?

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

還有 window.onresize 事件。

僅對(duì)于彈窗

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

沒有最小化/最大化

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

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

滾動(dòng)窗口

我們已經(jīng)在 Window 大小和滾動(dòng) 一章中討論過(guò)了滾動(dòng)窗口。

?win.scrollBy(x,y) ?

相對(duì)于當(dāng)前位置,將窗口向右滾動(dòng) ?x? 像素,并向下滾動(dòng) ?y? 像素。允許負(fù)值。

?win.scrollTo(x,y) ?

將窗口滾動(dòng)到給定坐標(biāo) ?(x,y)?。

?elem.scrollIntoView(top = true) ?

滾動(dòng)窗口,使 ?elem? 顯示在 ?elem.scrollIntoView(false)? 的頂部(默認(rèn))或底部。

這里也有 window.onscroll 事件。

彈窗的聚焦/失焦

從理論上講,使用 window.focus() 和 window.blur() 方法可以使窗口獲得或失去焦點(diǎn)。此外,這里還有 focus/blur 事件,可以捕獲到訪問(wèn)者聚焦到一個(gè)窗口和切換到其他地方的時(shí)刻。

盡管,在實(shí)際中它們被進(jìn)行了嚴(yán)格地限制,因?yàn)樵谶^(guò)去,惡意網(wǎng)站濫用這些方法。

例如,看下面這段代碼:

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

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

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

例如,移動(dòng)端瀏覽器通常會(huì)完全忽略 window.focus()。并且,當(dāng)彈窗是在單獨(dú)的選項(xiàng)卡而不是新窗口中打開時(shí),也無(wú)法進(jìn)行聚焦。

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

例如:

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

總結(jié)

彈窗很少使用,因?yàn)橛衅渌x擇:在頁(yè)面內(nèi)或在 iframe 中加載和顯示信息。

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

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

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

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


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)