Unikaj blokowania wyskakujących okienek w przeglądarce

172

Opracowuję przepływ uwierzytelniania OAuth wyłącznie w JavaScript i chcę pokazać użytkownikowi okno „udziel dostępu” w wyskakującym okienku, ale zostaje ono zablokowane.

Jak mogę zapobiec wyskakującym okienkom utworzonym przez blokowanie wyskakujących okienek w różnych przeglądarkach window.openlub window.showModalDialogprzed ich blokowaniem?

Pablo Fernandez
źródło
11
Nawet gdyby było to możliwe (nie wiem), jeśli ludzie używają programów do blokowania wyskakujących okienek, należy to uszanować. Większość przeglądarek wyświetla komunikat, gdy witryna próbuje otworzyć wyskakujące okienko, aby nadal mogli je zobaczyć, jeśli chcą. Możesz umieścić w witrynie uwagę, że niektóre treści otwierają się w wyskakującym okienku, a użytkownik powinien na to zezwolić, aby kontynuować.
Felix Kling
5
Najlepsza praktyka wyglądałaby następująco: 1) Zrób to z powodzeniem 2) Zamknij okna i zarygluj drzwi i kul się ze strachu przed gromadzącym się tłumem zdenerwowanych użytkowników sieci 3) Pokutuj, usuń pop-up-busta-busta i szanuj twoją publiczność.
Alex Mcp,
Alex i Felix, zaktualizowałem pytanie. Nie wykorzystam wiedzy do zła :). Dzięki!
Pablo Fernandez
9
Chciałbym dodać, że omijanie programu blokującego wyskakujące okienka może w rzeczywistości próbować poprawić komfort użytkowania. W przykładzie, nad którym teraz pracuję, używamy aplikacji Javascript (opartej na ExtJS) i próbujemy pozwolić użytkownikom płacić za pomocą systemu PayPal. Dajemy im przycisk, który mogą kliknąć, aby uruchomić paypal w nowym oknie, ale niektóre wersje IE blokują go jako wyskakujące okienko (nawet jeśli jest to kliknięcie przycisku). Jeśli teraz włączą wyskakujące okienko, ekran ładuje się ponownie i jako aplikacja JavaScript tracimy stan okna i muszą zacząć od nowa. A więc tak naprawdę: problem w tym, że IE jest głupi.
NateDSaint
1
@FelixKling Tak, to możliwe. Twórcy przeglądarek już o tym pomyśleli. Otwarcie wyskakującego okienka jest w porządku, o ile istnieje zamiar użytkownika (sygnalizowany przez kliknięcie łącza lub przycisku). Programy blokujące wyskakujące okienka powinny szanować intencje użytkownika. Jeśli program blokujący wyskakujące okienka w IE nie działa, to właśnie ten program blokuje wyskakujące okienka. Użytkownicy używają blokad wyskakujących okienek, aby uniemożliwić skryptom otwieranie wyskakujących okienek w dowolnym momencie, a nie do blokowania wyskakujących okienek, które sami próbowali otworzyć (klikając przycisk lub łącze).
Stijn de Witt

Odpowiedzi:

286

Ogólna zasada jest taka, że ​​blokowanie wyskakujących okienek włącza się, jeśli window.openlub podobnie jest wywoływane z javascript, który nie jest wywoływany przez bezpośrednie działanie użytkownika . Oznacza to, że możesz zadzwonić window.openw odpowiedzi na kliknięcie przycisku bez uderzenia przez blokadę wyskakujących okienek, ale jeśli umieścisz ten sam kod w zdarzeniu licznika czasu, zostanie on zablokowany. Głębokość łańcucha połączeń również ma znaczenie - niektóre starsze przeglądarki patrzą tylko na bezpośredniego dzwoniącego, nowsze przeglądarki mogą trochę cofnąć się, aby sprawdzić, czy dzwoniącym było kliknięcie myszą itp. Utrzymuj to tak płytko, jak to tylko możliwe, aby uniknąć blokowania wyskakujących okienek.

