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 :)

mavaze
źródło
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");
          }
        });
      });
    });
TJ Crowder
źródło
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
Evgeniy Kubyshin
źródło
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, "");
    });
};
KnuturO
źródło
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/

Mohammed Safeer
źródło
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>');
jayphelps
źródło
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';

    });
});
Richard
źródło
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
Fernando Carvajal
źródło
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');
});
Alexis Bellido
źródło
2
var url = window.open("", "_blank");
url.location = "url";

to działało dla mnie.

Diego Santa Cruz Mendezú
źródło
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)
Tabish
źródło