Cross-Browser, Event-based, Element Resize Detection

UPDATE: IE as of version 11 no longer supports the native resize event discussed in this article, as such, the code has been architected to use the scroll event, which continues to ensure broad cross browser operation.

DOM Elements! Y U No Resize Event?

During your coding adventures, you may have run into occasions where you wanted to know when an element in your document changed dimensions – basically the window resize event, but on regular elements. Element size changes can occur for many reasons: modifications to CSS width, height, padding, as a response to changes to a parent element’s size, and many more. Before today, you probably thought this was mere unicorn lore, an impossible feat – well buckle up folks, we’re about to throw down the gauntlet.

Eye of Newt, and Toe of Frog

The following italicized section is no longer relevant, IE removed the resize event beginning in IE11:

Before we start adding bits to our hacker’s cauldron, let’s review which browsers this will target…you might have just thought to yourself “well, all of them, right?” – but you’d be wrong. This hack is only needed for WebKit and Firefox. In a twist of fate, IE offers built-in support for resize events on regular DOM elements – I shit you not, see for yourself: http://msdn.microsoft.com/en-us/library/ie/ms536959%28v=vs.85%29.aspx.

The following italicized section is no longer relevant, the code now uses the scroll event:

Now, for the DOM alchemy! We’ll need to add to our cauldron overflow or underflow events. If you haven’t heard of them, that’s OK, because I have just the post to get you up to speed – go ahead, I’ll wait here –> Back Alley Coder: Overflow and Underflow Events.

Whew, you’re back! Now that you’re in the overflow know, you might think this hack is simply setting overflow and underflow on an element, but that doesn’t provide us with the functionality we’re after. Overflow and underflow events only fire when an element changes flow state, not each time an element changes size.

We’re going to need a few DOM elements and a couple well-placed scroll event listeners to create what I refer to as ‘sensors’. Let’s take a look at the code you’ll need to make cross-browser element resize events possible:

Resize Sensor HTML

The following HTML block is auto-appended to any element you attach a resize event to. You can only attach resize events to elements that allow children – basically, no elements declared with self-closing tags.

<div class="resize-triggers">
    <div class="expand-trigger"><div></div></div>
    <div class="contract-trigger"></div>
</div>

Resize Sensor CSS

.resize-triggers {
	visibility: hidden;
}

.resize-triggers, .resize-triggers > div, .contract-trigger:before {
  content: " ";
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  overflow: hidden;
}

.resize-triggers > div {
  background: #eee;
  overflow: auto;
}

.contract-trigger:before {
  width: 200%;
  height: 200%;
}

Resize Event Methods

The following is the JavaScript you’ll need to enable resize event listening. The first two functions are prerequisites that are used in the main addResizeListener and removeResizeListener methods.

(function(){
	var attachEvent = document.attachEvent;
	
	if (!attachEvent) {
		var requestFrame = (function(){
		  var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
					function(fn){ return window.setTimeout(fn, 20); };
		  return function(fn){ return raf(fn); };
		})();
		
		var cancelFrame = (function(){
		  var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame ||
					   window.clearTimeout;
		  return function(id){ return cancel(id); };
		})();
	
		function resetTriggers(element){
			var triggers = element.__resizeTriggers__,
				expand = triggers.firstElementChild,
				contract = triggers.lastElementChild,
				expandChild = expand.firstElementChild;
			contract.scrollLeft = contract.scrollWidth;
			contract.scrollTop = contract.scrollHeight;
			expandChild.style.width = expand.offsetWidth + 1 + 'px';
			expandChild.style.height = expand.offsetHeight + 1 + 'px';
			expand.scrollLeft = expand.scrollWidth;
			expand.scrollTop = expand.scrollHeight;
		};

		function checkTriggers(element){
		  return element.offsetWidth != element.__resizeLast__.width ||
				 element.offsetHeight != element.__resizeLast__.height;
		}
		
		function scrollListener(e){
			var element = this;
			resetTriggers(this);
			if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__);
			this.__resizeRAF__ = requestFrame(function(){
				if (checkTriggers(element)) {
					element.__resizeLast__.width = element.offsetWidth;
					element.__resizeLast__.height = element.offsetHeight;
					element.__resizeListeners__.forEach(function(fn){
						fn.call(element, e);
					});
				}
			});
		};
	}
	
	window.addResizeListener = function(element, fn){
		if (attachEvent) element.attachEvent('onresize', fn);
		else {
			if (!element.__resizeTriggers__) {
				if (getComputedStyle(element).position == 'static') element.style.position = 'relative';
				element.__resizeLast__ = {};
				element.__resizeListeners__ = [];
				(element.__resizeTriggers__ = document.createElement('div')).className = 'resize-triggers';
				element.__resizeTriggers__.innerHTML = '<div class="expand-trigger"><div></div></div>' +
													   '<div class="contract-trigger"></div>';
				element.appendChild(element.__resizeTriggers__);
				resetTriggers(element);
				element.addEventListener('scroll', scrollListener, true);
			}
			element.__resizeListeners__.push(fn);
		}
	};
	
	window.removeResizeListener = function(element, fn){
		if (attachEvent) element.detachEvent('onresize', fn);
		else {
			element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
			if (!element.__resizeListeners__.length) {
				element.removeEventListener('scroll', scrollListener);
				element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__);
			}
		}
	}
	
})();

