13

LIVE DEMO

Given a URI of a file, I'd like to open it in a new tab (not a new window).

It looks like it is not possible to use $window.open(uri, '_blank').

So, I tried the following trick:

var link = angular.element('<a href="uri-here" target="_blank"></a>');
angular.element(document.body).append(link);
link[0].click();
link.remove();

and it works.

But, if I put exactly the same code in a promise callback, it doesn't work anymore (it opens the file in a new window instead).

Any idea what's going on here?

PLAYGROUND HERE

Community
  • 1
  • 1
Misha Moroshko
  • 166,356
  • 226
  • 505
  • 746
  • 4
    It cannot be done. It's user preference whether new pages opens in a window or new tab and it cannot be changed. – laurent Jul 11 '14 at 00:55
  • Your demo playground seems to work as expected. I'm on IE 11. – Michael Kang Jul 11 '14 at 01:36
  • @pixelbits Interesting. It seems to work in Firefox 30.0 as well. I wonder why it doesn't work in Chrome (35.0 in my case). – Misha Moroshko Jul 11 '14 at 01:51
  • 1
    As others pointed out, you can't override users choice on browser behavior (or even default one). Anyway, I would rewrite your "dirty trick" of the link with a more clean (and readable) `$window.open('http://martinfowler.com/ieeeSoftware/whenType.pdf')` – domokun Jul 17 '14 at 13:25
  • 1
    The simple answer is security says you can't. I think the best way to maintain your tab is to simply move you file selection logic into what is serves the new window and drop the ajax call, it's kind of a pointless step anyway isn't it. – Dylan Jul 20 '14 at 19:23

7 Answers7

11

From your code/content, you can't force the browser to open a new tab (rather than a new window, or vice-versa). It's up to the browser settings to force it one way or another.

Anything else would be a security risk.

David-SkyMesh
  • 5,041
  • 1
  • 31
  • 38
  • Could you please explain why this would be a security risk? Why do you think opening in a new tab is the default behaviour in Firefox and IE, but not in Chrome? – Misha Moroshko Jul 14 '14 at 09:10
  • It's about being able to draw (or evade) the attention of the end-user. Browsers have default strategies for how they open new windows (what the functionality actually does) and whether they instead/optionally open them within the same window under a new tab. Users get used to whichever strategy the browser provides. The browsers provide ways for end-users (not websites) to override the default strategy. By being able to force it one way or another, you'd gain the ability to have your new content *go unnoticed* by some users and/or annoy other users who've already expressed their preference. – David-SkyMesh Jul 15 '14 at 00:44
4

Let us understand fundamental how pop up blocker work.

If user trigger the function to open a new url, then pop up blocker will allow it(it should applied to any modern browser - at least firefox, chrome)

If not from user (like javascript function in background, promise or any other function trigger not from user), browser will block unless user whitelist the site manually.

This is not working.

function openInNewTab() {
    window.open('http://stackoverflow.com','_blank');
}

openInNewTab();//fail

This is working

<h1><button onclick="openInNewTab()">Open In New Tab - working</button></h1>

I created simple plunkr version - http://plnkr.co/edit/QqsEzMtG5oawZsQq0XBV?p=preview

So, to answer your question. It is impossible unless user authorize it (user trigger it or white listed the site).

Quote from firefox -

Pop-up windows, or pop-ups, are windows that appear automatically without your permission.

https://support.mozilla.org/en-US/kb/pop-blocker-settings-exceptions-troubleshooting

*Open in new tab / new windows not make any difference. Pop up blocker will still always block. It doesn't means that browser will allow if open in new tab. It is just coincidentally for certain browser default the settings in that manner.

Workaround

You can ask user explicitly to trigger the function to open in new tab after the background execution.
You can display message in UI to ask user to open the url. Example - http://plnkr.co/edit/iyNzpg64DtsrijAGbHlT?p=preview

hutingung
  • 1,800
  • 16
  • 25
2

You can only open new windows inside click event handlers fired by the user.

The reason for this is usability.

I'm not sure if all browsers have this behavior but some browsers do not allow scripts to open windows without the user being noticed. Imagine when you visit a web page and suddenly, the web page opens several windows => it's annoying.

See this DEMO (tested with my Chrome and Firefox), even we trigger click event by script, the browser still blocks the popup.

$("#test").click(function(){
    openInNewTab();
});

$("#test").click();

You cannot open a new window inside your ajax success callback because your ajax success is run in another cycle after the click event handler has finished its execution.

See this link for a workaround

