Pomijaj blokowanie wyskakujących okienek w oknie.open, gdy ustawiona jest funkcja JQuery event.preventDefault ()


98

Chcę wyświetlać okno dialogowe JQuery warunkowo po kliknięciu hiperłącza.

Mam wymaganie takie jak warunek1, aby otworzyć okno dialogowe JQuery, a jeśli warunek1 nie jest spełniony, przejdź do strony, do której odwołuje się tag „href”, którego zdarzenie kliknięcia dotyczy.

Jestem w stanie wywołać funkcję w przypadku kliknięcia linku. Ta funkcja sprawdza teraz wspomniany stan, wykonując inny adres URL (który wykonuje mój kontroler Spring i zwraca odpowiedź).

Wszystko działa idealnie, tylko window.open jest blokowane przez blokowanie wyskakujących okienek.

$('a[href*=/viewpage?number]').live('click', function(e) {
    e.preventDefault();
    redirectionURL = this.href;
    pageId= getUrlVars(redirectionURL)["number"];
    $.getJSON("redirect/" + pageId, {}, function(status) {
        if (status == null) {
            alert("Error in verifying the status.");
        } else if(!status) {
            $("#agreement").dialog("open");
        } else {
            window.open(redirectionURL);
        }
    });
});

Jeśli e.preventDefault();usunę z kodu, bloker popoup nie blokuje strony, jednak dla warunku1 otwiera okno dialogowe, a także otwiera stronę „href”.

Jeśli rozwiążę jedno, stwarza to problem dla innego. Nie jestem w stanie oddać sprawiedliwości obu warunkom jednocześnie.

Czy możesz mi pomóc rozwiązać ten problem?

Kiedy to zostanie rozwiązane, mam inny problem do rozwiązania, np. Nawigację po zdarzeniu OK dialogu :)


1
spróbuj nie używać .live, który jest przestarzały i wskaźnik do .on ()!
— projekty mas

@EvilP: Jak powiedzieli ci ostatnio inni, tylko w wersji 1.7 i nowszych. ZAWiele projektów nadal będzie na 1,5 lub 1,6.
— TJ Crowder

10
Downvoter: Tylko dlatego, że nie lubisz wyskakujących okienek, nie oznacza to, że to pytanie powinno zostać odrzucone. Pytanie powinno zostać odrzucone, jeśli „... nie wykazuje żadnego wysiłku badawczego; jeśli jest niejasne lub nieprzydatne” . Pytanie jest jasne, nie wydaje się leniwe i wynika z nietrywialnej kombinacji czynników.
— TJ Crowder

@TJCrowder: Nie określił, której wersji używa. Powinienem więc wziąć pod uwagę, że używa najnowszej wersji. Jeśli używa innej wersji, na pewno by o tym wspomniał, ponieważ WTEDY BYŁby świadomy faktu, że live jest przestarzałe i wyjaśniłby DLACZEGO używa .live (). Nikt nigdy nie powiedział mi „ostatniego” razu, więc pomyślałem, że powinieneś sobie odpocząć i się uspokoić. Nie ma potrzeby być tak surowym ...
— projekty mas

Nie otrzymałem żadnego powiadomienia o tym. Ale czy to nie jest normalne, że jeśli ktoś nie określa swojej wersji, to uważa, że ​​używa najnowszej? Chodzi mi o to, że nie bez powodu muszę używać 1.3 i zdaję sobie sprawę z tego, że istnieje nowsza i lepsza wersja.
— projekty mas

Odpowiedzi:


144

Blokowanie wyskakujących okienek zazwyczaj zezwala tylko window.openwtedy, gdy jest używane podczas przetwarzania zdarzenia użytkownika (np. Kliknięcia). W twoim przypadku dzwonisz window.open później , a nie podczas zdarzenia, ponieważ $.getJSONjest asynchroniczny.

Masz dwie możliwości:

  1. Zrób coś innego, zamiast window.open.

  2. Spraw, aby wywołanie AJAX było synchroniczne, czego normalnie powinieneś unikać jak plagi, ponieważ blokuje to interfejs użytkownika przeglądarki. $.getJSONjest równa:

    $.ajax({
      url: url,
      dataType: 'json',
      data: data,
      success: callback
    });

    ... więc możesz $.getJSONzsynchronizować połączenie, mapując parametry do powyższych i dodając async: false:

    $.ajax({
        url:      "redirect/" + pageId,
        async:    false,
        dataType: "json",
        data:     {},
        success:  function(status) {
            if (status == null) {
                alert("Error in verifying the status.");
            } else if(!status) {
                $("#agreement").dialog("open");
            } else {
                window.open(redirectionURL);
            }
        }
    });

    Ponownie, nie zalecam synchronicznych wywołań Ajax, jeśli możesz znaleźć inny sposób osiągnięcia swojego celu. Ale jeśli nie możesz, proszę.

    Oto przykład kodu, który nie przeszedł testu z powodu wywołania asynchronicznego:

    Przykład na żywo | Źródło na żywo (linki na żywo nie działają z powodu zmian w JSBin)

    jQuery(function($) {
      // This version doesn't work, because the window.open is
      // not during the event processing
      $("#theButton").click(function(e) {
        e.preventDefault();
        $.getJSON("http://jsbin.com/uriyip", function() {
          window.open("http://jsbin.com/ubiqev");
        });
      });
    });

    A oto przykład, który działa, używając połączenia synchronicznego:

    Przykład na żywo | Źródło na żywo (linki na żywo nie działają z powodu zmian w JSBin)

    jQuery(function($) {
      // This version does work, because the window.open is
      // during the event processing. But it uses a synchronous
      // ajax call, locking up the browser UI while the call is
      // in progress.
      $("#theButton").click(function(e) {
        e.preventDefault();
        $.ajax({
          url:      "http://jsbin.com/uriyip",
          async:    false,
          dataType: "json",
          success:  function() {
            window.open("http://jsbin.com/ubiqev");
          }
        });
      });
    });