Demo-licious!

Here’s a pseudo code usage of the method.

var myElement = document.getElementById('my_element'),
    myResizeFn = function(){
        /* do something on resize */
    };
addResizeListener(myElement, myResizeFn);
removeResizeListener(myElement, myResizeFn);

Cut to the chase, let’s see this resize thang in action:

Resize ALL The Things!

Now that we’re equipped with a nifty, cross-browser element resize event, what would it be good for? Here’s a few possible uses:

  • Resize-proof Web Component UI development
  • Per-element responsive design
  • Size-based loading of content
  • Anything you can imagine!

The Oft-Overlooked Overflow and Underflow Events

A Primer on Overflow and Underflow

To level-set, I’ll define and describe what overflow and underflow are in the context of the web. Overflow is a rather simple concept you’re probably familiar with: when an element’s content takes up more space than it allows, given style or box model constraints, it causes a scrollbar to appear or the content to be cut off from view (if you set overflow: hidden;). Underflow is the less common case you probably don’t think about: an element currently in an overflown state leaves that state as a result of the element growing in size or a reduction of the amount of content within it – visually, the scrollbars disappear and all content is visible within the element. As it turns out, Firefox and WebKit browsers offer events that alert you of changes between these two flow states.

What if I told you

Let’s Talk Flow Events, Boyee!

In Firefox there are two separate events, overflow and underflow. In WebKit browsers, there is a single event, overflowchanged. In using these events, it becomes painfully clear that WebKit’s catch-all overflowchanged event is ill-conceived. Webkit’s overflowchanged event forces you to compare three different event properties, event.orient, event.verticalOverflow, and event.horizontalOverflow to filter for various combinations of values in order do anything useful – needless to say, it’s about as fun as a bag of glass. The bad news: as you’ve now come to expect, Internet Explorer doesn’t support any variant of flow events, here’s my surprised face –> :|

Unifying Flow Events

To make this flow event circus easier to deal with, let’s distill them into one cross-browser method that allows us to discreetly listen for overflow and underflow state changes:


function addFlowListener(element, type, fn){
  var flow = type == 'over';
  element.addEventListener('OverflowEvent' in window ? 'overflowchanged' : type + 'flow', function(e){
    if (e.type == (type + 'flow') ||
      ((e.orient == 0 && e.horizontalOverflow == flow) || 
      (e.orient == 1 && e.verticalOverflow == flow) || 
      (e.orient == 2 && e.horizontalOverflow == flow && e.verticalOverflow == flow))) {
      return fn.call(this, e);
    }
  }, false);
}

// Example usage
addFlowListener(myElement, 'over', function(){ ... });
addFlowListener(myElement, 'under', function(){ ... });

Look What I Can Do!

FlightDeck and MetaLab: Bad Messaging Leads to Bad Times

NOTE: I spoke with Andrew Wilkinson (CEO of MetaLab) prior to releasing this post.

The Back Story

I arrived at Mozilla 4 years ago at age of 26 with a passion for the web. Like many Mozillians, my previous job was with a private company. Mozilla was radically different than any work environment I had ever been in. Not only is Mozilla open source, it’s also open meeting, open planning, open specs, open mockups, open bug lists – yeah, lots of open. I wasn’t used to this, not that I shied away from openness or wanted to be secretive, it simply took a while to acclimate myself.