dthorpe
źródło
2
Co ciekawe, wyskakujące okienka zainicjowane przez zdarzenie zmiany przypisane do wybranego elementu zostaną zablokowane (w przeglądarce Chrome, a nie FF), mimo że zdarzenie to jest inicjowane przez bezpośrednią akcję użytkownika, na przykład kliknięcie. Chociaż jeśli są powiązane z danymi wejściowymi, są dozwolone. Dziwne.
ccnokes
6
Nikt nie powiedział, że przeglądarka jest spójna. : P
dthorpe
8
Dzięki eksperymentom udało mi się zrozumieć, że głębokość stosu nie ma nic wspólnego z blokowaniem wyskakujących okienek. W rzeczywistości sprawdza, czy window.open jest wywoływane w ciągu 1 sekundy po akcji użytkownika, czy nie. Testowane w Chrome 46 i Firefox 42.
Mesqalito
1-sekundowy limit czasu można obejść w obu przeglądarkach, pozwalając na nieokreślony czas, używając odpowiedzi @ tj-crowder na tej stronie .. mieć adres URL, który wywołujesz przez Ajax, powinien być jakimś php (lub czymkolwiek), który używa uśpienia ( ) przez pewien czas, zanim zwróci odpowiedź. Przeglądarka zawiesza się podczas „ładowania” strony, a następnie uruchamia wyskakujące okienko po otrzymaniu odpowiedzi. Jednak w Chrome musisz dodać alert () tuż przed ajax (), aby ta sztuczka zadziałała.
fanfara
184

Na podstawie Jason Sebring „s bardzo przydatnych końcówkę , a na rzeczy pokryte tu i tam znalazłem idealne rozwiązanie dla mojego przypadku:

Pseudokod z fragmentami kodu JavaScript:

  1. natychmiast utwórz puste okienko po akcji użytkownika

    var importantStuff = window.open('', '_blank');

    Opcjonalnie: dodaj „oczekującą” wiadomość informacyjną. Przykłady:

    a) Zewnętrzna strona HTML: zamień powyższy wiersz na

    var importantStuff = window.open('http://example.com/waiting.html', '_blank');

    b) Tekst: dodaj następujący wiersz poniżej powyższego:

    importantStuff.document.write('Loading preview...');
  2. wypełnij go treścią, gdy jest gotowy (na przykład, gdy zostanie zwrócone połączenie AJAX)

    importantStuff.location.href = 'http://shrib.com';

Wzbogać połączenie window.openz dowolnymi dodatkowymi opcjami, których potrzebujesz.

Właściwie używam tego rozwiązania do przekierowania mailto i działa na wszystkich moich przeglądarkach (Windows 7, Android). _blankNieco pomaga na mailto przekierowanie do pracy na urządzeniach mobilnych, btw.

Twoje doświadczenie? Jakiś sposób, aby to poprawić?

Mister szwajcarski
źródło
1
? więc co zrobić, jeśli nie jest to spowodowane działaniem użytkownika w przeglądarce? Na przykład po uwierzytelnieniu użytkownika na serwerze musi otworzyć nową kartę
Don Cheadle
@mmcrae nie rób tego. Cokolwiek zamierzasz zrobić, istnieje lepszy sposób niż wyskakujące okienko. W przeciwnym razie prawdopodobnie nigdy nie chciałbym oglądać Twojej witryny. Dzisiejsze przeglądarki zapewniają, że nie powinieneś być w stanie tego zrobić - zobacz odpowiedź @dthorpe .
Swiss Mister
5
Whatever it is you intend to do, there is a better way than a popup windowlol? Dziwne uogólnienie. Klient chciałby, aby strona, na której się uwierzytelnił, pozostała otwarta, a nowa witryna / strona docelowa po uwierzytelnieniu otwierała się w nowej karcie. Tak, nie mój pomysł na doskonałe wrażenia użytkownika ... ale wydaje się rozsądny
Don Cheadle
@mmcrae. Usiądź i pomyśl poważnie. Obiecuję, że w opisywanym scenariuszu znajdziesz „akcję użytkownika”. Cała moja odpowiedź polega na tym, że tworzysz wyskakujące okienko, gdy masz akcję użytkownika - a następnie wypełnia je treścią później. Wskazówka dla twojego przypadku: użytkownik uderza „uwierzytelnij” -> utwórz wyskakujące okienko (puste); licznik czasu się skończył lub wróciła odpowiedź serwera lub cokolwiek innego -> wypełnij zawartość wyskakującego okienka
Swiss Mister
3
Przydatna wskazówka, jeśli żądanie AJAX nie powiedzie się, zadzwoń, importantStuff.closeaby zamknąć nową kartę i podać i zaalarmować na oryginalnej stronie.
Goose
24

