« 0.01 nanoseconds | Main| Dear lazyweb, what UPS should I buy? »

Every time you use window.open, God kills a kitten

Category javascript
I was booking some travel recently and couldn't select a date on one of the travel sites because they were using window.open, so my browser blocked it as a popup. They weren't trying to sell me anything I wasn't planning on buying anyway, they weren't trying to install malware, it was just a date picker. But I couldn't use it without modifying my browser's security settings (or switching to a less secure browser). It's 2008... window.open? Seriously?

So, for any of you who still use window.open to display a date picker, or a comment form (see Andrew's blog as an example of the right way to do this), or... well, anything else... and the only reason is because you asked someone for an alternative and were simply told, "duh, use a positioned div" - but not told how - hopefully this post will show you how easy a better alternative can be.

To display something that looks like a popup but really isn't, all you really need is:
  • a div that hides until needed and has a higher z-index than the rest of the page
  • code to show and hide that div, and position it where you want it to display
What's a z-index? If you use layers in your Notes client development, you're already familiar with this concept: think of your user interface as existing in three-dimensional space. The x axis is horizontal, the y axis is vertical, and the z axis measures the conceptual distance between the "front" and the "back" of what's available to the user. Unlike x and y, however, which are typically measured in pixels or some other unit associated with the available width of the user's screen, the z index is theoretically limitless, and is simply identified by an integer. So on a web page (or a Notes layer), anything with a higher z-index is "closer" to the user; anything with a lower z-index is "further" away, and therefore displays "behind" the higher elements. Mozilla.org has a great tutorial on this concept if you want to dig deeper, but for the moment, just remember that the element with the highest z-index displays on top of everything else.

Here is an example (also available for download) of using a "positioned div" to display additional content that you might otherwise launch in a separate window with window.open. To enable this, start out by creating a div somewhere in your page markup, give it an id and a class (in my example, I'm using "pseudoWindow" as the id and "layerwindow" as the class), and define some CSS for that class:

div.layerwindow {
  background-color: #eeeeee;
  border: 3px solid #abcdef;
  display: none;
  overflow: scroll;
  padding: 3px;
  position: absolute;
  z-index: 2;
}


This causes that div to hide initially, as well as defining how it will look when it's no longer hidden.

Next, you'll need some JavaScript code for showing the div, positioning it, and hiding it again:

var LayerWindow = function(){
  var getViewPort = function(){
    var viewPortWidth;
    var viewPortHeight;
    var vYscroll;
    if (window.innerWidth) {
      viewPortWidth = window.innerWidth;
      viewPortHeight = window.innerHeight;
      vYscroll = window.pageYOffset;
    } else if (document.documentElement && document.documentElement.clientWidth) {
      viewPortWidth = document.documentElement.clientWidth;
      viewPortHeight = document.documentElement.clientHeight;
      vYscroll = document.documentElement.scrollTop;
    } else {
      var bodyTag = document.getElementsByTagName('body')[0];
      viewPortWidth = bodyTag.clientWidth;
      viewPortHeight = bodyTag.clientHeight;
      vYscroll = document.body.scrollTop;
    }
    return {
      w: viewPortWidth,
      h: viewPortHeight,
      yScroll: vYscroll
    };
  };

  return {
    open: function(id, options){
      var vp = getViewPort();
      if (document.getElementById(id)) {
        overlayer = document.getElementById(id);
        overlayer.style.width = options.w + 'px';
        overlayer.style.height = options.h + 'px';
        overlayer.style.top = (vp.yScroll + parseInt(vp.h / 2, 10) - parseInt(options.h / 2, 10)) + 'px';
        overlayer.style.left = (parseInt(vp.w / 2, 10) - parseInt(options.w / 2, 10)) + 'px';
        overlayer.style.display = 'block';
      }
      return false;
    },
    close: function(id){
      document.getElementById(id).style.display = 'none';
    }
  };
}();


Finally, update whatever would be triggering the window.open to instead just display the hidden div:

<a href="#" onclick="return LayerWindow.open('pseudoWindow', {'w':300, 'h':250});">Open layer window</a>



There are a multitude of ways to add further elegance and functionality to this (and nearly every widely used JavaScript framework has their own implementation) - for example, loading the content of the hidden div via AJAX prior to displaying it instead of using pre-populated content - but hopefully this will give you a head start toward eliminating your own use (if any) of window.open... please, think of the kittens.

(cross-posted at BleedYellow)

Comments

Gravatar Image1 - Well done. You've actually made me laugh out like a mad man twice today. My co-workers may soon committ me.
That's a very elequent way of providing popup functionality.

Gravatar Image2 - (pwned)

Touché, Rob. The code-to-html tool I've been using doesn't preserve indenting, so I've updated this post to add all the necessary &nbsp;'s.

Gravatar Image3 - It's 2008. Why can't we display code on the Internet with proper indenting? This is especially true in comments.

<code>
main{
printf("Hello, its 2008!";
}
</code>

Man, that annoying ... and hard to read. I'd take formatting over graphical emoticons any day.

Otherwise, great article.

Peace,

Rob:-]

Gravatar Image4 - ...so that's how all these moths are getting in...

Gravatar Image5 - "Every time God closes a door, he opens a window"
Maria VonTrapp

Post A Comment

:-D:-o:-p:-x:-(:-):-\:angry::cool::cry::emb::grin::huh::laugh::lips::rolleyes:;-)

Contact Me

Hire Me

What the Quote?

"...but if you call it 'Operation Butter Hammock', nobody knows what you're talking about."

Adam Slagle

"Naked butts isn't art."

Anna Byrd ( age 9)

"Yeah, 'cause we gotta keep our Turkey peeps happy."

Steven Rodgers

Elsewhere

Assorted Linkage

Apparel

Lotus Rocks

I write the code that makes the young girls cry

ClustrMap