One of the first projects I was tasked with when I arrived was Add-on Builder. It was to be a lightweight code environment for Firefox add-ons – mostly for beginners and people who wanted to test their add-ons in a collaborative way (think jsFiddle for Firefox Add-ons). Unfortunately, it was also the source of the most frustrating, painful event of my professional career. Given Add-on Builder was end-of-life’d a few months ago to free up resources for other developer-facing products, I thought I’d finally write about the event and what actually happened. As it turns out, it was far less interesting than the woefully inaccurate fable it mutated into. Here goes:

A new project, yay!

I started planning the project in document form, listing out the use-cases, what it was to be, what it shouldn’t be, pretty standard stuff. As a Product Manager, the focus is on user stories, features, and product lifecycle, not the UI, color scheme, gradients, and image assets. Another thing many PMs do, is create functional mockups – not to be confused with design mockups. If you’re not familiar with the difference between functional and design mockups, here’s an overview: functional mockups are generated at an early stage to communicate what features need to be built, while design mockups are given to developers at a later stage so they can implement the UI, styles, image assets etc. I’m not a designer by any means, but I learned Photoshop by making posters at a previous job in college, and later used Photoshop as my tool for creating functional mockups to spec-out product features. The idea with functional mocks should be “Here team, we need these key features, pay no attention to the design.”

Soon it came time to take things beyond the documentation stages. I had seen an awesome design firm, MetaLab, around the web before coming to work for Mozilla, and always thought it would be cool to have a product designed by them. I figured heck, this is Mozilla, maybe I’ll get a design budget for the project! I was used to budgets at other companies, we had clearly defined numbers to work with before projects began. Mozilla wasn’t huge on the exact dollars and cents in those days, there was a very casual project funding process (not that it is super corporate today, but we’re a bit more precise now).

It’s a category 5 FUD-storm! Run!

We had our first meeting with MetaLab to discuss the project, it went really well, everyone seemed excited. After our initial contact with MetaLab, I started fiddling with functional mocks for the project. I had never used a tool like Balsamiq or Moqups; I knew Photoshop, and that was my go-to. Without any malicious intent, I took a screenshot of a MetaLab web page and spec’d some of the first features we would need in Add-on Builder. I honestly didn’t think anything of it because it was just a functional mock, and I figured I’d be handing them to MetaLab so they could create the real designs. In addition, the page I took a screenshot of had a narrow, center-column layout that was not what you would use for the actual design of a code editor. After we sent MetaLab a few more details, they generated a quote for us. The quote turned out to be much higher than my boss expected, and I was instructed to decline it. I sent MetaLab an email telling them we were not able to proceed.

A couple weeks later, I stuck the first of the functional mocks on the project’s wiki page. It’s an open wiki, so everyone could see what we were planning and contribute. Again, I thought nothing of it, they were going to be discarded as soon as a real design was generated. A few months down the road, some random user saw the functional mocks on the project’s wiki page and told MetaLab we had copied their design. From there it swelled to a category 5 FUD-storm of galactic proportion, and no one was left unpoopified.

Breakin’ it down like a fraction

Let’s correct the major inaccuracies that swirled around (blog post 1, blog post 2, press FUD) by examining what actually happened, point by point:


The Myth: We stole MetaLab’s design.

Reality: The mocks were purely functional – and I reinforced this specifically, with all members of the team, on many occasions ⤵

The mocks were destined for the trash from the beginning, they were only intended to explain to developers on the team “Hey, there should be a button to press so the user can do ______.” It’s not shocking these are identical – I was cutting and pasting things to show features

metalabmozillasmall

Here’s what was actually being designed for the product (it hasn’t changed since the first line of code was committed) ⤵

real-design-flightdeck


The Myth: We used MetaLab’s design elements in Add-on Builder.

Reality: Never at anytime during the entire life of the project – including initial development and before this issue was brought to our attention – were any code, styles, or images used in the product from any design elements, assets, colors etc. in the functional mockups ⤵

piotr-email-flightdeck

But heck, don’t take my word for it, everything was done out in the open for everyone to see from day 1: github.com/mozilla/FlightDeck


The Myth: We intentionally used the functional mockups to our gain.

Reality: A single video briefly showed one of the mocks – I wasn’t aware this had happened until we were notified (the video folks grabbed it from our wiki page – oh god, the wiki!) ⤵

piotr-chat-flightdeck


Owning My Part

