TJKDesign: Home Page

ez-css Putting the 'less' in table-less layouts. css-101 logo
Bookmark this article at these sites:

Opening Popup Windows with no extra markup.

Popup windows are a hot topic among the web designers' community. People who are new to the trade struggle to customize them while seasoned web designers argue about their very use.

ImportantIt should be noted that these arguments are not limited to usability and accessibility, but often involve a choice of DTD Declarations.

This article does not discuss what designers should do regarding popup windows, markup and such. It is just about adding a tool to the box. It's up to you whether or not you find it useful.

Doing the Windows

A little recap about the most common ways to popup Windows (with their caveat):

<a href="http://validator.w3.org/" target="_blank">W3C Validator</a>
  • The user may have no choice other than to open the page in a new window.
  • The target value does not name the window and thus this link will spawn multiple popups.
  • Depending on the DTD used, target** may not be allowed here.
  • There is no separation of structure and behavior.

** As a side note, using a Custom DTD Declaration is a solution that "passes" the Markup Validator, but - as far as I know - not the CSS Validator.
<a href="#" onclick="window.open("http://validator.w3.org");return false;">W3C Validator</a>
or
<a href="Javascript:window.open('http://validator.w3.org'); void(0)">W3C Validator</a>
  • The user may have no choice other than to open the page in a new window.
  • The user cannot open the link in a new tab or window (using browser's built-in features).
  • The window is not named and thus this link will spawn multiple popups.
  • The page is not accessible in JS-challenged UAs or if Javascript is disabled.
  • There is no separation of structure and behavior.
<a href="http://validator.w3.org" onclick="window.open(this.href);return false;" onkeypress="window.open(this.href);return false;">W3C Validator</a>
  • If there is Javascript support, the user may have no choice other than to open the page in a new window.
  • The window is not named and thus this link will spawn multiple popups.
  • The redundant mechanism (onclick/onkeypress) is part of the WAI guidelines (checkpoint 9.3), but it will make tabbing navigation impossible in some browsers.
  • There is no separation of structure and behavior.

Standards-Compliant Popups

Newer - better - methods rely on scripts that parse the document and apply the behavior to the anchors accordingly. These solutions use attributes of the A element (target, class or rel) as a "hook" (a marker).

Most of these methods style the links too (using some icons like this one Icon for popup Windows for example), to warn the user about the particular behavior attached to the links.

Important When a class is used, it is important that the author makes sure the links are not styled in JS-challenged browsers or if Javascript is disabled, otherwise it could be confusing for the user.

The 2 scripts below go one step further, setting the markup free of anchor's attributes.

Popups for External Links:

function TJKpop(){ // v1.0 | www.TJKDesign.com
  var e = document.getElementById('wrapper');
  if (e){
    var a=e.getElementsByTagName('a');
    for (var i=0;i<a.length;i++){
    if (a[i].getAttribute('href') != null && a[i].getAttribute('href').indexOf("://") >= 0 && a[i].getAttribute('href').toUpperCase().indexOf(document.domain.toUpperCase()) == -1){
        a[i].className+=a[i].className?' outlink':'outlink';
        a[i].title+=' (opens in new window)';
        a[i].onclick=function(){newWin=window.open(this.href,'TJKWin');if(window.focus){newWin.focus()} return false;}
//      a[i].onkeypress=function(){newWin=window.open(this.href,'TJKWin');if(window.focus){newWin.focus()} return false;}
      }
    }
  }
}
window.onload = function(){if(document.getElementById) TJKpop();}

What does this script do?

  1. using the href value of the anchors, it finds the "external" links inside a given element.
  2. it applies a style to the links (applies a class or appends a class name to an existing class attribute's value)
  3. it attaches a title attribute to the links (or appends a string to the existing title attribute's value)
  4. it applies the behavior to each one of these links (through the onclick event handler)

Important You may use any available element you want. For example, if your markup does not include a "wrapper", you may use "body" instead; in this case, you'd use the following:
var e = document.getElementsByTagName('body')[0];

What are the main advantages of this method?

  • It is hook-free; it assures complete separation of structure and behavior
  • In JS-challenged browsers, or if Javascript is disabled, there is no style applied that would differentiate these links from others
  • It does not spawn multiple windows

For those interested in how it works:

function TJKpop(){ var e = document.getElementById('wrapper');
Find "wrapper" in the document and set it to "e".
if (e){
If "e" contains an object reference, it is worth looking past this opening bracket.
var a=e.getElementsByTagName('a');
Creates an array ("a") of all As found in "wrapper".
for (var i=0;i<a.length;i++){
We are using a for loop to go through all the anchors. Between the parentheses, we have an Initialization statement (we could have used "var i=1;i<=a.length;i++" as well), a Condition (as long as "i" is less than the number of elements in the array, the statements inside the for loop are executed) and an Updation statement (that adds 1 to the variable "i").
if (a[i].getAttribute('href') != null && a[i].getAttribute('href').indexOf("://") >= 0 && a[i].getAttribute('href').toUpperCase().indexOf(document.domain.toUpperCase()) == -1){
We cannot assume to find a "href" attribute with every A element, so we start by checking for this first. Then we use getAttribute() to get the value of the named attribute on the current node to verify that this value contains the "://" string.
That would be enough if the value returned was always a perfect match with the value from the source code; unfortunatley - in some browsers - the value returned is always an absolute path. This is why we need to check for the presence of another string, to make sure we exclude links that point to our own domain.
a[i].className+=a[i].className?' outlink':'outlink';
The DOM attribute className is used to set a class for that node; if there are previously existing current classes then it appends the string " outlink".
a[i].title+=' (opens in new window)';
We do the same with title. If there is a previously existing title, it appends the string " (open in new window)" .
a[i].onclick=function(){newWin=window.open(this.href,'TJKWin');if(window.focus){newWin.focus()} return false;}
Now we use the onclick event handler to attach the behavior to the links. "this.href" passes the href value found in the markup to the function; the string "TJKWin" is the name we give to the popup so these links do not spawn multiple windows. Then, by switching "focus" between the parent window and the popup we prevent the popup from disappearing behind the opener if the user selects another link. To finish, we use "return false" to make sure that Javascript enabled browsers ignore the href value in the markup.

Important To prevent issues with popup blockers, you may want to test for window.open before running the script or you can simply replace the onclick statement with the following:
a[i].target='_blank'; (this will spawn multiple windows though...)
// a[i].onkeypress=function(){newWin=window.open(this.href,'TJKWin');if(window.focus){newWin.focus()} return false;}
This line is commented and thus ignored. The WAI guidelines (checkpoint 9.3) says one should implement a redundant mechanism (i.e., onclick/onkeypress), but - as far as I know - this recommendation makes tabbing navigation impossible in Gecko browsers and Opera.
Read the end of this article to find out about a workaround...
}}}}
Time to close all the brackets we've opened.
window.onload = function(){if(document.getElementById) TJKpop();}
document.getElementsById is used here to make sure the method is available and that we can run the script.
We're using the window.onload handler to call the function, but there are better solutions (see Simon Willison's solution).

Custom Popups for specific links:

Because the script looks for links within a particular element, it is easy to take advantage of this and mark up your pages accordingly. For example, one could use a DIVision to contain thumbnail images linked to larger ones and let the script do the work (applying the behavior only to the thumbnails inside that DIV).

Another possibility is to use this method to target files inside a specific folder; to use another example involving thumbnails and larger images, imagine using a folder's name ("bigPic" for example)...

This would be the markup:

<a href="img/bigpic/superman.jpg" title="Click to see larger image"><img src="img/superman.jpg" alt="Superman" /></a>

To create this thumbnail:
Superman

This would be the script to create custom popup windows only for files within this directory:

function TJKpopFolder(){ // v1.0 | www.TJKDesign.com
  var e = document.getElementById('wrapper');
  if (e){
    var a=e.getElementsByTagName('a');
    for (var i=0;i<a.length;i++){
    if (a[i].getAttribute('href') != null && a[i].getAttribute('href').toUpperCase().indexOf("BIGPIC") >= 0){
        a[i].className+=a[i].className?' popup':'popup';
        a[i].title+=' (opens in new window)';
        a[i].onclick=function(){newWin=window.open(this.href,'TJKWin','location=no,menubar=no,status=no,toolbar=no,width=755,height=200');if(window.focus){newWin.focus()} return false;}
      }
    }
  }
}
window.onload = function(){if(document.getElementById) TJKpopFolder();}

Let's not forget PDF files:

function TJKpopAppPdf(){ // v1.0 | www.TJKDesign.com
var zA=document.getElementsByTagName("a"); for (var i=0;i<zA.length;i++){ // if the type value contains "application/pdf" or if the href value contains "PDF" or if the file is in a "PDF" folder then we have a winner if (zA[i].getAttribute("href") != null && (zA[i].getAttribute("type") == "application/pdf" || zA[i].getAttribute("href").toUpperCase().indexOf(".PDF") >= 0 || zA[i].getAttribute("href").toUpperCase().indexOf("PDF/") >= 0)){ zA[i].title+=" (opens in new window)"; zA[i].className+=zA[i].className?" pdfFile":"pdfFile"; // This spawns multiple windows, but works fine with popup blockers // zA[i].target="_blank"; // This opens a unique window and brings it in front of the opener each time the user clicks on the link // Note that the new window opens without a toolbar. This is to avoid further conusion for the visitor zA[i].onclick=function() {newWin=window.open(this.href,"TJKWin","toolbar=no");if( window.focus){newWin.focus()} return false;} } } }

Et voilà!

Important This article is about selecting a particular set of links inside a document without extra markup, it does not take into consideration all issues related to popups.

Important Special thanks to Adam Smith, Ben Curtis, Andrew Krespanis and Gary White for their valuable feedback.

For further reading visit the following URIs:

On the subject of popups:

On solutions to open popups:

On the subject of events: