Избегайте блокировщиков всплывающих окон браузера

172

Я разрабатываю поток аутентификации OAuth исключительно на JavaScript и хочу показать пользователю окно «Предоставить доступ» во всплывающем окне, но оно блокируется.

Как я могу предотвратить всплывающие окна, созданные window.openили window.showModalDialogблокируемые всплывающими окнами в разных браузерах?

Пабло Фернандес
источник
11
Даже если бы это было возможно (я не знаю), если люди используют блокировщики всплывающих окон, вы должны уважать это. Большинство браузеров отображают сообщение, когда сайт пытается открыть всплывающее окно, чтобы они могли видеть его, если захотят. Вы можете разместить на своем сайте замечание, что некоторые материалы открываются во всплывающем окне, и пользователь должен разрешить это для продолжения.
Феликс Клинг
5
Лучшая практика выглядела бы следующим образом: 1) Сделайте это успешно 2) Закройте свои окна и закройте свою дверь и закричите в страхе от собирающейся толпы расстроенных веб-покровителей 3) Покайтесь, уберите pop-up-busta-busta и уважайте ваша аудитория.
Алекс Макп
Алекс и Феликс, я обновил вопрос. Я не буду использовать знания для зла :). Спасибо!
Пабло Фернандес
9
Я хотел бы добавить, что обход блокировщика всплывающих окон на самом деле пытается улучшить пользовательский опыт. В примере, над которым я сейчас работаю, мы используем приложение Javascript (на основе ExtJS), и мы пытаемся позволить пользователям платить с помощью PayPal. Мы даем им кнопку, которую они могут нажать, чтобы запустить PayPal в новом окне, но некоторые версии IE блокируют его как всплывающее окно (даже если это нажатие кнопки). Если теперь они активируют всплывающее окно, экран перезагружается и, как приложение JavaScript, мы теряем состояние окна, и им приходится начинать все сначала. Так что на самом деле: проблема в том, что IE тупой.
NateDSaint
1
@FelixKling Да, это возможно. Производители браузеров уже подумали об этом. Открытие всплывающего окна в порядке, если есть намерение пользователя (сигнализируется пользователем, нажимающим ссылку или кнопку). Блокировщики всплывающих окон должны уважать намерения пользователя. Если блокировщик всплывающих окон в IE этого не делает, виноват именно блокировщик всплывающих окон. Пользователи используют блокировщики всплывающих окон, чтобы запретить скрипты открывать всплывающие окна по своему желанию, а не блокировать всплывающие окна, которые они сами пытались открыть (нажав кнопку или ссылку).
Стейн де Витт

Ответы:

286

Общее правило заключается в том, что блокировщики всплывающих окон включаются, если window.openили аналогичный код вызывается из JavaScript, который не вызывается прямым действием пользователя . То есть, вы можете позвонить window.openв ответ на нажатие кнопки, не блокируя всплывающие окна, но если вы добавите тот же код в событие таймера, он будет заблокирован. Глубина цепочки вызовов также является фактором - некоторые старые браузеры смотрят только на непосредственного абонента, новые браузеры могут немного откатить назад, чтобы увидеть, был ли вызывающий абонент щелчком мыши и т. Д. Держите его как можно более мелким, чтобы избежать блокировки всплывающих окон.

dthorpe
источник
2
Интересно, что всплывающие окна, инициированные с помощью события изменения, связанного с элементом select, будут заблокированы (в Chrome, а не FF), даже если это событие инициируется прямым действием пользователя, например щелчком. Хотя, если привязаны к входу, они разрешены. Странный.
cnnokes
6
Никто не сказал, что браузер был последовательным. : P
dthorpe
8
Из экспериментов я понял, что глубина стека не имеет ничего общего с блокировщиком всплывающих окон. Он фактически проверяет, вызывается ли window.open в течение 1 секунды после действия пользователя или нет. Протестировано в Chrome 46 и Firefox 42.
Mesqalito
Тайм-аут в 1 секунду можно обойти в обоих браузерах, что позволяет в течение неопределенного времени использовать ответ @ tj-crowder на этой странице . Пусть URL, по которому вы звоните через ajax, будет каким-то php (или любым другим), использующим sleep ( ) на некоторое время, прежде чем он вернет ответ. Браузер будет зависать, пока он «загружает» страницу, а затем запускать всплывающее окно при получении ответа. В Chrome, однако, вы должны добавить alert () прямо перед ajax (), чтобы этот трюк работал.
фанфары
184