I was young and had just come from a private company where I created tons of functional mocks like this, and never did we implement a design from them. Because I was use to communicating features to a small team using functional mocks, then discarding them upon receipt of real designs, I never intended to steal anything by cutting and pasting features in a mock. I naively posted a functional mock on a public wiki that was based on a screenshot of a web site, and I could have attributed the image I cut and pasted things into regardless of its purpose or ultimate, intended destination: the trash. I was wrong, I should have thought of this stuff.

I should also have fought harder to tell the outside world what really happened. Sadly, the folks in charge of our communication felt it best to refrain from communicating what actually happened with any level of detail (face-to-palm-to-desk-to-wall). After talking with Andrew from MetaLab, it’s clear they were equally bewildered by the hazy communication, and unspecific apology we issued. Andrew said they have no hard feelings, and after telling them about what really happened, they were curious why we weren’t more clear about it all to begin with. Next time I will be more assertive about what needs to be communicated – letting inaccuracies float around in the press and on blogs to live forevermore did no one any favors.

Why write about it now?

The weeks that came after this craziness were really tough. Sites, blogs, and press were saying hurtful things (based on myths I was told not to correct), and the looks I got made me want to permanently work from home. I never felt so vulnerable in my professional life – which is ironic, because my co-workers had nicknamed me “Tenacious D”. It rarely, if ever, comes up these days, but it has always frustrated me that the record was never corrected – I just got tired of that. Sometimes it’s hard not to look back when you don’t definitively put the past to bed.

MVCR – Minimum Viable CSS Ribbon

I thought I’d jot down a snippet of CSS I came up with recently that I use to generate UI ribbons. The code uses :before/:after pseudo elements, which means it works in IE8+ and all the other not-shitty browsers. To ensure the content of the ribbon can be modified dynamically using JavaScript, I’ve set the pseudo element’s content property with the value attr(ribbon). The attr() content function grabs the parent element’s ribbon HTML attribute string (example: ribbon="SomeText") and uses it for the ribbon’s content. I’m pretty sure this is one of, if not the, shortest bit of code required to create CSS ribbons, but perhaps you all can improve upon it:

.ribbon:before {
	display: block;
	content: attr(ribbon);
	background: #eee;
}
	
.ribbon:after {
	display: block;
	content: " ";
	border-left: 5px dotted transparent;
	border-top: 5px solid #ccc;
	height: 0;
	width: 0;
}

CSS Selector Listeners

The following article explores a new event mechanism that I like to call CSS Selector Listeners. Selector listeners are completely bad ass, and sufficiently indistinguishable from magic, as you will soon find out.

Rise of the Mutants

Developers recently were empowered with a new way to listen for changes in the DOM: DOM Level 4 Mutation Observers. Mutation Observers emit fairly general events that tell you basic things, like when a new element has entered the DOM, or an attribute has changed on an element or subtree you are observing.

As a result of their relatively low detail resolution, developers have created libraries to help make Mutation Observers more useful for developers. Rafael Weinstein of Google recently introduced a library called Mutation Summary, which lets you watch the DOM through the lens of a limited subset of CSS selectors using a combination of Mutation Observers and its own custom logic layer to filter and interpret mutation events. The concept of listening to selector matches is intriguing because selectors inherently require the parser to quickly assess complex DOM conditions and perform selector-related logic to check for matches. Having the ability to listen for when selector states become active would be a powerful tool indeed!

A Hidden World of Events

Mutation Summary is pretty cool, but what if I told you there was a hidden world of arcane CSS selector event magic living in your browser that offered unlimited, detailed insight into the state of the DOM? Well there is, and technically there always was, in every style sheet you’ve ever written. All we needed was the right key to access this world…well, a Keyframe, to be exact.

Piggybacking the Parser, with Keyframes

I recently posted a method for using CSS Animation Keyframes to detect node insertions via the animationstart event of a dummy keyframe animation. Now let’s take a second to realize what this hack really is at its core: the ability to listen for any selector-based matches the CSS parser makes anywhere in the DOM. The Selector Listener code I’ve developed provides two methods, addSelectorListener and removeSelectorListener. These methods are available at both document and element level, and allow you to listen for any selector match, regardless of complexity. Once the parser detects a matched selector, the event bubbles up the DOM from the matched target element to the element or document the selector listener is attached to. Here’s what it looks like in action:

// Some action to perform when a match occurs
var sequenceMatch = function(){
  alert("Selector listeners, they're easy as A, B, C!");
};