Dodatkowo Swiss Mister post, w moim przypadku window.open został uruchomiony wewnątrz obietnicy, która włączyła blokadę wyskakujących okienek, moje rozwiązanie brzmiało: w kątowym:

$scope.gotClick = function(){

  var myNewTab = browserService.openNewTab();
  someService.getUrl().then(
    function(res){
        browserService.updateTabLocation(res.url, myNewTab);

    }
  );
};

przeglądarka internetowa:

this.openNewTab = function(){
     var newTabWindow = $window.open();
     return newTabWindow;
}

this.updateTabLocation = function(tabLocation, tab) {
     if(!tabLocation){
       tab.close();
     }
     tab.location.href = tabLocation;
}

w ten sposób możesz otworzyć nową kartę, używając odpowiedzi na obietnicę i nie wywołując funkcji blokowania wyskakujących okienek.

David
źródło
1
Doprowadziło to do mojego rozwiązania! Gdzie utworzyłem zmienną zawierającą otwartą kartę, a następnie wypełniłem adres URL po załadowaniu danych. Dzięki. const tab = window.open(); observable.subscribe(dataUrl => tab.location.href = dataUrl);
jonas
1
Jeśli masz włączoną blokadę wyskakujących okienek, nie rozwiąże to problemu z blokadą wyskakujących okienek ...
Alejandro Vales
Rozwiązanie @Alejandro Vales jonas będzie działać tylko wtedy, gdy kod zostanie uruchomiony ze zdarzenia onClick lub jakiejkolwiek interakcji użytkownika. Kiedy próbujesz otworzyć nową kartę przez windows.open wewnątrz obietnicy, limit czasu, subskrybuj ... przeglądarka myśli, że jest to manipulacja na użytkowniku, więc rozwiązaniem jest utworzenie instancji przed wprowadzeniem obietnicy, w środku po prostu się zmień href jak Jonas
David
@David Bardzo dziękuję !!! Nie zdarzyło mi się zobaczyć odpowiedzi .thenon-a i dlatego tak się pogubiłem: / SRY ...
Alejandro Vales
@David To jest świetne! Działa jak marzenie. Gdybym mógł zawracać Ci głowę jeszcze jednym pytaniem - czy wiesz, jak to działa również w Edge? Szturchnięcie w odpowiedni zasób bardzo by pomogło ...
dzenesiz
21

Jako dobrą praktykę uważam, że dobrym pomysłem jest sprawdzenie, czy wyskakujące okienko zostało zablokowane i podjęcie działań w przypadku. Musisz wiedzieć, że window.open ma wartość zwracaną i ta wartość może być pusta, jeśli akcja się nie powiedzie. Na przykład w poniższym kodzie:

function pop(url,w,h) {
    n=window.open(url,'_blank','toolbar=0,location=0,directories=0,status=1,menubar=0,titlebar=0,scrollbars=1,resizable=1,width='+w+',height='+h);
    if(n==null) {
        return true;
    }
    return false;
}

jeśli wyskakujące okienko jest zablokowane, window.open zwróci wartość null. Więc funkcja zwróci false.

Jako przykład, wyobraź sobie wywołanie tej funkcji bezpośrednio z dowolnego linku z target="_blank": jeśli wyskakujące okienko zostanie pomyślnie otwarte, powrót falsezablokuje działanie linku, w przeciwnym razie, jeśli wyskakujące okienko zostanie zablokowane, powrót truepozwoli na zachowanie domyślne (otwiera nowe _puste okno) i kontynuuje .

<a href="http://whatever.com" target="_blank" onclick='return pop("http://whatever.com",300,200);' >

W ten sposób będziesz mieć wyskakujące okienko, jeśli to działa, i _blank okno, jeśli nie.

Jeśli wyskakujące okienko się nie otworzy, możesz:

  • otwórz puste okno, jak w przykładzie i kontynuuj
  • otwórz fałszywe wyskakujące okienko (iframe wewnątrz strony)
  • poinformuj użytkownika („proszę zezwolić na wyświetlanie wyskakujących okienek w tej witrynie”)
  • otwórz puste okno, a następnie poinformuj użytkownika itp.
