Ohleduplné otevírání nových oken s JavaScriptem

20. dubna 2005

V některých případech při otevírání odkazu do nového okna je pro uživatele pohodlnější, aby nevyskakovala další okna, pokud mu již jedno „naše okno“ vyskočilo – to je jednoduché, nicméně takové okno se může skrýt v pozadí hlavního okna a uživatel pak nezaregistruje, že se jeho obsah změnil. V článku si nejen ukážeme, jak namísto otevření dalšího okna jen přenést již otevřené okno do popředí, ale také jak se sžít s v současnosti hojně využívanými blokátory vyskakovacích oken.

Tento článek volně navazuje na na předchozí články o chytřejším a ohleduplnějším otvírání nových oken a reaguje i na změny, které přinesl Service Pack 2 pro Windows XP a masové rozšíření různých toolbarů umožňujících automatické blokování vyskakovacích oken.

Ačkoli si leckdo řekne, že nastavit odkazu atribut target a do dokumentu v otevíraném okně nastavit na událost onload vykonání metody focus() je triviální (a má svým způsobem pravdu), v naší ukázce vyřešíme více problémů najednou:

  • Atribut target ve vyšších verzích XHTML není podporován a navíc jeho použití pro otevření nového okna kompletně znemožní uživateli přejít na odkaz v případě, že má aktivní blokování otevírání nových oken. Jako náhradu proto použijeme nastavení vlastnosti target v DOM pomocí klientského skriptování, a to pouze tehdy, pokud je toto opravdu potřeba.
  • Abychom mohli ovládat otvírané okno, musíme jej otevřít skriptem prostřednictvím window.open() – to je vpořádku, musíme ovšem zajistit i funkčnost bez podpory skriptování a v případě skriptování ošetřit stav, kdy má klient „vyskakování“ oken blokováno.
  • Pokud jsme okno otevřeli pomocí window.open() a zachovali si v proměnné jeho handler, můžeme pak uplatnit metodu focus() – je však třeba se ujistit, že okno nebylo zavřeno dotazem na vlastnost window.closed.
  • Ne vždy máme možnost zasahovat do dokumentu otvíraného do nového okna, přidání onload="window.focus();" není vždy možné a často ani vhodné, pokud je dokument nějak interaktivní.
  • V některých aplikacích se nám uživatel v novém okně přihlašuje do nějaké doplňkové aplikace – může jít například o různé nastavování služeb, administrační rozhraní a podobně. Pokud budeme schopní již otevřené okno s přihlášeným uživatelem pouze přenést do popředí, uživatel o své přihlášení nepřijde. Pokud bychom na další kliknutí uživatele v hlavním okně otevřeli nové okno, uživatel by o přihlášení přišel (pokud nepoužíváme cookies), což je nepohodlné.

Výsledkem je krátký skript, který umísťujeme do hlavní stránky. Stránka (dokument) v otevíraném okně nás vůbec nemusí zajímat, může být i z úplně cizího serveru a přesto se korektně přenese do popředí, jakmile klikneme na odkaz v mateřské stránce. Prohlédněte si ukázku (zdrojový kód) odkazující také na zadání nového příspěvku do diskuse našeho Interfóra a na editaci profilu. Vyzkoušejte přitom chování, když je nové okno otevřeno a je v pozadí, kdy nové okno zavřete a kdy hlavní stránku ukázky obnovíte.

Ukázky do fóra a do knihkupectví jsou voleny záměrně, protože zde se ukáže, že uživatel při vhodně navržené aplikaci nepřijde o přihlášení nebo o obsah košíku. Pozorujte také chování při aktivním blokování vyskakovacích oken – například v Microsoft Internet Exploreru na Windows XP sice problikne ikonka blokovaného okna, avšak místo nehezkého chybového hlášení o zablokovaném novém okně se odkazovaná stránka zobrazí v hlavním okně.

Nejprve si ukažme odkaz pro otevření „chytrého“ nového okna:

<a href=“http://interforum.interval.cz/AddPost.aspx?ForumID=38″ onclick=“return !OpenMyWin(this,’_okno‘);“>Nový příspěvek</a>

Nové okno je otvíráno funkcí OpenMyWin(). Funkce vrací hodnotu „true“, pokud se nové okno podařilo otevřít nebo je již otevřené okno úspěšně „ovládáno“. Negovanou hodnotu necháváme klíčovým slovem return probublat až na úroveň odkazu – pokud se tedy nové okno nepodaří otevřít (klient má například blokované otvírání nových oken), odkaz zafunguje klasickým způsobem, takže náš doplněný skript nezpůsobí v našem webu bariéru.

Příklad stránky s funkcí, která jen zamezí zavření okna a upozorní uživatele (žádné přidané testování, zda zobrazit či nezobrazit varování, zde není):