3
Stukrotne dzięki. Twoja sugestia synchronizacji połączenia zadziałała jak urok. To samo pomogło mi w moim prawdopodobnym następnym numerze obsługi nawigacji po zdarzeniu OK w dialogu.
— mavaze

Ponieważ TJ Crowder pozostawił otwarte 2 pytania, [1. znajdź alternatywę dla window.open] i [2. unikaj połączeń synchronicznych Ajax] sugestie dotyczące tych są mile widziane dla dobra społeczności. W przeciwnym razie jego odpowiedź była dla mnie idealnym rozwiązaniem na to pytanie :)
— mavaze

4
Dla mnie dodałem link z target = "_ blank", aby zachęcić użytkownika do kliknięcia.
— Hiroshi

1
@Evan: Będziesz musiał bezpośrednio używać XHR. W bilecie znajduje się przykład wycofywania żądań synchronicznych .
— TJ Crowder

2
@mavaze Myślę, że możesz uniknąć obu problemów, jeśli możesz zmienić przepływ sterowania, aby najpierw otworzyć okno, (synchronizacja), a następnie wykonać żądanie AJax (asynchroniczne), a następnie użyć odpowiedzi, aby wyświetlić komunikat o błędzie lub wykonać przekierowanie.
— Stijn de Witt

61

możesz wywołać window.open bez blokowania przeglądarki tylko wtedy, gdy użytkownik wykona bezpośrednio jakąś akcję. Przeglądarka wysyła jakąś flagę i określa, że ​​okno zostało otwarte przez działanie użytkownika.

Możesz więc wypróbować ten scenariusz:

  1. var myWindow = window.open ('')
  2. narysuj dowolną wiadomość ładowania w tym oknie
  3. gdy żądanie zostanie wykonane, zadzwoń pod numer myWindow.location = „ http://google.com ”

2
To nie działa w Safari Mobile (testowane na iPhonie 4). Wypróbowałem kilka innych pomysłów (np. myWindow.postMessage), Ale z powodu ograniczenia Safaris dotyczącego nie wykonywania JavaScript w tle, okno nadrzędne nigdy nie może wysłać zmiany lokalizacji.
— roundrobin

@BausTheBig Testowałem na iPhonie 6, IOS8. Zadziałało jak urok.
— lvarayut

szukał sposobu na śledzenie linków zewnętrznych za pomocą Google Analytics bez blokowania wyskakujących okienek. To było dokładnie to, czego potrzebowałem. Wydaje się, że działa dobrze na iPhonie 4s w iOS7 (rzeczywisty telefon) i iOS 8 (symulator iOS).
— notacouch

37

Miałem ten problem i nie miałem gotowego adresu URL, dopóki oddzwonienie nie zwróci niektórych danych. Rozwiązaniem było otwarcie pustego okna przed uruchomieniem wywołania zwrotnego, a następnie po prostu ustawienie lokalizacji, w której wywołanie zwrotne powróci.

