Wyobraź sobie, że dostałeś zlecenie serwisowe dla pewnej bardzo starej aplikacji webowej. Aplikacja ma jedno okno główne i okna pop-up, w których prezentowane są poufne informacje (np. dane płacowe). Klient chciałby by wszystkie pop-upy zostały automatycznie zamknięte gdy użytkownik opuści główne okno aplikacji lub naciśnie w tym oknie przycisk „wyloguj”…
Więc… jak zamknąć wszystkie okna otworzone za pomocą metody window.open?
W sieci pytanie to pada bardzo często. Niestety najczęściej podawane jest naiwne rozwiązanie problemu, które polega na zapamiętaniu referencji do otwartych okien i późniejsze wywołanie metody close na pop-upach:
var popups = [];
function openPopup() {
var wnd = window.open('Home/Popup', 'popup' + popups.length, 'height=300,width=300');
popups.push(wnd);
}
function closePopups() {
for (var i = 0; i < popups.length; i++) {
popups[i].close();
}
popups = [];
}
W praktyce to nie zadziała bo przecież tablica z referencjami zostaje wyczyszczona przy pełnym przeładowaniu strony (np. po kliknięciu w link albo przy postbacku)…
Inne sugerowane rozwiązanie to nadanie pop-upowi unikalnej nazwy (za pomocą drugiego parametru metody open) i późniejsze pozyskanie referencji do okna:
var wnd = window.open('', 'popup0');
wnd.close();
Chodzi o skorzystanie z faktu, że metoda window.open działa na 2 sposoby:
- Jeśli okno o zadanej nazwie nie istnieje, zostanie utworzone.
- Jeśli okno o zadanej nazwie istnieje, nie zostanie ponownie utworzone, zamiast tego zostanie zwrócona referencja do niego (jeśli zostanie podany niepusty URL zawartość okna zostanie dodatkowo przeładowana).
Problem leży w punkcie nr 1. Jeśli okno pop-up o danej nazwie nie było wcześniej otwarte, zawołanie open a potem close, spowoduje, że przez krótką chwilę pop-up będzie widoczny. Lipa…
A może da się przechować referencję do pop-upa między przeładowaniami strony?
Jeśli nie istnieje konieczność obsługi starych przeglądarek (mało prawdopodobne dla starej aplikacji) można by spróbować zapisać wskazanie na pop-up do localStorage. To jednak się nie uda:
var popup = window.open('http://morzel.net', 'test');
localStorage.setItem('key', JSON.stringify(popup));
TypeError: Converting circular structure to JSON
Stare tricki z utrzymaniem stanu strony oparte o cookies lub window.name też nie zadziałają.
Wiec… co z tym zrobić?
Nawet jeśli nie możesz sobie pozwolić na dużą zmianę jaką jest wprowadzenie ramek nie poddawaj się :)
Okna popup mają właściwość opener, która wskazuje na okno nadrzędne (tzn. to w którym znajdowało się wywołanie window.open). Okna pop-up mogą więc cyklicznie sprawdzać czy otwierające je okno główne jest nadal otwarte. Mogą też odwoływać się do zmiennych w oknie nadrzędnym. Fakt ten można wykorzystać do wymuszenia zamknięcia okien pop-up gdy użytkownik naciśnie przycisk „wyloguj” w oknie głównym. Gdy użytkownik jest zalogowany (i tylko wtedy!), należy dodać do obiektu window okna głównego znacznik zalogowania w postaci zmiennej (np. loggedIn).
Oto kod JS, który powinien zostać umieszczony na stronie wyświetlanej w pup-upie:
window.setInterval(function () {
try {
if (!window.opener || window.opener.closed === true || window.opener.loggedIn !== true) {
window.close();
}
} catch (ex) {
window.close(); // FF może rzucić wyjątek bezpieczeństwa przy próbie odwołania do loggedIn (dla zewnętrznego serwisu)
}
}, 1000);
Sprawdzanie zmiennej z okna opener ma jeszcze jedną zaletę. Jeśli użytkownik w oknie głównym przejdzie do innej aplikacji (np. poprzez przycisk wstecz lub link do zewnętrznego serwisu) wówczas okna pop-up wykryją brak monitorowanej właściwości window.opener i zamkną się automatycznie.
Cóż, nie jest to kod który lubisz pisać ale osiąga pożądany efekt nawet pomimo bolesnych braków w API przeglądarek. Gdyby tylko zrobili metodę window.exists('name')…