// Attaching your selector listener to the document or an element
document.addSelectorListener('.one + .two + .three', sequenceMatch);

// Remove the selector listener when it is no longer needed
document.removeSelectorListener('.one + .two + .three', sequenceMatch);

The Goods: Code & Demo

The code that provides all this new hotness, as well as more examples of what’s possible, is available on Github here: SelectorListener Code Repo

You can also play around a bit with this demo page: SelectorListener Demo

A Modal Cure in Pure CSS – No Wrappers, no JavaScript, no BS

Modals. They’ve been the subject of countless hacks over the years (I did a cross-browser one a while ago here), but due to cross-browser considerations, they usually are less than elegant in their implementation.

Well, if you don’t care about IE < 9, things are about to get much, much easier. This little trick is so simple it’s going to make you cry, in fact it’s so easy, it may cause you to audibly curse IE louder and more passionately than you ever have:

<div id="modal">
    <!-- YOUR CONTENT HERE (seriously, it's just one div) -->
</div>
#modal {
    display: block;
    position: fixed;
    top: 50%;
    left: 50%;
    box-sizing: border-box;
    transform: translate(-50%, -50%);
}

Here’s a fiddle so you can see it in action:

Yeah I know, all those hours wasted on wrapper divs, tables, crazy CSS, and performance-robbing JS, all to get hoodwinked by some top/left positioning and a transform property, go figure.

I Want a DAMNodeInserted Event!

Have DOM Level 3 Mutation Events got you down?

There’s a long, sordid history behind DOM Level 3 Mutation Events. They’re basically the DOM Event equivalent of crack for developers: a ridiculous high of programmatic, dynamic handling of DOM manipulation, with a crash of endless, unavoidable, performance-destroying event evaluation.

John Resig detailed the plight of DOM Mutation Events in a mailing list thread, circa 2009:

Yes, DOM mutation events already exist (in Firefox and Opera – fairly reliably – and dicey in Safari). They have a huge problem, though:

They absolutely cripple DOM performance on any page which they’re enabled.

Firefox, for example, when it realizes that a mutation event has been turned on, instantly goes into an incredibly-slow code path where it has to fire events at every single DOM modification. This means that doing something like .innerHTML = “foo” where it wipes out 1000 elements would fire, at least 1000 + 1 events (1000 removal events, 1 addition event).

Mutation Events have since been deprecated, and browsers have not implemented any sort of replacement…but unknowingly, they actually have ;)

But wait! An epic hack emerges!

What I’m going to present below is a hack in the truest sense of the word, but damn, is it cool. The method I’ve devised provides the same functionality DOMNodeInserted offered, without requiring you to annihilate browser performance in the process – and it is very likely to work for a long, long time.

The Description

Basically what we’re going to do is setup a CSS keyframe sequence that targets (via your choice of CSS selector) whatever DOM elements you want to receive a DOM node insertion event for. I used a relatively benign and little used css property, clip I use outline-color in an attempt to avoid messing with intended page styles – the code once targeted the clip property, but it is no longer animatable in IE as of version 11. That said, any property that can be animated will work, choose whichever one you like.

Next I added a document-wide animationstart listener that I use as a delegate to process the node insertions. The animation event has a property called animationName on it that tells you which keyframe sequence kicked off the animation. Just make sure the animationName property is the same as the keyframe sequence name you added for node insertions and you’re good to go.

The Demo

That’s about it, pretty simple huh? Let’s see it in action – notice that the text in the divs isn’t added until their insertion into the DOM is detected:

Party time, excellent!

There you have it folks, a scope-able, performant, relatively simple method for DOM node insertion listeners in all browsers that support CSS3 Animations.

In related news: I will be accepting donations in the form of Jamba Juice gift cards, or pure gold bullion if you’re feeling especially generous.

Deep, Strict Equality Comparison of Native Types in JavaScript

The Why

My buddy and I have been working on a web app and he recently needed a way to compare and ensure that two JS native instances were of the same type and that they were strictly equal (=== vs a loose equality check, ==). After you do the basic check to ensure that the two natives are of the same type, checking strict equality is a piece of cake for some types like Number, String, and Boolean. The interesting checks come when dealing with the other types though. The code is worth a thousand posts in this case, so let’s cut to the chase:

The Code

