100

I want to show a JQuery dialog conditionally on click event of an hyperlink .

I have a requirement like on condition1 open a JQuery dialogue and if condition1 is not satisfied, navigate to the page as referenced by 'href' tag of whose click event is in question.

I am able to call a function on link's click event. This function now checks the said condition by executing another URL (that executes my Spring controller and returns response).

All works perfect with only window.open being blocked by popup blocker.

$('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);
        }
    });
});

If I remove e.preventDefault(); from code, popoup blocker doesn't block the page, however for condition1 it then opens the dialogue as well as opens the 'href' page.

If I solve one, it creates issue for another. I am not able to give justice to both conditions simultaneously.

Could you help me solve this issue please?

Once this is solved I have another issue to solve i.e. navigation on dialogue's OK event :)

T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
mavaze
  • 1,197
  • 3
  • 9
  • 12
  • 1
    try not to use .live its deprecated and a pointer to .on() ! – mas-designs Mar 01 '12 at 10:54
  • @EvilP: As others said to you last time, only in 1.7 and up. A **lot** of projects will still be on 1.5 or 1.6. – T.J. Crowder Mar 01 '12 at 11:02
  • 10
    Downvoter: Just because you don't like popups, that doesn't mean this question should be downvoted. A question should be downvoted if it *"...does not show any research effort; if is unclear or not useful"*. The question is clear, doesn't seem lazy, and stems from a non-trivial combination of factors. – T.J. Crowder Mar 01 '12 at 11:03
  • @T.J.Crowder: He didn't specify which version he is using. So I should consider that he is using the latest version. If he is using a different version, I'm sure he would mention that because THEN he WOULD be aware of the fact that live is deprecated and would explain WHY he is using .live(). Nobody ever told me the "last" time so I thing you should get yourself a break and calm down. There is no need to be so harsh... – mas-designs Mar 01 '12 at 11:16
  • Didn't got any notification about that. But isn't it normal to consider that if somebody doesn't specify their version to consider that they are using the latest ? I mean I have to use 1.3 for a reason and I'm aware of the fact that there is a newer and better version. – mas-designs Mar 01 '12 at 11:20
  • @EvilP Thanks for introducing me to a new method .on() and thanks T.J.Crowder for highlighting that this is part of 1.7+. I have taken a note of this and will definitely consider if I happen to upgrade my JQuery plugin from current version of 1.4.4. – mavaze Mar 02 '12 at 05:26

10 Answers10

145

Popup blockers will typically only allow window.open if used during the processing of a user event (like a click). In your case, you're calling window.open later, not during the event, because $.getJSON is asynchronous.

You have two options:

  1. Do something else, rather than window.open.

  2. Make the ajax call synchronous, which is something you should normally avoid like the plague as it locks up the UI of the browser. $.getJSON is equivalent to:

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

    ...and so you can make your $.getJSON call synchronous by mapping your params to the above and adding 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);
            }
        }
    });
    

    Again, I don't advocate synchronous ajax calls if you can find any other way to achieve your goal. But if you can't, there you go.

    Here's an example of code that fails the test because of the asynchronous call:

    Live example | Live source (The live links no longer work because of changes to 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");
        });
      });
    });
    

    And here's an example that does work, using a synchronous call:

    Live example | Live source (The live links no longer work because of changes to 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");
          }
        });
      });
    });
    
T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • 3
    Thanks a million. Your suggestion of making call synchronous worked like a charm. The same did help me in my probable next issue of handling navigation on dialogue's OK event. – mavaze Mar 02 '12 at 07:36
  • As T.J. Crowder has left 2 questions open, [1. find alternative to window.open] and [2. avoid ajax synchronous call] suggestions on those are welcome for benefit of the community. Else I found his answer a perfect solution for the said question :) – mavaze Mar 02 '12 at 07:47
  • 4
    For me, I added a link with target="_blank" to prompt the user to click. – hiroshi Apr 20 '12 at 07:14
  • In the upcoming jQuery 1.8, the synchronous functionality is being deprecated. Any ideas how to make this work without that? – Evan Jun 25 '12 at 03:06
  • 1
    @Evan: You'll have to use XHR directly. There's an example in [the ticket for deprecating synchronous requests](http://bugs.jquery.com/ticket/11013). – T.J. Crowder Jun 25 '12 at 05:34
  • @PaulTomblin: That's because the old mechanism for ajax that JSBin had no longer works. The *code* still works (just double-checked with recent jQuery and Firefox), the issue with the above is a JSBIn artefact. I've updated the answer to say that. That said, the `async` option on `ajax` has been deprecated since jQuery 1.8... :-) – T.J. Crowder Dec 30 '14 at 16:21
  • 2
    @mavaze I think you can avoid both issues if you can change the control flow to first open the window, (sync), then do the AJax request (async) and then use the response to either show the error message or do the redirect. – Stijn de Witt Aug 21 '17 at 19:40
  • @StijndeWitt: I didn't think to question the flow. I completely agree, that would be better if at all possible. – T.J. Crowder Aug 22 '17 at 06:53
  • Great answer! I though I will need to re-do the whole approach and now I just added `async: false` and it works! Thanks a lot! – Mike Keskinov Nov 27 '18 at 16:45
  • "during the processing of a user event (like a click)" That is correct but you can add a listener to open window before you perform something like "preventDefault()" and after response is received simple locate opened window. – Aleksandr Ryabov Jan 18 '19 at 00:58