На основе Jason Sebring «S очень полезный совет , и на вещи , покрытые здесь и там , я нашел идеальное решение для моего случая:

Псевдокод с фрагментами Javascript:

  1. немедленно создать пустое всплывающее окно на действие пользователя

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

    Необязательно: добавьте некоторое «ожидающее» информационное сообщение. Примеры:

    а) Внешняя HTML-страница: замените вышеуказанную строку на

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

    б) Текст: добавьте следующую строку ниже приведенной выше:

    importantStuff.document.write('Loading preview...');
  2. заполните его содержимым, когда будете готовы (например, когда будет возвращен вызов AJAX)

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

Обогатите вызов window.openлюбыми дополнительными опциями, которые вам нужны.

Я на самом деле использую это решение для перенаправления почты, и оно работает во всех моих браузерах (Windows 7, Android). Этот _blankбит помогает перенаправлению почты на мобильный телефон, кстати.

Твой опыт? Есть ли способ улучшить это?

Швейцарский господин
источник
1
? так что вы делаете, если это не от действий пользователя в браузере? Для моего примера, после того, как пользователь аутентифицируется на сервере, он должен открыть новую вкладку
Don Cheadle
@mmcrae не делай этого. Что бы вы ни собирались сделать, есть лучший способ, чем всплывающее окно. В противном случае, я бы никогда не хотел видеть ваш сайт. Современные браузеры гарантируют, что вы не сможете этого сделать - см. Ответ @dthorpe .
швейцарский господин
5
Whatever it is you intend to do, there is a better way than a popup windowржунимагу? Странное обобщение. Клиент хотел бы, чтобы страница, на которой он проходил аутентификацию, оставалась открытой, а новый сайт / целевая страница после аутентификации открывался в новой вкладке. Да, не мое представление о превосходном пользовательском опыте ... но это кажется разумным
Дон Чидл
@mmcrae. Садись и думай серьезно. Я обещаю вам, что вы найдете «действие пользователя» в сценарии, который вы описываете. Суть моего ответа в том, что вы создаете всплывающее окно, когда у вас есть действие пользователя, а затем заполняете его содержимым позже. Подсказка для вашего случая: пользователь нажимает «аутентифицировать» -> создать всплывающее окно (пусто); таймер включен или ответ сервера вернулся или что-то еще -> заполнить содержимое во всплывающем окне.
швейцарский господин
3
Полезный совет: если ajax-запрос завершается неудачно, позвоните, importantStuff.closeчтобы закрыть новую вкладку и предоставить уведомление на исходной странице.
Гусь
24

В дополнение к сообщению Swiss Mister, в моем случае window.open был запущен в обещании, которое включало блокировку всплывающих окон, мое решение было: in angular:

$scope.gotClick = function(){

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

    }
  );
};

browserService:

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

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

это то, как вы можете открыть новую вкладку, используя ответ на обещание и не вызывая блокировщик всплывающих окон.

Дэвид
источник
1
Это привело к моему решению! Где я создал переменную, содержащую открытую вкладку, а затем заполнил URL после загрузки данных. Спасибо. const tab = window.open(); observable.subscribe(dataUrl => tab.location.href = dataUrl);
Джонас
1
Если у вас включен блокировщик всплывающих окон, это не решит проблему с блокировщиком всплывающих окон ...
Алехандро Валес
@Alejandro Решение Vales jonas будет работать только в том случае, если код будет запущен из события onClick или любого взаимодействия с пользователем. Когда вы пытаетесь открыть новую вкладку через windows.open внутри обещания, тайм-аута, подписки ... браузер думает, что это манипуляция для пользователя, поэтому решение состоит в том, чтобы создать экземпляр перед вводом обещания, внутри вас просто изменить Href, как сделал Джонас
Дэвид
@ Давид Спасибо большое !!! Я не случайно увидел .thenответ на этот вопрос, и поэтому я так растерялся: / SRY ...
Алехандро Вейлс
@ Дэвид Это здорово! Работает как шарм. Если бы я мог побеспокоить вас еще одним вопросом - знаете ли вы, как заставить это работать и в Edge? Смещение к правильному ресурсу очень помогло бы ...
dzenesiz
21

Как хорошая практика, я думаю, что это хорошая идея, чтобы проверить, было ли заблокировано всплывающее окно и принять меры в случае. Вы должны знать, что window.open имеет возвращаемое значение, и это значение может быть нулевым, если действие не выполнено. Например, в следующем коде:

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

если всплывающее окно заблокировано, window.open вернет ноль. Так что функция вернет false.

В качестве примера представьте, что вы вызываете эту функцию напрямую из любой ссылки с помощью target="_blank": если всплывающее окно успешно открыто, возвращение falseзаблокирует действие ссылки, в противном случае всплывающее окно заблокирует trueповедение по умолчанию (откройте новое окно _blank) и продолжит работу. ,

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

Таким образом, у вас будет всплывающее окно, если оно работает, и окно _blank, если нет.

Если всплывающее окно не открывается, вы можете:

  • откройте пустое окно, как в примере, и продолжайте
  • открыть поддельное всплывающее окно (iframe внутри страницы)
  • сообщить пользователю («пожалуйста, разрешите всплывающие окна для этого сайта»)
  • откройте пустое окно, а затем сообщите пользователю и т. д.
FrancescoMM
источник
Спасибо. Работал!
Bcktr
8

из JavaScript API Google:

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

Смотрите область, где написано:

Настройка аутентификации

Реализация OAuth 2.0 на клиенте использует всплывающее окно, чтобы предложить пользователю выполнить вход и одобрить приложение. Первый вызов gapi.auth.authorize может вызвать блокировку всплывающих окон, так как он косвенно открывает всплывающее окно. Чтобы запретить блокировку всплывающих окон при вызовах аутентификации, вызовите gapi.auth.init (callback) при загрузке клиента. Предоставленный обратный вызов будет выполнен, когда библиотека будет готова выполнить аутентификационные вызовы.

Я бы предположил, что это связано с реальным ответом выше, в том, как он объясняет, если есть немедленный ответ, он не отключит всплывающую тревогу. «Gapi.auth.init» делает так, чтобы API происходил немедленно.

Практическое применение

Я создал микросервис аутентификации с открытым исходным кодом, используя паспорт узла на npm и различные паспортные пакеты для каждого провайдера. Я использовал стандартный подход перенаправления к третьей стороне, предоставляя ей URL перенаправления для возврата. Это было программно, поэтому я мог иметь различные места для перенаправления обратно, если вход / регистрация и на определенных страницах.

github.com/sebringj/athu

passportjs.org

Джейсон Себринг
источник
3

Я пробовал несколько решений, но это единственное, которое действительно работает для меня во всех браузерах

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

pomobc
источник
2
Это просто не работает для людей, у которых включена блокировка всплывающих окон
Алехандро Вейлс
что такое url? это не назначено.
Shinriyo
URL-адрес @shinriyo - это ссылка на любую страницу, к которой вы хотите получить доступ, напримерhttp://example.com
pomobc
@AlejandroVales У меня включен блокировщик всплывающих окон, и он работает. Проблема в том, что window.open()нельзя открыть новое окно программно, и, если нам нужно, этот ответ является одним из вариантов. С помощью newTabпеременной, на которую мы ссылаемся, window.open()и позже мы можем ее вызвать, вставить нужный urlнам, в моем случае, URL какого-то большого двоичного объекта, и новая вкладка будет открыта по введенному URL.
stanimirsp
0

Я не хотел создавать новую страницу, если обратный вызов не был успешно возвращен, поэтому я сделал это для имитации клика пользователя:

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
источник
3
в Chrome 62 у меня не работает. Всплывающее окно (страница) блокируется.
с.ермакович
1
Да, вы правы - я также обнаружил, что это противоречиво. Я сделал синхронный вызов API
user3479425
-8

Самый простой способ избавиться от этого - это:

  1. Не используйте document.open ().
  2. Вместо этого используйте this.document.location.href = location; где location это URL для загрузки

Пример:

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

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

Это сработало очень хорошо для меня. ура

Прамод Шетти
источник
2
Как насчет открытия URL в новой вкладке?
3 правила