If you are unfamiliar with things like typeOf, Array.from, and Object.extend – (THIS DOES NOT EXTEND THE PROTOTYPE OF OBJECT), that’s OK because those are little helpers provided by the awesome MooTools JS framework which I contribute to ;)

Object.extend({
  'equals': function(first, second){
    if (first !== second){
      var type = typeOf(first),
          every = Array.every;
      if (type != typeOf(second)) return false;
      switch (type){
        case 'string': case 'regexp': return String(first) == String(second);
        case 'date': return first.getTime() == second.getTime();
        case 'arguments':
          first = Array.from(first);
          second = Array.from(second);
        case 'object': every = Object.every;
        case 'array': case 'object': case 'arguments':
          if (Object.getLength(first) != Object.getLength(second)) return false;
          return every(first, function(value, i){
            return (i in second) && Object.equals(value, second[i]);
          });
      }
    }
    else return true;
  }
});

Step 1: Type checking

In this code block, I first start by type checking with MooTools’ typeOf, this helper is a bit better than the native typeof because it distinguishes between arrays, arguments objects, and DOM collections (also node lists), as well as a few other type-check oddities.

Step 2: Use the right equality check

Once I know both types are the same, we throw that into a switch to sift it into the right equality check case. You’ll notice there are very different methods employed to ascertain equality depending on the type you’re dealing with.

Step 3: Some checks are easier than others…

As stated above, numbers, strings, and booleans are all easy to check, so we knock them out in one case. Next up is Regexp, if you just compare them strait-up, two regexp objects will always report false, even if their regexp matching characters are identical. The best way there is to use toString on each to compare the actual matching characters. Next is Date. I chose not to use toString to compare date objects because the output is a low resolution time that only goes to minutes. To know two dates are exactly the same, instead I use getTime(), which gives me millisecond precision. Lastly are Array and Object. These two are not too much harder, they just require iterating the array or walking the object, then using the Object.equals method recursively on the values descending to the full depth of the instance.

What about Function equality?

Due to the nature of the need, and the nearly impossible task of ensuring functional equality on unnamed and non-cached functions, this was not a concern for me and is not likely a concern for the broad range of use-cases.

Hope this helps, enjoy!

(If you see any errors or I missed a type you think should be included, let me know in the comments!)

The Best Damn Modal Method Periodâ„¢

The Gist

In the long annals of web history there have been many attempts at creating modal dialog boxes. As far as I’m aware, all methods to date use hard-coded element heights and heavy-handed JavaScript logic to center the modal in the viewport (not to mention the complex measurement of elements and resetting of styles they do on window resize). There are a number of ways to vertically center things with HTML and CSS, Douglas Heriot details most of them in the following post: Vertical Centering with CSS via ThemeForest’s Lost In the Woods blog.

Thinking Outside the Box…Literally

The other day, I threw an off-the-shelf lightbox script into a new project that I had used previously. Quickly, old frustrations returned as I looked over the mess of code and calculations required to do something as simple as positioning an element in the center of a page. I thought “Holy expletive outburst Batman, there has to be a better way than this!”. Turns out there was.

I’ve devised a method for centering modal content that is radically easier than any that have come before it. There are two types of modals you can create with this method that I’ll be discussing in this post. The first is a basic single element modal. The second is a modal with a header, footer, and even more dynamic height functionality. Both varieties automatically resize and recenter the modal in response to changes in size or content. The two methods work in IE7+, Firefox 2+, Chrome, and Safari 3+ (IE6 support requires fixed position fakery, but that is par for the course).

Here is a diagram so you can visualize the basic HTML and CSS that forms the foundational for both variations of the method:

A Simple Modal

Start with the following simple HTML structure:

<table id="modal">
  <tbody id="modal-tbody">
    <tr id="modal-tr">
      <td id="modal-td">
        <div id="modal-box">
          <div id="modal-body"> 
              <!-- Your content will go in the
              modal-body element. Always use
              this element for adding padding,
              margin and styles, as it will preserve
              your height and width settings. -->
          </div>
        </div>
      </td>
    </tr>
  </tbody>
</table>

“Nooo, a table!”, I can hear it now, sigh. You want a simple, non-JS, vertically centered modal or not buddy? Yeah, that’s what I thought. Here’s a preview of what you’re about to think in 10 seconds: “Just when I was about to light this guy up for simply nesting crap in a table (big wup), he throws down this sweet CSS and totally redeems himself!”. Here’s why you’re about to think that:

(I have commented next to the property values that can be optionally user-specified using any CSS unit you’d like)

html, body
{
    height: 100%; /* root and body MUST be height 100% */
}

#modal
{
    position: fixed;
    top: 0;
    left: -100%;
    height: 100%;
    width: 100%;
}

#modal-tbody, #modal-tr, #modal-td
{
    height: 100%; /* All table elements should be height 100% */
}

#modal-td
{
    vertical-align: middle;
}

#modal-content
{
    position: relative;
    left: 100%;
    height: auto; /* HEIGHT optional, any unit */
    width: 50%; /* WIDTH optional, any unit */    
    max-height: 80%; /* MAX-HEIGHT optional, if height auto, must be % */
    min-height: 80px; /* MIN-HEIGHT optional, any unit */
    max-width: 225px; /* MAX-WIDTH optional, any unit */
    min-width: 80px; /* MIN-WIDTH optional, any unit */
    margin: 0 auto;
    border: 1px solid;
    background: #eaeaea;
    overflow: auto;
}

Here’s a basic example via jsFiddle:

NOTE: When using the simple modal method with height set to auto, you must constrain the modal to a percentage-based max-width in order to ensure that scroll bars appear when the modal content is taller than the window. An example of this use-case would be cases where you need to add elements to the modal, but outside the modal-body element, such as a header and footer. Luckily the advanced method described below allows for this.

The Advanced, Have-your-cake-and-eat-it-too Modal

There are cases were you want the best of everything. For such cases, you’ll need a slightly modified HTML structure:

<table id="modal">
  <tbody id="modal-tbody">
    <tr id="modal-tr">
      <td id="modal-td">
        <div id="modal-box">
          <div id="modal-header"></div>
            <div id="modal-content">
              <div id="modal-body">
                  *** YOUR CONTENT HERE ***
              </div>
            </div>
          <div id="modal-footer"></div>
        </div>
      </td>
    </tr>
  </tbody>
</table>

There is also a single addition to the CSS:

#modal-content
{
	overflow: auto;
}

Notice I have wrapped the body in another element, modal-content. This is the only required change, the header and footer are just so our advanced modal demo looks more like the intricate modals you see on the web today.

Next, you need to set a single style with JavaScript on window resize. Now don’t get all sad face on me, this one-line of JavaScript is nothing like the complex, performance assaulting JS found in the modal scripts floating around the net. This is about as close to set-it-and-forget-it you’ll ever get, let’s take a look:

window.addEventListener('resize', function(){
    document.getElementById('modal-content').style.maxHeight = document.documentElement.offsetHeight - headerFooterMargin;
}, false);

Above you’ll notice the header/footer/margin variable. In cases where you have a header, footer, or want to allow extra space between the modal and the edge of the window on resize, you’ll need to subtract that value from the document’s height offset.

The awesome thing about this JavaScript is that it retains 100% of the adaptive ability of the modal. All other modal scripts on the web fall down when you add dynamic content or manipulate the size of their modal elements. Usually if you add content afterward, modal scripts require you to fire some laborious recalculation method that resets a massive set of height, width, and positioning values in order to maintain a centered and scrollable modal. The Best Damn Modal Method Period eliminates all that unneeded calculation and frees you up to create the most dynamic and complex modal interactions you can think of.

Here’s a full-page demo of the advanced method in action. For my own purposes, I create a MooTools class using the Best Damn Modal Method Period for my own convenience, but the output remains the same:

Sexy Full-size Advanced Modal Demo

Happy modal’n folks!

10 years later, the sadness remains

I always have a hard time around 9/11 each year.

Every year I watch the videos again and cry just like I did that day sitting in my school’s student union. Of the 3,000 innocent people who were killed, I didn’t know one, but I would have liked to. 19 psychopaths with airplanes took away from all of us the ability to ever know them. They extinguished the hopes, dreams, and aspirations of 3,000 in an instant. They stole 3,000 fathers, mothers, sons, daughters, and spouses from families that loved them. They murdered 3,000 of our fellow Americans.

Please take the time each day to appreciate, cherish, and love whoever walks through the door with a smile to greet you. Perhaps it’s your father, mother, son, daughter, or spouse, maybe it’s your roommate or friend. Remember that for 3,000 families, never again will they see that person walk through their door to say hello or I love you.

Sit in quiet reflection and think about how you felt that day in September, write the names of those 3,000 on your heart, and most importantly, never forget.