61

you can call window.open without browser blocking only if user does directly some action. Browser send some flag and determine that window opened by user action.

So, you can try this scenario:

  1. var myWindow = window.open('')
  2. draw any loading message in this window
  3. when request done, just call myWindow.location = 'http://google.com'
Ishan Jain
  • 7,501
  • 9
  • 45
  • 72
  • 2
    This doesn't work on Safari Mobile (tested on IPhone 4). I tried several other ideas (e.g. ```myWindow.postMessage```) but because of Safaris restriction of not executing JavaScript in the background, the parent window can never send that location change. – roundrobin May 14 '14 at 00:28
  • @BausTheBig I tested on IPhone 6, IOS8. It worked like a charm. – lvarayut Jan 24 '15 at 13:44
  • was looking for a way to track external links with Google Analytics without getting popup blocked. This was exactly what I needed. Seems to work fine on iPhone 4s in iOS7 (actual phone) and iOS 8 (iOS Simulator). – notacouch Mar 11 '15 at 21:14
38

I had this problem and I didn't have my url ready untill the callback would return some data. The solution was to open blank window before starting the callback and then just set the location when the callback returns.

$scope.testCode = function () {
    var newWin = $window.open('', '_blank');
    service.testCode().then(function (data) {
        $scope.testing = true;
        newWin.location = '/Tests/' + data.url.replace(/["]/g, "");
    });
};
Michael Cole
  • 13,473
  • 4
  • 66
  • 81
KnuturO
  • 1,141
  • 11
  • 14
  • 1
    I used this solution to open a new window and bypass the popupblocker – VeldMuijz Feb 09 '16 at 10:07
  • What if I don't know before hand if I'll need to open a the popup? As in: `action().then(doWindowOpenThing).catch(doSomethingElseThatDoesntOpenAPopup)` So I can't open a window before hand (to keep its reference, etc) if later on I won't be using it, right? – Luiz May 11 '16 at 13:50
  • No it would open a new tab every time and I guess you would not want that in the case where you don't use it. The thing is the browser will only allow you to open a new tab in the click function (user initiated action) otherwise the popup blocker will see this as a script opening new tab without user permission and block it. – KnuturO May 12 '16 at 10:51
  • Please, do test if newWin is null! – FrancescoMM Mar 13 '19 at 17:40
  • Why ? I see no reason for this. – KnuturO Mar 14 '19 at 14:03
  • Thanks, this is exactly what I needed. In my case the new tab (window) will always be used, so no problem creating it first. Of course that if chrome programmers were really smart they should implement exceptions for websites where the user is logged in. If(user_logged_in()) allow_pop_ups = true ;) – Pedro Araujo Jorge Dec 16 '20 at 14:38
18

try this, it works for me,

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

Is fiddle for this http://jsfiddle.net/safeeronline/70kdacL4/1/

Mohammed Safeer
  • 17,481
  • 8
  • 66
  • 74
8

Windows must be created on the same stack (aka microtask) as the user-initiated event, e.g. a click callback--so they can't be created later, asynchronously.

However, you can create a window without a URL and you can then change that window's URL once you do know it, even asynchronously!

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);
}

Modern browsers will open the window with a blank page (often called about:blank), and assuming your async task to get the URL is fairly quick, the resulting UX is mostly fine. If you instead want to render a loading message (or anything) into the window while the user waits, you can use Data URIs.

window.open('data:text/html,<h1>Loading...<%2Fh1>');
jayphelps
  • 14,317
  • 2
  • 38
  • 52
3

This code help me. Hope this help some people

$('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
  • 91
  • 3
2

The observation that the event had to be initiated by the user helped me to figure out the first part of this, but even after that Chrome and Firefox still blocked the new window. The second part was adding target="_blank" to the link, which was mentioned in one comment.

In summary: you need to call window.open from an event initiated by the user, in this case clicking on a link, and that link needs to have target="_blank".

In the example below the link is using 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
  • 685
  • 1
  • 5
  • 13
2

Try using an a link element and click it with javascriipt

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

and the script

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

Use it like this

//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
  • 1,395
  • 15
  • 18
2
var url = window.open("", "_blank");
url.location = "url";

this worked for me.

1

I am using this method to avoid the popup blocker in my React code. it will work in all other javascript codes also.

When you are making an async call on click event, just open a blank window first and then write the URL in that later when an async call will complete.

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

on async call's success, write the following

popupWindow.document.write(resonse.url)
Tabish
  • 1,328
  • 15
  • 13