FrancescoMM
źródło
Dziękuję Ci. Pracowałem!
Bcktr
8

z Google oauth JavaScript API:

http://code.google.com/p/google-api-javascript-client/wiki/Authentication

Zobacz obszar, w którym brzmi:

Konfigurowanie uwierzytelniania

Implementacja OAuth 2.0 na kliencie wykorzystuje wyskakujące okienko, aby monitować użytkownika o zalogowanie się i zatwierdzenie aplikacji. Pierwsze wywołanie gapi.auth.authorize może wywołać blokowanie wyskakujących okienek, ponieważ pośrednio otwiera je. Aby zapobiec uruchamianiu się programu blokującego wyskakujące okienka przy wywołaniach autoryzacji, wywołaj gapi.auth.init (wywołanie zwrotne) podczas ładowania klienta. Dostarczone wywołanie zwrotne zostanie wykonane, gdy biblioteka będzie gotowa do wykonywania wywołań uwierzytelniania.

Domyślam się, że ma to związek z prawdziwą odpowiedzią powyżej, ponieważ wyjaśnia, czy istnieje natychmiastowa odpowiedź, nie wywoła wyskakującego alarmu. „Gapi.auth.init” sprawia, że ​​api następuje natychmiast.

Praktyczne zastosowanie

Zrobiłem mikrousługę uwierzytelniania typu open source przy użyciu paszportu węzła na npm i różnych pakietów paszportów dla każdego dostawcy. Użyłem standardowego przekierowania do strony trzeciej, podając adres URL przekierowania, do którego można wrócić. To było zautomatyzowane, więc mogłem mieć różne miejsca, do których przekierowałem z powrotem podczas logowania / rejestracji i na określonych stronach.

github.com/sebringj/athu

passportjs.org

Jason Sebring
źródło
3

Wypróbowałem wiele rozwiązań, ale jego jest jedynym, które faktycznie działało dla mnie we wszystkich przeglądarkach

let newTab = window.open(); newTab.location.href = url;

pomobc
źródło
2
To po prostu nie działa dla osób, które mają włączoną blokadę wyskakujących okienek
Alejandro Vales,
co jest url? nie jest przypisany.
shinriyo
@shinriyo url to link do dowolnej strony, do której chcesz uzyskać dostęp, na przykładhttp://example.com
pomobc
@AlejandroVales Mam włączoną funkcję blokowania wyskakujących okienek i działa. Problem polega na tym, że window.open()nie można programowo otworzyć nowego okna i jeśli zajdzie taka potrzeba, ta odpowiedź jest jedną z opcji. Za pomocą newTabzmiennej, do której się odwołujemy, window.open()a później możemy ją wywołać, wstawić urlpotrzebny, w moim przypadku adres URL jakiegoś obiektu blob, a nowa karta zostanie otwarta na podanym adresie URL.
stanimirsp
0

Nie chciałem tworzyć nowej strony, chyba że wywołanie zwrotne powróciło pomyślnie, więc zrobiłem to, aby zasymulować kliknięcie użytkownika:

function submitAndRedirect {
  apiCall.then(({ redirect }) => {
      const a = document.createElement('a');
      a.href = redirect;
      a.target = '_blank';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
  });
}
user3479425
źródło
3
nie działa dla mnie w Chrome 62. Popup (strona) zostaje zablokowany.
s.ermakovich
1
Tak, masz rację - stwierdziłem również, że jest to niespójne. Skończyło się na tym, że moje wywołanie API
stało się
-8

Najłatwiejszym sposobem na pozbycie się tego jest:

  1. Nie używaj document.open ().
  2. Zamiast tego użyj this.document.location.href = location; gdzie lokalizacja to adres URL do załadowania

Np .:

<script>
function loadUrl(location)
{
this.document.location.href = location;
}</script>

<div onclick="loadUrl('company_page.jsp')">Abc</div>

To działało bardzo dobrze dla mnie. Twoje zdrowie

Pramod Shetty
źródło
2
A co z otwarciem adresu URL w nowej karcie?
3 zasady