Create Pop-ups Without Dead Links

Christian Heilmann
Share

They’re the topic of many a heated discussion on the Web, and the favourite toy of evildoers who want to annoy us with "in your face" advertising — pop-up windows.

But, let’s not judge the tool by the villain who handles it. Indeed, there are times and conditions in which a pop-up can be a desirable effect; these conditions usually arise in Web applications rather than Websites, though.

One of the major drawbacks of pop-up windows is their dependency on JavaScript. Javascript is a great tool for enhancing markup to become interactive without reloading. Yet, we should be aware of its problems.

It is ridiculously easy to create a pop-up window, and there are many outdated tutorials and code generators that can lure you into thinking that a link with javascript:window.open() and the appropriate parameters is all you need to create a great pop-up.

Unlearning What You’ve Learnt

Let’s not waste time discussing why this is wrong — a lot of other articles, such as this evolt tute, and this article on A List Apart, have done that already. Let’s instead think about the problem at hand and find another way to achieve a good outcome.

For some good reason (e.g. the client will not be happy without it, you cannot refresh the main page, etc.):

  • We want a pop-up that we can control (in terms of its size, location, turning off parts of the browser chrome, etc.).
  • We want the pop-up to work for people who don’t use JavaScript. The pop-up call should simply open a new browser window (or tab).
  • Regardless of the fact that we replicate browser tasks, we want a "close window" link in the pop-up.

These are common requirements we’ve all had to fulfill during our time as interaction designers. If we analyse them, we can conclude:

  • The control of the pop-up and the close link needs JavaScript.
  • The opening of a link in a new window can be achieved in markup via the target attribute (except for XHTML strict documents, but there is a workaround).

Now we need to make sure that, in our development, the appropriate tool does only what it needs to. Let’s start with the HTML markup.

<p>This will be our demo page that  
<a href="popkid.html" target="_blank">
shows a pop-up window(where possible)</a>
and simply <a href="popkid.html">links to the  
page without a pop-up.</a></p>

That’s all we require. We don’t need any inline event handlers like onclick or onkeypress — these are JavaScript, and should be added in the JavaScript code.

Use the Force of Cleaner JavaScript

Let’s start by defining the window attributes and the text content of the "close window" link. As this link can only work with JavaScript, we’ll add it via the DOM, later.

var closeElementId='closewindow'; 
var closeLinkText='Close window';
var windowAttributes='width=310,height=400,left=0,top=0,scrollbars=no,location=no';
Change the Pages that Spawn the Pop-ups

We need a JavaScript function to turn the HTML links on our pages into pop-up links. We can achieve this easily by looping through all links and checking which ones have a target attribute. This, however, should only happen on documents that are not contained within the pop-up; hence, we check whether there is an opener document.

if(!window.opener) 
{
 var as,i,popfun
 as=document.getElementsByTagName('a');
 for (i=0;i<as.length;i++)
 {
   if(as[i].target=='_blank')
   {
     popfun=function(){
       window.open(this.href,'',windowAttributes);
       return false;
     };
     as[i].onclick=popfun;
     as[i].onkeypress=popfun;
   }
 }

Looping through all <a> elements of the document, we check if they have a target attribute set to '_blank'. If they do, we apply the window.open function when the link is clicked, or when a key is pressed while the link is focused (accessibility, as we know, also means mouse-independence). Returning false ensures that the link does not get opened in the opener document – it’s opened only in the pop-up. We know which document to open in the pop-up by reading the href attribute of the link.

If we don’t want to loop all links (this may take some time), we can just loop through special parts of the page. Say, for example, that we defined a content part with <div id="content">. In this case, all we need to do is to find the following:

as=document.getElementsByTagName('a');

We’d replace it with:

as=document.getElementById('content').getElementsByTagName('a');

Adding Extras to the Documents Inside the Pop-up

That took care of the opener pages. Now, let’s look at the pop-ups, which do have a window.opener:

} else { 
 var closep,closelink,closetext;
 closelink=document.createElement('a');
 closetext=document.createTextNode(closeLinkText);
 closelink.href='#';
 closelink.appendChild(closetext);
 closelink.onclick=function(){self.close();};
 closelink.onkeypress=function(){self.close();};
 if(document.getElementById(closeElementId))
 {
   document.getElementById(closeElementId).appendChild(closelink);  
 } else {
   closep=document.createElement('p');
   closep.id=closeElementId;
   closep.appendChild(closelink);
   document.body.insertBefore(closep,document.body.firstChild);  
 }
}

First, we create a new link element, and apply the text defined earlier as the text content. Then, to make it appear as a link, we set the href of the link to "#". To make the link close the window, we apply the function self.close() to it. This is activated when the user clicks on the link, or when a key is pressed while the link has the focus.

Now, we get greedy: we want to enable the HTML developers to define where the link should appear.

If they don’t want to define the location, we simply apply the link in its own paragraph as the first element of the document. But, to make sure we can still apply CSS to it, we give it an ID.

This ID is the one we defined earlier as closeElementId. Now, if the HTML already contains an element that has this ID, we simply add our link as a new child to this element using appendChild(). If we don’t already have an element with that ID, we create a new paragraph element and apply the ID to it. We add the link to this new paragraph, and add the paragraph as a new first child element to the body of the document using insertBefore.

Call to Action!

That’s all we need to do. The only thing left is to wrap all the above in a function, call it when the page loads, and add the whole lot to a .js include that will be added to each of the documents involved.

The onload call can be achieved in various ways. One method involves attaching an event to the window; another is simply to add a window.onload=functionname; as the last line of the .js include.

Scott Andrew wrote a handy function that attaches an onload event to the window. The benefit of this is that it adds to other onload events, rather than overwriting them. One problem with it is that it doesn’t work in Internet Explorer on Mac computers.

Download the full script and the HTML demo pages here.

Other Applications

What else could we do with this solution? One nice feature would be to give users the choice to open the pop-ups with styling, or without, as they wished. We could implement this by applying to the opener pages a form containing a checkbox, and to apply the window attributes only when the box was checked.

This setting should be stored in a cookie or the back end, as it’s rather useless if the user has to redefine it on every page.

As we normally remove the browser toolbars from a pop-up, and we cannot assume that everybody knows the keyboard shortcut to print the page, we could also add a "print this page" link that would invoke the window.print() function.

Another idea might be to flag to the user that these specific links open in pop-ups by applying an icon to them, or changing their display. But, whatever we do, let’s make sure that we do it in JavaScript and keep the HTML unharmed.