var myTWin = window.myTWin;
function OpenMyWin(link,winName)
{
  var retValue=true;
  if (myTWin!=null && !myTWin.closed)
  {
    myTWin.focus();
    myTWin.location.href=link.href;
  }
  else
  {
    myTWin=window.open(link.href,winName);
    if (myTWin==null || typeof(myTWin)==“undefined“)
      retValue=false;
    else
    {
      link.target=winName;
      myTWin.focus();
    }
  }
  return retValue;
}

Ve skriptu si deklarujeme proměnnou myTWin – v té budeme držet objekt otevřeného okna po tom, co nové okno otevřeme.

Ve funkci OpenMyWin()si připravíme návratovou hodnotu – předpokládáme, že se vše zdaří, inicilizujeme ji tedy s hodnou true. Dále otestujeme, zda proměnná myTWin, odkazující na objekt okna, není „null“. Pokud není (okno již dříve bylo otevřeno), otestujeme, jestli okno nebylo mezitím zavřeno – má vlastnost closed. Pokud je okno otevřeno, je k dispozici, pak ho přesuneme do popředí metodou focus() a jako novou URL nastavíme location.href okna na „href“ předaného odkazu „link“.

Pokud okno není otevřeno (nebylo otevřeno nebo bylo již zavřeno), pak prostě otevřeme nové okno metodou window.open(). Výsledek otevření je předán do proměnné myTWin – otestujeme, zda není null nebo odkaz na objekt není typu undefined. Pokud je odkaz null nebo undefined, je zřejmé, že se nové okno nezdařilo otevřít – klient má pravděpdobně aktivní blokátor vyskakovacích oken (jako například Google/Yahoo toolbar, rozšíření MSIE6 ve Windows XP SP2 nebo zákaz otvírání nových oken v prohlížečích FireFox, Opera a dalších). V tomto případě s novým oknem nelze pracovat, pouze nastavíme návratovou hodnotu na false.

Pokud je odkaz na objekt okna korektní, můžeme s oknem pracovat. Pro jistotu můžeme nastavit již zmiňovaný target, použijeme stejnou hodnotu jako pro název okna. Dále přeneseme okno do popředí metodou focus(). Toto přenesení je důležité – může se totiž stát, že mateřské okno někdo obnoví nebo v něm přejde na jinou stránku a tím ztratí obsah proměnné myTWin. Okno s názvem „_MyWin“ může ale být stále otevřené na pozadí, takže odkaz by se zobrazil v něm a uživatel mající hlavní okno v popředí by toto nemusel zaregistrovat.

Jako návratovou hodnotu funkce tedy nakonec předáme hodnotu udávající, zda se nové okno zdařilo otevřít. Tato návratová hodnota, jak již bylo uvedeno, slouží k potlačení nebo naopak provedení původní funkce odkazu. Můžeme si tak být jisti, že se klient na stránku odkazovanou do nového okna dostane alternativně v témže okně, pokud má vyskakovací okna blokovaná.

Ještě důkladnější řešení by předpokládalo zamezit ztrátě obsahu myTWin – to by bylo možné umístěním skriptu do nadřízeného rámce (s ohledem na přístupnost velmi nevhodné), nebo do nějakého třetího okna, které by na procházení stránek v hlavním okně nebylo závislé.

V části kódu funkce, kde zjistíme, že je okno otevřené, a nastavujeme novou adresu okna, by bylo možné otestovat, zda se aktuální adresa okna rovná té, kterou chceme nastavit. Pokud by adresy byly rovny, nastavení location.href bychom neprovedli – pouze bychom přenesli okno do popředí. Volba postupu závisí na aplikaci – v některých aplikacích je toto „nenastavení adresy“ nežádoucí (uživatel mohl v onom okně nejméně jednou odeslat nějaký formulář metodou POST – adresa je tedy stejná, ale obsah stránky ne), v jiných aplikacích je velmi vhodné, protože tak uživateli například nezmizí obsah rozepsaného příspěvku ve formuláři. Naopak do otvíraného okna můžeme doplnit upozornění před zavřením okna.

Jak vidíte, skript neobsahuje žádné konfigurovatelné hodnoty – tak, jak leží a běží, jej ihned můžete zaintegrovat do libovolné aplikace a odkazů v ní použitých, jen si je potřeba u každého odkazu promyslet název otvíraného okna. Odkazy se stejným názvem okna budou otvírány do téhož okna.

Na závěr si zapamatujme, že ohleduplné chování nového okna můžeme dnes zajistit pouze klientským skriptem – nepoužívejme proto k otvírání nového okna v žádném případě atribut target, a to ani tehdy, pokud Doctype našeho dokumentu tento atribut podporuje.

Pozn. red.: Tento článek vyšel poprvé 23. 7. 2004. Původní verze článku a k němu vedené diskuse jsou vám k dispozici v ZIP archivech.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *