It seems Chrome doesn't have an API to open the popup, but has a dedicated system for doing it with a hotkey: _execute_browser_action key in commands.
The special functionality of _execute_browser_action in commands is not supported(1) by Firefox.
The type of popup I care about is browserAction, not pageAction.
How can I have the browserAction popup open when a keyboard shortcut/hotkey combination is pressed?
Available natively in Firefox versions >= 52
This functionality will be natively available in Firefox 52, which is currently Firefox Developer Edition (i.e. Firefox 52.0a2).
As you know, for WebExtensions, you create a global hotkey using the _execute_browser_action key within the object supplied for the commands key. For example:
"commands":{
"_execute_browser_action": {
"suggested_key": {
"default": "Alt+Shift+J"
}
}
}
Open a pseudo-popup (polyfill this functionality in the older versions of Firefox)
While the explicit functionality will not be available until Firefox 52, you can polyfill this functionality in the current version of Firefox, by defining a custom command that is named "_execute_browser_action". It's going to look a bit different than your normal popup, but it will be functional. It will be in a panel, which you may have to account for with some associated styling which is applied only when it is in a panel instead of a popup. There may also be some differences in what the active tab is when your panel is open. However, the code below at least accounts for that when performing queries with chrome.tabs.query(), or browser.tabs.query(), by making the response be what would be expected if it was open in a real popup instead of a panel.
The same code will continue to work on Firefox 52+. On Firefox 52+, the "_execute_browser_action" directly activates the browser action click, or popup.
For when you aren't using a popup, the primary thing is that you do not use an anonymous function for the browserAction.onClicked listener. This allows the functionality to also be called by the commands.onCommand listener. The commands.onCommand was introduced in Firefox 48, so this should work on any version which is 48+.
You may have some issues with needing permissions other than activeTab when using this polyfill. Exactly what is needed, if anything, will depend on your code.
The following is an extension which causes the functionality invoked with a browser action button to be executed when you hit the keyboard shortcut Alt-Shift-J. It will either activate the doActionButton() function, or, if a popup is defined, it will open your popup as a panel which will behave similarly to how a popup normally behaves, but it is not perfect. It gets the name of the popup file from the one that is currently defined for the current active tab, as would be the case for clicking the browserAction button.
manifest.json:
{
"description": "Polyfill browserAction keyboard shortcut, including popups.",
"manifest_version": 2,
"name": "Polyfill browserAction keyboard shortcut",
"version": "0.1",
"background": {
"scripts": [
"background.js"
]
},
"browser_action": {
"default_icon": {
"32": "myIcon.png"
},
"default_title": "Open popup",
"default_popup": "popup.html"
},
"commands": {
"_execute_browser_action": {
"suggested_key": {
"default": "Alt+Shift+J"
}
}
}
}
background.js:
chrome.browserAction.onClicked.addListener(doActionButton);
function doActionButton(tab){
console.log('Action Button clicked. Tab:',tab);
}
chrome.commands.onCommand.addListener(function(command) {
//Polyfill the Browser Action button
if(command === '_execute_browser_action') {
chrome.tabs.query({active:true,currentWindow:true},function(tabs){
//Get the popup for the current tab
chrome.browserAction.getPopup({tabId:tabs[0].id},function(popupFile){
if(popupFile){
openPopup(tabs[0],popupFile);
} else {
//There is no popup defined, so we do what is supposed to be done for
// the browserAction button.
doActionButton(tabs[0]);
}
});
});
return;
} //else
});
//popupWindowId can be true, false, or the popup's window Id.
var popupWindowId = false;
var lastFocusedWin;
var lastActiveTab;
function openPopup(tab,popupFile){
chrome.windows.getLastFocused(function(win){
lastFocusedWin=win;
if(popupWindowId === false){
//This prevents user from pressing the button quickly multiple times in a row.
popupWindowId = true;
lastActiveTab = tab;
chrome.windows.create({
url: popupFile,
type: 'popup',
},function(win){
popupWindowId = win.id;
//Poll for the view of the window ID. Poll every 50ms for a
// maximum of 20 times (1 second). Then do a second set of polling to
// accommodate slower machines.
// Testing on a single moderately fast machine indicated the view
// was available after, at most, the second 50ms delay.
waitForWindowId(popupWindowId,50,20,actOnPopupViewFound,do2ndWaitForWinId);
});
return;
}else if(typeof popupWindowId === 'number'){
//The window is open, and the user pressed the hotkey combo.
// Close the window (as happens for a browserAction popup).
closePopup();
}
});
}
function closePopup(){
if(typeof popupWindowId === 'number'){
chrome.windows.remove(popupWindowId,function(){
popupWindowId = false;
});
}
}
chrome.windows.onRemoved.addListener(function(winId){
if(popupWindowId === winId){
popupWindowId = false;
}
});
chrome.windows.onFocusChanged.addListener(function(winId){
//If the focus is no longer the popup, then close the popup.
if(typeof popupWindowId === 'number'){
if(popupWindowId !== winId){
closePopup();
}
} else if(popupWindowId){
}
});
function actOnPopupViewFound(view){
//Make tabs.query act as if the panel is a popup.
if(typeof view.chrome === 'object'){
view.chrome.tabs.query = fakeTabsQuery;
}
if(typeof view.browser === 'object'){
view.browser.tabs.query = fakeTabsQuery;
}
view.document.addEventListener('DOMContentLoaded',function(ev){
let boundRec = view.document.body.getBoundingClientRect();
updatePopupWindow({
width:boundRec.width + 20,
height:boundRec.height + 40
});
});
updatePopupWindow({});
}
function updatePopupWindow(opt){
let width,height;
if(opt){
width =typeof opt.width === 'number'?opt.width :400;
height=typeof opt.height === 'number'?opt.height:300;
}
//By the time we get here it is too late to find the window for which we
// are trying to open the popup.
let left = lastFocusedWin.left + lastFocusedWin.width - (width +40);
let top = lastFocusedWin.top + 85; //Just a value that works in the default case.
let updateInfo = {
width:width,
height:height,
top:top,
left:left
};
chrome.windows.update(popupWindowId,updateInfo);
}
function waitForWindowId(id,delay,maxTries,foundCallback,notFoundCallback) {
if(maxTries--<=0){
if(typeof notFoundCallback === 'function'){
notFoundCallback(id,foundCallback);
}
return;
}
let views = chrome.extension.getViews({windowId:id});
if(views.length > 0){
if(typeof foundCallback === 'function'){
foundCallback(views[0]);
}
} else {
setTimeout(waitForWindowId,delay,id,delay,maxTries,foundCallback,notFoundCallback);
}
}
function do2ndWaitForWinId(winId,foundCallback){
//Poll for the view of the window ID. Poll every 500ms for a
// maximum of 40 times (20 seconds).
waitForWindowId(winId,500,40,foundCallback,windowViewNotFound);
}
function windowViewNotFound(winId,foundCallback){
//Did not find the view for the window. Do what you want here.
// Currently fail quietly.
}
function fakeTabsQuery(options,callback){
//This fakes the response of chrome.tabs.query and browser.tabs.query, which in
// a browser action popup returns the tab that is active in the window which
// was the current window when the popup was opened. We need to emulate this
// in the popup as panel.
//The popup is also stripped from responses if the response contains multiple
// tabs.
let origCallback = callback;
function stripPopupWinFromResponse(tabs){
return tabs.filter(tab=>{
return tab.windowId !== popupWindowId;
});
}
function stripPopupWinFromResponseIfMultiple(tabs){
if(tabs.length>1){
return stripPopupWinFromResponse(tabs);
}else{
return tabs;
}
}
function callbackWithStrippedTabs(tabs){
origCallback(stripPopupWinFromResponseIfMultiple(tabs));
}
if(options.currentWindow || options.lastFocusedWindow){
//Make the query use the window which was active prior to the panel being
// opened.
delete options.currentWindow;
delete options.lastFocusedWindow;
options.windowId = lastActiveTab.windowId;
}
if(typeof callback === 'function') {
callback = callbackWithStrippedTabs;
chrome.tabs.query.apply(this,arguments);
return;
}else{
return browser.tabs.query.apply(this,arguments)
.then(stripPopupWinFromResponseIfMultiple);
}
}
WebExtensions is still in development:
The WebExtensions API is very much still in development. What is working improves with each version of Firefox. For now, you are probably best off developing and testing your WebExtension add-on with Firefox Developer Edition, or Firefox Nightly (for _execute_browser_action). You should also make careful note of what version of Firefox is required for the functionality you desire to use. This information is contained in the "Browser compatibility" section of the MDN documentation pages.
Some portions of the code in this question have been copied/modified from various other answers of mine.
Support for _exectue_browser_action is on its way: https://bugzilla.mozilla.org/show_bug.cgi?id=1246034
Meanwhile I'm quite sure it's not possible.
_exectue_browser_action, _execute_page_action, _execute_sidebar_action implemented: Special shortcuts.
I have code so far that will save a bookmark of the current tab then close it when i push my WebExtension button. I want the code to save and then close all of the tabs.
var currentTab;
var currentBookmark;
// gets active tabe
function callOnActiveTab(callback) {
chrome.tabs.query({currentWindow: true}, function(tabs) {
for (var tab of tabs) {
if (tab.active) {
callback(tab, tabs);
}
}
});
}
/*
* Add the bookmark on the current page.
*/
function Bookmark() {
chrome.bookmarks.create({title: currentTab.title, url: currentTab.url}, function(bookmark) {
currentBookmark = bookmark;
});
callOnActiveTab((tab) => {
chrome.tabs.remove(tab.id);
});
}
/*
* Switches currentTab and currentBookmark to reflect the currently active tab
*/
function updateTab() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
if (tabs[0]) {
currentTab = tabs[0];
chrome.bookmarks.search({url: currentTab.url}, (bookmarks) => {
currentBookmark = bookmarks[0];
});
}
});
}
function listTabs() {
Bookmark();
}
chrome.browserAction.onClicked.addListener(listTabs);
chrome.tabs.onUpdated.addListener(updateTab);
// listen to tab switching
chrome.tabs.onActivated.addListener(updateTab);
If I add the Bookmark() function to the end of updateTab() function, the button no longer works and when I change tabs it saves that one and exits all tabs.
Quite a bit of your code appears overly complicated for what you appear to be attempting to do. A significant part of your problem with not being able to use the Bookmark function to bookmark and remove multiple tabs is that it is relying on a global variable that is changed by an asynchronous event handler which is tracking the active tab. That function can be re-coded to use an argument that is passed in to the function. In that way it can be generally re-used.
Note: I moved the removal of the tab out of the bookmarkTab function (what is Bookmark in your code). Having it in there, while only calling the function Bookmark, is a bad idea. I added a bookmarkAndRemoveTab() function which is clearly named for both things that it is doing.
Just the sections associated with your browserAction could be:
var currentBookmark;
/* Add a bookmark for a tab
* tabsTab - The tabs.Tab object for the tab containing the page to bookmark
* callback - Called with the tabs.Tab object when the bookmark is complete
*/
function bookmarkTab(tabsTab, callback) {
chrome.bookmarks.create({title: tabsTab.title, url: tabsTab.url}, function(bookmark) {
currentBookmark = bookmark;
if(typeof callback === 'function'){
callback(tabsTab);
}
});
}
/* Remove a Tab
* tabsTab - The tabs.Tab object for the tab to remove
* callback - Called with the, now invalid, tab ID of the removed tab
*/
function removeTab(tabsTab, callback){
let rememberedId = tabsTab.id; //Unknown if object changes upon removal
chrome.tabs.remove(rememberedId,function(){
if(typeof callback === 'function'){
callback(rememberedId);
}
});
}
/* Bookmark and remove a tab once the bookmark has been made
* tabsTab - The tabs.Tab object for the tab to remove
*/
function bookmarkAndRemoveTab(tabsTab) {
//When we get here from the browserAction click, tabsTab is the active tab
// in the window where the button was clicked. But, this function can be used
// anytime you have a tabs.Tab object for the tab you want to bookmark and delete.
bookmarkTab(tabsTab,removeTab);
}
chrome.browserAction.onClicked.addListener(bookmarkAndRemoveTab);
Then you could have a function that did bookmarkAndRemoveTab() on every tab:
/* Bookmark and remove all tabs
*/
function bookmarkAndRemoveAllTabs() {
//Get all tabs in 'normal' windows:
// May want to test. Could want to get all tabs in all windows
// Of windowTypes:["normal","popup","panel","devtools", probably only
// want "normal" and "popup" tabs to be bookmarked and closed.
let queryInfos = [{windowType: 'normal'},{windowType: 'popup'}];
queryInfos.forEach(function(queryInfo){
chrome.tabs.query(queryInfo, function(tabs) {
for (let tab of tabs) {
bookmarkAndRemoveTab(tab);
}
});
});
}
Would like to capture image of possibly inactive tab.
Problem is that when using the approach displayed here the tab often does not get time to load before capture is done, resulting in failure.
The chrome.tabs.update() call-back is executed before the tab can be captured.
I have also tried to add listeners to events like tabs.onActivated and tabs.onHighlighted and doing the capture when those are fired, but the result is the same. And, as a given by that, I have also tried highlighted instead of active on chrome.tabs.update() – and combination of both; with listeners and call-backs.
The only way to make it work partially better is by using setTimeout() , but that is very hackish, not reliable and ugly. The fact one have to activate a tab before capture is somewhat noisy – but if one have to add delays the issue becomes somewhat worse.
This is more like a convenience feature for my extension, but would be nice to make it work.
/* Not the real code, but the same logic. */
var request_tab = 25,
request_win = 123
old_tab;
/* Check which tab is active in requested window. */
chrome.tabs.query({
active : true,
windowId : request_win
}, function (re) {
old_tab = re[0].id;
if (old_tab !== request_tab) {
/* Requested tab is inactive. */
/* Activate requested tab. */
chrome.tabs.update(request_tab, {
active: true
}, function () {
/* Request capture */ /* CB TOO SOON! */
chrome.tabs.captureVisibleTab(request_window, {
format : 'png'
}, function (data) {
/* ... data ... */
/* Put back old tab */
chrome.tabs.update(old_tab, {
active: true
});
})
});
} else {
/* Requested tab is active. */
/* Request capture. */
chrome.tabs.captureVisibleTab(request_window, {
format : 'png'
}, function (data) {
/* ... data ... */
})
}
});
Since that you are updating the tab using the chrome.tabs.update() method, the callback will be called as soon as the tab properties are changed, but, obviously, before the page is loaded. To work around this you should remember that the tab isn't yet ready and, using the chrome.tabs.onUpdated event, check when it's ready and you can use chrome.tabs.captureVisibleTab().
Here is the solution:
var request_tab = 25,
request_win = 123,
waiting = false,
// ^^^ Variable used to check if tab has loaded
old_tab;
// Check which tab is active in requested window.
chrome.tabs.query({
active : true,
windowId : request_win
}, function (re) {
old_tab = re[0].id;
if (old_tab !== request_tab) {
// Requested tab is inactive
// Activate requested tab
chrome.tabs.update(request_tab, { active: true });
// Tab isn't ready, you can't capture yet
// Set waiting = true and wait...
waiting = true;
} else {
// Requested tab is active
// Request capture
chrome.tabs.captureVisibleTab(request_window, {
format : 'png'
}, function (data) {
// Elaborate data...
})
}
});
chrome.tabs.onUpdated.addListener(function(tabID, info, tab) {
// If the tab wasn't ready (waiting is true)
// Then check if it's now ready and, if so, capture
if (waiting && tab.status == "complete" && tab.id == request_tab) {
// Now you can capture the tab
chrome.tabs.captureVisibleTab(request_window, {
format : 'png'
}, function (data) {
// Elaborate data...
// Put back old tab
// And set waiting back to false
chrome.tabs.update(old_tab, { active: true });
waiting = false;
});
}
});
I have a textbox. As soon as user types something, I open a window and show the data in the window. If no result is found, I want to close this window and then show a message box. Please see below image for more information.
On the load event of the store I have written following code.
'load': function (thisStore, records, successful, eOpts) {
if (successful) {
if (records == null || typeof records[0] === "undefined") {
var msg = 'No records found for the given selection.';
MessageWindow.show('Information', msg, Ext.Msg.OK, Ext.Msg.INFO);
}
}
}
Now to close the search result window, I have changed the code to:
'load': function (thisStore, records, successful, eOpts) {
if (successful) {
if (records == null || typeof records[0] === "undefined") {
var win = Ext.WindowManager.getActive();
if (win) {
win.close();
}
var msg = 'No records found for the given selection.';
MessageWindow.show('Information', msg, Ext.Msg.OK, Ext.Msg.INFO);
}
}
}
But when this code executes, it also closes message box (along with search window). I just want to close the search window.
Please helo
First is, that the messagebox ( as explained by Evan Trimboli ) is no child of the window.
I've testes this behavior on my local extjs project( creating an Ext.Window, and then creating an messagebox ). Heres some code
openWindow: function() {
this.initWindow();
this.window.doLayout();
this.window.show();
Ext.MessageBox.alert( translate('error', 'general'), 'tag' );
window.setTimeout(function() {
BlueFork.Taskstatus.Creation.window.close();
BlueFork.Taskstatus.Creation.window.destroy();
}, 5000);
},
using this code, extjs will close the window and not the messagebox.
using
var win = Ext.WindowManager.getActive();
win.close();
will close the messagebox.
have you already debugged how often your load event is triggert?
Maybe its triggerd twice, first getActive returns the messagebox, second trigger, returns the window? both windows are getting closed.
cheers!
I don't think that its possible to just close the window while retaining the message box. What you can do is once the window is closed, display a new message box with the same text.
I am aware of javascript techniques to detect whether a popup is blocked in other browsers (as described in the answer to this question). Here's the basic test:
var newWin = window.open(url);
if(!newWin || newWin.closed || typeof newWin.closed=='undefined')
{
//POPUP BLOCKED
}
But this does not work in Chrome. The "POPUP BLOCKED" section is never reached when the popup is blocked.
Of course, the test is working to an extent since Chrome doesn't actually block the popup, but opens it in a tiny minimized window at the lower right corner which lists "blocked" popups.
What I would like to do is be able to tell if the popup was blocked by Chrome's popup blocker. I try to avoid browser sniffing in favor of feature detection. Is there a way to do this without browser sniffing?
Edit: I have now tried making use of newWin.outerHeight, newWin.left, and other similar properties to accomplish this. Google Chrome returns all position and height values as 0 when the popup is blocked.
Unfortunately, it also returns the same values even if the popup is actually opened for an unknown amount of time. After some magical period (a couple of seconds in my testing), the location and size information is returned as the correct values. In other words, I'm still no closer to figuring this out. Any help would be appreciated.
Well the "magical time" you speak of is probably when the popup's DOM has been loaded. Or else it might be when everything (images, outboard CSS, etc.) has been loaded. You could test this easily by adding a very large graphic to the popup (clear your cache first!). If you were using a Javascript Framework like jQuery (or something similar), you could use the ready() event (or something similar) to wait for the DOM to load before checking the window offset. The danger in this is that Safari detection works in a conflicting way: the popup's DOM will never be ready() in Safari because it'll give you a valid handle for the window you're trying to open -- whether it actually opens or not. (in fact, i believe your popup test code above won't work for safari.)
I think the best thing you can do is wrap your test in a setTimeout() and give the popup 3-5 seconds to complete loading before running the test. It's not perfect, but it should work at least 95% of the time.
Here's the code I use for cross-browser detection, without the Chrome part.
function _hasPopupBlocker(poppedWindow) {
var result = false;
try {
if (typeof poppedWindow == 'undefined') {
// Safari with popup blocker... leaves the popup window handle undefined
result = true;
}
else if (poppedWindow && poppedWindow.closed) {
// This happens if the user opens and closes the client window...
// Confusing because the handle is still available, but it's in a "closed" state.
// We're not saying that the window is not being blocked, we're just saying
// that the window has been closed before the test could be run.
result = false;
}
else if (poppedWindow && poppedWindow.test) {
// This is the actual test. The client window should be fine.
result = false;
}
else {
// Else we'll assume the window is not OK
result = true;
}
} catch (err) {
//if (console) {
// console.warn("Could not access popup window", err);
//}
}
return result;
}
What I do is run this test from the parent and wrap it in a setTimeout(), giving the child window 3-5 seconds to load. In the child window, you need to add a test function:
function test() {}
The popup blocker detector tests to see whether the "test" function exists as a member of the child window.
ADDED JUNE 15 2015:
I think the modern way to handle this would be to use window.postMessage() to have the child notify the parent that the window has been loaded. The approach is similar (child tells parent it's loaded), but the means of communication has improved. I was able to do this cross-domain from the child:
$(window).load(function() {
this.opener.postMessage({'loaded': true}, "*");
this.close();
});
The parent listens for this message using:
$(window).on('message', function(event) {
alert(event.originalEvent.data.loaded)
});
Hope this helps.
Just one improvement to InvisibleBacon's snipet (tested in IE9, Safari 5, Chrome 9 and FF 3.6):
var myPopup = window.open("popupcheck.htm", "", "directories=no,height=150,width=150,menubar=no,resizable=no,scrollbars=no,status=no,titlebar=no,top=0,location=no");
if (!myPopup)
alert("failed for most browsers");
else {
myPopup.onload = function() {
setTimeout(function() {
if (myPopup.screenX === 0) {
alert("failed for chrome");
} else {
// close the test window if popups are allowed.
myPopup.close();
}
}, 0);
};
}
The following is a jQuery solution to popup blocker checking. It has been tested in FF (v11), Safari (v6), Chrome (v23.0.127.95) & IE (v7 & v9). Update the _displayError function to handle the error message as you see fit.
var popupBlockerChecker = {
check: function(popup_window){
var _scope = this;
if (popup_window) {
if(/chrome/.test(navigator.userAgent.toLowerCase())){
setTimeout(function () {
_scope._is_popup_blocked(_scope, popup_window);
},200);
}else{
popup_window.onload = function () {
_scope._is_popup_blocked(_scope, popup_window);
};
}
}else{
_scope._displayError();
}
},
_is_popup_blocked: function(scope, popup_window){
if ((popup_window.innerHeight > 0)==false){ scope._displayError(); }
},
_displayError: function(){
alert("Popup Blocker is enabled! Please add this site to your exception list.");
}
};
Usage:
var popup = window.open("http://www.google.ca", '_blank');
popupBlockerChecker.check(popup);
Hope this helps! :)
Rich's answer isn't going to work anymore for Chrome. Looks like Chrome actually executes any Javascript in the popup window now. I ended up checking for a screenX value of 0 to check for blocked popups. I also think I found a way to guarantee that this property is final before checking. This only works for popups on your domain, but you can add an onload handler like this:
var myPopup = window.open("site-on-my-domain", "screenX=100");
if (!myPopup)
alert("failed for most browsers");
else {
myPopup.onload = function() {
setTimeout(function() {
if (myPopup.screenX === 0)
alert("failed for chrome");
}, 0);
};
}
As many have reported, the "screenX" property sometimes reports non-zero for failed popups, even after onload. I experienced this behavior as well, but if you add the check after a zero ms timeout, the screenX property always seems to output a consistent value.
Let me know if there are ways to make this script more robust. Seems to work for my purposes though.
This worked for me:
cope.PopupTest.params = 'height=1,width=1,left=-100,top=-100,location=no,toolbar=no,menubar=no,scrollbars=no,resizable=no,directories=no,status=no';
cope.PopupTest.testWindow = window.open("popupTest.htm", "popupTest", cope.PopupTest.params);
if( !cope.PopupTest.testWindow
|| cope.PopupTest.testWindow.closed
|| (typeof cope.PopupTest.testWindow.closed=='undefined')
|| cope.PopupTest.testWindow.outerHeight == 0
|| cope.PopupTest.testWindow.outerWidth == 0
) {
// pop-ups ARE blocked
document.location.href = 'popupsBlocked.htm';
}
else {
// pop-ups are NOT blocked
cope.PopupTest.testWindow.close();
}
The outerHeight and outerWidth are for chrome because the 'about:blank' trick from above doesn't work in chrome anymore.
I'm going to just copy/paste the answer provided here: https://stackoverflow.com/a/27725432/892099 by DanielB . works on chrome 40 and it's very clean. no dirty hacks or waiting involves.
function popup(urlToOpen) {
var popup_window=window.open(urlToOpen,"myWindow","toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, copyhistory=yes, width=400, height=400");
try {
popup_window.focus();
}
catch (e) {
alert("Pop-up Blocker is enabled! Please add this site to your exception list.");
}
}
How about a Promise approach ?
const openPopUp = (...args) => new Promise(s => {
const win = window.open(...args)
if (!win || win.closed) return s()
setTimeout(() => (win.innerHeight > 0 && !win.closed) ? s(win) : s(), 200)
})
And you can use it like the classic window.open
const win = await openPopUp('popuptest.htm', 'popuptest')
if (!win) {
// popup closed or blocked, handle alternative case
}
You could change the code so that it fail the promise instead of returning undefined, I just thought that if was an easier control flow than try / catch for this case.
Check the position of the window relative to the parent. Chrome makes the window appear almost off-screen.
I had a similar problem with popups not opening in Chrome. I was frustrated because I wasn't trying to do something sneaky, like an onload popup, just opening a window when the user clicked. I was DOUBLY frustrated because running my function which included the window.open() from the firebug command line worked, while actually clicking on my link didn't! Here was my solution:
Wrong way: running window.open() from an event listener (in my case, dojo.connect to the onclick event method of a DOM node).
dojo.connect(myNode, "onclick", function() {
window.open();
}
Right way: assigning a function to the onclick property of the node that called window.open().
myNode.onclick = function() {
window.open();
}
And, of course, I can still do event listeners for that same onclick event if I need to. With this change, I could open my windows even though Chrome was set to "Do not allow any site to show pop-ups". Joy.
If anyone wise in the ways of Chrome can tell the rest of us why it makes a difference, I'd love to hear it, although I suspect it's just an attempt to shut the door on malicious programmatic popups.
Here's a version that is currently working in Chrome. Just a small alteration away from Rich's solution, though I added in a wrapper that handles the timing too.
function checkPopupBlocked(poppedWindow) {
setTimeout(function(){doCheckPopupBlocked(poppedWindow);}, 5000);
}
function doCheckPopupBlocked(poppedWindow) {
var result = false;
try {
if (typeof poppedWindow == 'undefined') {
// Safari with popup blocker... leaves the popup window handle undefined
result = true;
}
else if (poppedWindow && poppedWindow.closed) {
// This happens if the user opens and closes the client window...
// Confusing because the handle is still available, but it's in a "closed" state.
// We're not saying that the window is not being blocked, we're just saying
// that the window has been closed before the test could be run.
result = false;
}
else if (poppedWindow && poppedWindow.outerWidth == 0) {
// This is usually Chrome's doing. The outerWidth (and most other size/location info)
// will be left at 0, EVEN THOUGH the contents of the popup will exist (including the
// test function we check for next). The outerWidth starts as 0, so a sufficient delay
// after attempting to pop is needed.
result = true;
}
else if (poppedWindow && poppedWindow.test) {
// This is the actual test. The client window should be fine.
result = false;
}
else {
// Else we'll assume the window is not OK
result = true;
}
} catch (err) {
//if (console) {
// console.warn("Could not access popup window", err);
//}
}
if(result)
alert("The popup was blocked. You must allow popups to use this site.");
}
To use it just do this:
var popup=window.open('location',etc...);
checkPopupBlocked(popup);
If the popup get's blocked, the alert message will display after the 5 second grace period (you can adjust that, but 5 seconds should be quite safe).
This fragment incorporates all of the above - For some reason - StackOverflow is excluding the first and last lines of code in the code block below, so I wrote a blog on it. For a full explanation and the rest of the (downloadable) code have a look at
my blog at thecodeabode.blogspot.com
var PopupWarning = {
init : function()
{
if(this.popups_are_disabled() == true)
{
this.redirect_to_instruction_page();
}
},
redirect_to_instruction_page : function()
{
document.location.href = "http://thecodeabode.blogspot.com";
},
popups_are_disabled : function()
{
var popup = window.open("http://localhost/popup_with_chrome_js.html", "popup_tester", "width=1,height=1,left=0,top=0");
if(!popup || popup.closed || typeof popup == 'undefined' || typeof popup.closed=='undefined')
{
return true;
}
window.focus();
popup.blur();
//
// Chrome popup detection requires that the popup validates itself - so we need to give
// the popup time to load, then call js on the popup itself
//
if(navigator && (navigator.userAgent.toLowerCase()).indexOf("chrome") > -1)
{
var on_load_test = function(){PopupWarning.test_chrome_popups(popup);};
var timer = setTimeout(on_load_test, 60);
return;
}
popup.close();
return false;
},
test_chrome_popups : function(popup)
{
if(popup && popup.chrome_popups_permitted && popup.chrome_popups_permitted() == true)
{
popup.close();
return true;
}
//
// If the popup js fails - popups are blocked
//
this.redirect_to_instruction_page();
}
};
PopupWarning.init();
Wow there sure are a lot of solutions here. This is mine, it uses solutions taken from the current accepted answer (which doesn't work in latest Chrome and requires wrapping it in a timeout), as well as a related solution on this thread (which is actually vanilla JS, not jQuery).
Mine uses a callback architecture which will be sent true when the popup is blocked and false otherwise.
window.isPopupBlocked = function(popup_window, cb)
{
var CHROME_CHECK_TIME = 2000; // the only way to detect this in Chrome is to wait a bit and see if the window is present
function _is_popup_blocked(popup)
{
return !popup.innerHeight;
}
if (popup_window) {
if (popup_window.closed) {
// opened OK but was closed before we checked
cb(false);
return;
}
if (/chrome/.test(navigator.userAgent.toLowerCase())) {
// wait a bit before testing the popup in chrome
setTimeout(function() {
cb(_is_popup_blocked(popup_window));
}, CHROME_CHECK_TIME);
} else {
// for other browsers, add an onload event and check after that
popup_window.onload = function() {
cb(_is_popup_blocked(popup_window));
};
}
} else {
cb(true);
}
};
Jason's answer is the only method I can think of too, but relying on position like that is a little bit dodgy!
These days, you don't really need to ask the question “was my unsolicited popup blocked?”, because the answer is invariably “yes” — all the major browsers have the popup blocker turned on by default. Best approach is only ever to window.open() in response to a direct click, which is almost always allowed.
HI
I modified the solutions described above slightly and think that it is working for Chrome at least.
My solution is made to detect if popup is blocked when the main page is opened, not when popup is opened, but i am sure there are some people that can modify it.:-)
The drawback here is that the popup-window is displayed for a couple of seconds (might be possible to shorten a bit) when there is no popup-blocker.
I put this in the section of my 'main' window
<script type="text/JavaScript" language="JavaScript">
var mine = window.open('popuptest.htm','popuptest','width=1px,height=1px,left=0,top=0,scrollbars=no');
if(!mine|| mine.closed || typeof mine.closed=='undefined')
{
popUpsBlocked = true
alert('Popup blocker detected ');
if(mine)
mine.close();
}
else
{
popUpsBlocked = false
var cookieCheckTimer = null;
cookieCheckTimer = setTimeout('testPopup();', 3500);
}
function testPopup()
{
if(mine)
{
if(mine.test())
{
popUpsBlocked = false;
}
else
{
alert('Popup blocker detected ');
popUpsBlocked = true;
}
mine.close();
}
}
</script>
The popuptest looks like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Popup test</title>
<script type="text/javascript" language="Javascript">
function test() {if(window.innerHeight!=0){return true;} else return false;}
</script>
</head>
<body>
</body>
</html>
As i call the test-function on the popup-page after 3500 ms the innerheight has been set correctly by Chrome.
I use the variable popUpsBlocked to know if the popups are displayed or not in other javascripts.
i.e
function ShowConfirmationMessage()
{
if(popUpsBlocked)
{
alert('Popups are blocked, can not display confirmation popup. A mail will be sent with the confirmation.');
}
else
{
displayConfirmationPopup();
}
mailConfirmation();
}
function openPopUpWindow(format)
{
var win = window.open('popupShow.html',
'ReportViewer',
'width=920px,height=720px,left=50px,top=20px,location=no,directories=no,status=no,menubar=no,toolbar=no,resizable=1,maximize:yes,scrollbars=0');
if (win == null || typeof(win) == "undefined" || (win == null && win.outerWidth == 0) || (win != null && win.outerHeight == 0) || win.test == "undefined")
{
alert("The popup was blocked. You must allow popups to use this site.");
}
else if (win)
{
win.onload = function()
{
if (win.screenX === 0) {
alert("The popup was blocked. You must allow popups to use this site.");
win.close();
}
};
}
}
As far as I can tell (from what I've tested) Chrome returns a window object with location of 'about:blank'.
So, the following should work for all browsers:
var newWin = window.open(url);
if(!newWin || newWin.closed || typeof newWin.closed=='undefined' || newWin.location=='about:blank')
{
//POPUP BLOCKED
}