if I put exactly the same code in a promise callback, it doesn't work anymore (it opens the file in a new window instead).

I'm surprised that you're still able to open a new window. But this problem really has a lot of things to do with click events fired by the user.

Community
  • 1
  • 1
Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • BTW, your link http://jsbin.com/cuhekeve/1/edit is blocked in my browsers (Chrome and Firefox) because it's not inside a click event handler fired by the user. – Khanh TO Jul 17 '14 at 13:39
1

Your problem is two-fold, and both folds tread on uncertain territory.


In the old days of browsers, window.open did exactly that – open a new window. That's because the concept of tabs hadn't been invented yet. When tabs were introduced, they were treated exactly like windows to improve compatibility, and that tradition continues to this day. That, and the fact that window.open was only standardized very recently, means that JavaScript cannot distinguish between windows and tabs.

There is no "normal" way to specify whether a link should open in a new tab or not. You can use the following hack, though: specify a custom window size to the open call (via the third argument), like so:

window.open('http://example.com', '', 'width=' + screen.width);

This will cause almost all browsers to open a separate window because tabs cannot have custom sizes.


In JavaScript, there are trusted events and untrusted events. Trusted events are, for example, legitimate clicks on a link by the user, whereas an untrusted event would be a manual click() call on a link.

Only trusted event handlers may open new windows/tabs. This is to prevent client-side attacks that crash the browser or confuse a user by rapidly opening a hundred tabs on mouseover or something similar.

Your second example doesn't work because the popup blocker blocks the untrusted event that you triggered via the click(). Although it was caused by a real click, the asynchronous call in-between severs the link to trustedness.

rvighne
  • 20,755
  • 11
  • 51
  • 73
0

working version

$http.get('https://api.github.com/users/angular').then(openInNewTab());

EDIT----------------

Do not know why but a click() method called from a callback function acts differently than calling it straight.

You can see it here with a set interval example.

That is why I had call the function directly rather than going through a callback.

see it with timer callback

Chi Row
  • 1,106
  • 7
  • 18
  • I suggest using .success(), then you can change your results if you get a fail condition. However, this isn't the question. The issue is that they want to override a browser function: "in a new tab (not a new window)" – Brian Webb Jul 11 '14 at 01:07
  • Any idea why `.then(openInNewTab())` works but `.then(openInNewTab)` doesn't? – Misha Moroshko Jul 11 '14 at 01:15
  • .then expects a function. When openInNewTab() is called, its the return value that is passed to the 'then'. But openInNewTab is not returning anything - its undefined. I suspect it may be working for the wrong reasons. – Michael Kang Jul 11 '14 at 01:38
  • `.then(openInNewTab())` works because `openInNewTab()` is called immediately, which is equivalent to the first working button in the demo. The question is why it doesn't work if called **later**, inside a callback, for example. This answer doesn't really answer the question. – Misha Moroshko Jul 11 '14 at 01:46
  • @Misha Moroshko, see the second example in my answer. A click function in callback acts differently. It's not angular. It's a browser thing. Don't know the exact reason. – Chi Row Jul 11 '14 at 03:23
  • It's about rights escalation. A code by itself cannot open a new window or tab. But if the code is to be executed after a click (which means it starts after the user did an action on the web page) then the code can open a new window since it has the rights. – AlexandruB Jul 16 '14 at 17:28
0

or you can use $window service please see here : http://plnkr.co/edit/8egebfFj4T3LwM0Kd64s?p=preview

angular.module("Demo", []).controller("DemoCtrl", function($scope, $http, $window) {
  $scope.uri = 'http://martinfowler.com/ieeeSoftware/whenType.pdf';

  function openInNewTab() {

    var link = angular.element('<a href="' + $scope.uri + '" target="_blank"></a>');

    angular.element(document.body).append(link);

    link[0].click();
    link.remove();
  }

  $scope.works = openInNewTab;

  $scope.doesntWork = function() {
    $http.get('https://api.github.com/users/angular').then($window.open($scope.uri));
  };
});
sylwester
  • 16,498
  • 1
  • 25
  • 33
  • 1
    this `$window.open($scope.uri)` is run `immediately` and the returned value is passed to `.then`. That's why it works. – Khanh TO Jul 17 '14 at 14:00
0

For us the following worked well: http://blog-it.hypoport.de/2014/08/19/how-to-open-async-calls-in-a-new-tab-instead-of-new-window-within-an-angularjs-app/

In short: We remember the reference to the new window and changing the location afterwards.