Avoiding Popup Blockers in Your Web Apps

Tim Heuer laments the growing number of otherwise reputable web sites that instruct their flocks to disable popup blockers in order to complete some task within that web site.

Having stumbled into popup blocker hell myself only a few months ago with the Windows Live Contacts Control I can certainly attest to how easy it is for the blissfully ignorant (me!) to get snared by a popup blocker.  However, I agree with Tim that popup blockers provide a valuable service and disabling them should not be part of anyone's standard operating procedure, especially when the target audience is the non-technical consumer.

When a web site asks you to lower your shields in order to complete an online order or do some other mundane task, it seems a little odd.  But when you consider how easy it is to avoid tripping popup blocker alarms, asking the consumer to disable them is just plain sad.

The purpose of a popup blocker is to block popup "spam" - windows that pop up without the user's consent or request. Some of the more irritating sites throw up dozens of popup windows all at once, leaving the viewer lost in a forest of trash.  Some toss up a popup window when you leave their site. 

Why do the popup spammers do it?  To artificially inflate their page view counts. A browser popup window showing a particular HTML page counts as a page view, whether the consumer is looking at it or not.  Most banner advertisers consider that action a form of click fraud, and at a minimum grounds for termination of your ad serving account.

Here's the deal:  Popup blockers in web browsers don't block all popup windows.   A popup window that is opened in direct response to a user action, such as a button click, will not trigger a popup blocker alarm.  If you do a window.open() in a JavaScript button click event, the window opens without issue.  If the same code executes in a timer event, it will trip a popup blocker alarm.  Why?  Because a timer event is not the direct result of a user action. 

The same goes for page loads, page unloads, XMLHttpRequest events, or anything else that is not a direct user action implying consent.  Mouse clicks are user actions.  I haven't tried this, but I'd be willing to bet that mouse hovers over an HTML element do not qualify as a user action.  If anything, waiving the mouse around on the screen indicates indecision!

Different popup blockers have different degrees of sophistication in how they determine whether a window.open() is happening in response to a user action.  Some may tolerate a call to window.open() being made in a subroutine called by a click event, but for best results across the board keep the window.open as close to the click event as possible.  Meaning: keep the window.open in the click event itself.

We tripped over this in the first iteration of the Windows Live Contacts Control's WriteAPI.  When a third party web app submits contact data to the Contacts control, we need to make sure the user is aware of what the third party app is doing, so we need to show the submitted data to them and ask for their approval of the action.  There isn't enough screen real estate in the control itself to display the potentially large data, so a popup window is justified.

Let's say the user clicks a button on the third party web page to send data into the contacts control.  The web app calls a method on the Contacts control, which is actually JavaScript from our library executing in the context of the third party app.  Our code sanity checks the data, then bundles it up and tosses it over the domain barrier to our code that lives inside the Contacts control iframe, hosted on a Microsoft domain.  Our code on the inside checks the data again, then opens a popup window to ask the user to review and approve the submitted data.

That's where we got caught - the JavaScript code opening the popup window was not directly tied to a user click.  In our minds it was the direct result of the user click, but in the reality of the browser there were far too many call frames and other indirections between the user action and the window.open for the popup blocker to determine a causal relationship between them.

One solution that came to mind (very briefly) was to display the confirmation popup window from our code that executes in the context of the third party web app.  This has many problems, not the least of which is that we cannot trust code that is executing in the third party domain context.  We can only trust code that executes in the context of our own domain.  Besides, given that our code would still be at least one function call removed from the actual user action event, this approach is likely to fail with less sophisticated popup blockers.

Our solution?  Display a message inside the Contacts control to inform the user that data had been received from the web app, and prompt the user to "Click here to review the data".  This solution has the downside of introducing an extra user click into the chain of events, but that click is what enables us to display the popup window from our JavaScript running in the context of our Microsoft domain without offending the popup blockers.

So, we should allow some measure of understanding for a web app that stumbles into popup blockers unexpectedly.  The web developer is only guilty of a little ignorance and lack of testing. 

But a web site that actively instructs you to disable popup blockers to use their site? This is where making ignorance into corporate policy has become easier than understanding the problem and its usually trivial solution.  Show them no mercy!