$scope.testCode = function () {
    var newWin = $window.open('', '_blank');
    service.testCode().then(function (data) {
        $scope.testing = true;
        newWin.location = '/Tests/' + data.url.replace(/["]/g, "");
    });
};

1
Użyłem tego rozwiązania, aby otworzyć nowe okno i ominąć blokadę
— wyskakujących

A jeśli nie wiem wcześniej, czy będę musiał otworzyć wyskakujące okienko? Tak jak w: action().then(doWindowOpenThing).catch(doSomethingElseThatDoesntOpenAPopup) Więc nie mogę otworzyć okna wcześniej (aby zachować jego odniesienie itp.), Jeśli później nie będę go używać, prawda?
— Luiz

Nie, za każdym razem otwierałoby się nową kartę i myślę, że nie chcesz tego w przypadku, gdy jej nie używasz. Chodzi o to, że przeglądarka pozwoli ci otworzyć nową kartę tylko w funkcji kliknięcia (akcja inicjowana przez użytkownika), w przeciwnym razie blokada wyskakujących okienek zobaczy to jako skrypt otwierający nową kartę bez zgody użytkownika i zablokuje ją.
— KnuturO

Proszę, sprawdź, czy newWin ma wartość null!
— FrancescoMM

Czemu ? Nie widzę powodu.
— KnuturO

18

spróbuj tego, to działa dla mnie,

$('#myButton').click(function () {
    var redirectWindow = window.open('http://google.com', '_blank');
    $.ajax({
        type: 'POST',
        url: '/echo/json/',
        success: function (data) {
            redirectWindow.location;
        }
    });
});

Jest skrzypkiem dla tego http://jsfiddle.net/safeeronline/70kdacL4/1/


1
Dzięki, dla mnie też zadziałało - gdyby nie to skrzypce, pewnie nie uwierzyłbym, lol!
— rmcsharry

3
To powinna być akceptowana odpowiedź! Możesz również użyć redirectWindow.location. assign („new_url”), jeśli potrzebujesz danych asynchronicznych do zbudowania adresu URL.
— matheusr

Niezłe rozwiązanie.
— Utrzymaj

Proszę, sprawdź, czy redirectWindow ma wartość null! Zgodnie z różnymi opcjami przeglądarki możesz zostać zablokowany lub nie.
— FrancescoMM

8

Okna muszą być tworzone na tym samym stosie (czyli mikrozadaniu ), co zdarzenie inicjowane przez użytkownika, np . Wywołanie zwrotne kliknięcia - aby nie można było ich później utworzyć asynchronicznie.

Możesz jednak utworzyć okno bez adresu URL, a następnie zmienić adres URL tego okna, gdy już je znasz , nawet asynchronicznie!

window.onclick = () => {
  // You MUST create the window on the same event
  // tick/stack as the user-initiated event (e.g. click callback)
  const googleWindow = window.open();

  // Do your async work
  fakeAjax(response => {
    // Change the URL of the window you created once you
    // know what the full URL is!
    googleWindow.location.replace(`https://google.com?q=${response}`);
  });
};

function fakeAjax(callback) {
  setTimeout(() => {
    callback('example');
  }, 1000);
}

Nowoczesne przeglądarki otworzą okno z pustą stroną (często nazywaną about:blank), a zakładając, że zadanie asynchroniczne w celu uzyskania adresu URL jest dość szybkie, wynikowy UX jest w większości w porządku. Jeśli zamiast tego chcesz wyświetlić komunikat ładowania (lub cokolwiek) w oknie, podczas gdy użytkownik czeka, możesz użyć identyfikatorów URI danych .

window.open('data:text/html,<h1>Loading...<%2Fh1>');

3

Ten kod mi pomoże. Mam nadzieję, że to pomoże niektórym ludziom

$('formSelector').submit( function( event ) {

    event.preventDefault();

    var newWindow = window.open('', '_blank', 'width=750,height=500');

    $.ajax({

        url: ajaxurl,
        type: "POST",
        data: { data },

    }).done( function( response ) {

        if ( ! response ) newWindow.close();
        else newWindow.location = '/url';

    });
});

3

Spróbuj użyć elementu odsyłacza i kliknij go za pomocą javascriipt

<a id="SimulateOpenLink" href="#" target="_blank" rel="noopener noreferrer"></a>

i scenariusz

function openURL(url) {
    document.getElementById("SimulateOpenLink").href = url
    document.getElementById("SimulateOpenLink").click()
}

Użyj tego w ten sposób

//do stuff
var id = 123123141;
openURL("/api/user/" + id + "/print") //this open webpage bypassing pop-up blocker
openURL("https://www.google.com") //Another link

Dzięki temu uniknąłem zajmowania się pustym oknem.
— ponder275

Okazało się, że nie rozwiązało to mojego problemu z iPhone'em blokującym moje okno jako wyskakujące okienko.
— ponder275

2

Spostrzeżenie, że zdarzenie musiało zostać zainicjowane przez użytkownika, pomogło mi zrozumieć pierwszą część tego, ale nawet po tym Chrome i Firefox nadal blokowały nowe okno. Druga część polegała na dodaniu target = "_ blank" do linku, o którym była mowa w jednym komentarzu.

Podsumowując: musisz zadzwonić do window.open ze zdarzenia zainicjowanego przez użytkownika, w tym przypadku klikając na link, i ten link musi mieć target = "_ blank".

W poniższym przykładzie link używa class = "button-twitter".

$('.button-twitter').click(function(e) {
  e.preventDefault();
  var href = $(this).attr('href');
  var tweet_popup = window.open(href, 'tweet_popup', 'width=500,height=300');
});


1

Używam tej metody, aby uniknąć blokowania wyskakujących okienek w moim kodzie React. będzie działać również we wszystkich innych kodach javascript.

Gdy wykonujesz wywołanie asynchroniczne w przypadku zdarzenia kliknięcia, po prostu otwórz najpierw puste okno, a następnie wpisz w nim adres URL później, gdy wywołanie asynchroniczne zostanie zakończone.

const popupWindow = window.open("", "_blank");
popupWindow.document.write("<div>Loading, Plesae wait...</div>")

w przypadku powodzenia wywołania asynchronicznego napisz co następuje

popupWindow.document.write(resonse.url)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.