Google Closure Tutorials

Lightbox / Shadowbox / Whateverbox built on Closure

Posted in Images, UI by googleclosuretutorials on November 13, 2009

LOOKING FOR SOURCE? Zip containing all the required files (you still need the Closure library though, naturally).

For my first few forays into Google’s Closure library I’ll be looking at building some common UI components.

First up is the inline / modal popup. Most of the time these things are used for images, so for this little app I’m going to make it work for that purpose.

Now, what do we want out of this?

  1. It must be portable and easy to implement.
  2. The popup itself should shrink or expand to match the image inside it.
  3. When the popup is launched, the rest of the page should fade out.
  4. The popup must have a close button, but clicking on the page / background should also close it.

Right, let’s get started… I’m going to assume you’ve run through the Hello World tutorial provided by Google. If not, there is a great walkthrough of it on moretechtips.net.

To begin with, make a new .JS file and give it a meaningful name (being the imaginative person I am, I chose ‘inlinepopup.js’). The first thing we need to do is define a namespace for our InlinePopup Class. Therefore, our first declaration should therefore make use of goog.provide():

goog.provide( 'neame.ui.InlinePopup' );

Now that we’ve done that, we need to identify which classes in the Closure library we’ll be using for our InlinePopup. A quick glance through the google.ui package shows a few potential candidates but Dialog seems to be the most appropriate.  Frankly, it looks like all we’re going to need to do is tweak a few properties and Bob’s your uncle.

As for preloading the image – Google kindly provides us with the ImageLoader class, in the goog.net package. We’re also going to need to handle some events, so we’ll need goog.events for that, and we’re going to be working with the DOM so a DomHelper would be handy too. The lines to include these classes are as follows:

goog.require( 'goog.events' );
goog.require( 'goog.ui.Dialog' );
goog.require( 'goog.dom.DomHelper' );
goog.require( 'goog.net.ImageLoader' );

Ok, so we have our namespace, we’ve included the required classes… We know we’re going to need at least a constructor, a method to launch the popup, a method to close the popup and a method to handle the image load, so let’s define those now:

// constructor – takes no arguments
neame.ui.InlinePopup = function(){ };// open popup – takes a click event as an argument
neame.ui.InlinePopup.prototype.goPopup = function( e ){ };

// close popup – takes a click event as an argument
neame.ui.InlinePopup.prototype.closePopup = function( e ){ };

// image loaded – takes a load event as an argument
neame.ui.InlinePopup.prototype.imageLoaded = function( e ){ };

// bind to class – adds a listener to any links with the specified class ‘c’
neame.ui.InlinePopup.prototype.bindToClass = function( c ){ };

Note that none of these methods will return anything (or if you like AS3 or Java, they return void).

So we have our skeletons set up but no logic yet. The constructor is the logical place to start. I’ll write out the code here and we can discuss it afterwards:

neame.ui.InlinePopup = function(){// init objects
this.dia = new goog.ui.Dialog( 'inlinepopup' , true );
this.loader = new goog.net.ImageLoader();
this.domh = new goog.dom.DomHelper();

// setup objects
this.dia.setVisible( false );
this.dia.setBackgroundElementOpacity( 0.9 );
this.dia.getButtonElement().parentNode.removeChild( this.dia.getButtonElement() );
this.dia.getTitleCloseElement().innerHTML = 'X';

// event listeners
goog.events.listen( this.dia.getBackgroundElement() ,  goog.events.EventType.CLICK , this.closePopup , false , this );
goog.events.listen( this.loader , goog.events.EventType.LOAD , this.imageLoaded , false , this );

};

First off, we instantiate our objects. We create a new Dialog with the class name ‘inlinepopup’, using an iframe mask for the background. Next up, we create our ImageLoader and DomHelper. Nothing too taxing so far.

Now for some more interesting stuff – let’s set up our Dialog. The syntax is pretty much self-commenting but all I’m doing here is making sure it’s not visible at first, setting the background to 90% opacity, removing the OK and Cancel buttons, and adding a simple ‘X’ character to the close button (you might want to use an image background via CSS instead). If you change setVisible to true right now, you’ll be able to see the Dialog on your page if you create a new InlinePopup… But it won’t look like much until we apply the CSS later.

Next we set up our event listeners. One to listen for a CLICK on the background and call closePopup. Another to listen for a LOAD event on our imageloader and call imageLoaded.

So our constructor is now in place! On to the open and close methods:

neame.ui.InlinePopup.prototype.goPopup = function( e ){
// dont follow the link
e.preventDefault();// add link href as image to load
this.loader.addImage( 'previewimg' , e.currentTarget.href );
this.loader.start();

// set title
if( e.currentTarget.title )
this.dia.setTitle( e.currentTarget.title ); // use title if the anchor has one
else
this.dia.setTitle( e.currentTarget.href ); // otherwise show href as title

};

neame.ui.InlinePopup.prototype.closePopup = function( e ){
this.dia.setVisible( false );
};

closePopup shouldn’t really need any explanation – all it does it setVisible to false on the Dialog (i.e.: it hides it).

openPopup is a little more involved. Since this is going to be launched from anchor elements on the page (<a>), we need to make sure the link isn’t followed or the browser will navigate off the page. To prevent this we make a call to preventDefault on the event. This means the default action of the click event will not fire – the link will not be followed and the user will stay on the page. Next, we pass the link href to the ImageLoader and tell it to start loading. Since the anchor element is the currentTarget of the CLICK event, we can access this URL via e.currentTarget.href. ‘previewimg’ in this call is just the ID we want to give the image. If you want to load a bunch of these you’ll need a unique ID for each image, but in this case we’re only dealing with 1 image at a time so it’s safe to use the same ID each time.  Finally, we set the Dialog title. If the anchor element has a title attribute, we use that. Otherwise we just default to the href value.

So, we have a constructor, a way to open the Dialog and close it, but what we are missing is logic to handle the image load:

neame.ui.InlinePopup.prototype.imageLoaded = function( e ){
// set width, then content , then show it
this.dia.getDialogElement().style.width = ( e.target.width + 30 ) + 'px';
this.dia.setContent( '<img src="' + e.target.src + '" width="' + e.target.width + '" height="' + e.target.height + '" />' );
// show it
this.dia.setVisible( true );
};

The first statement matches the Dialog width to the image width. This is done fairly simply by accessing the target of the load event (the image) and just reading off it’s width property. I’m adding 30 pixels here to allow for 15 pixels padding either side of the image. Then we just need to set the content of the dialog and show it.

Last up is the bindToClass method:

neame.ui.InlinePopup.prototype.bindToClass = function( c ){var es = this.domh.getElementsByTagNameAndClass( '*' , c );
// listen for CLICK Event on every object in the NodeList
for( i in es ) if( typeof es[ i ] == 'object' )
goog.events.listen( es[ i ] , goog.events.EventType.CLICK , this.goPopup , false , this );

};

This is pretty self-explanatory but all we’re doing here is selecting every element on the page with the class given and listening for a click event on it. Then the event is passed to our goPopup method to open the Dialog.

And that’s it for the InlinePopup class!

Here’s the CSS I used. Feel free to play around with these but you need to keep the position declarations, or the dialog will render below the background iframe rather than on top of it. I pretty much just ripped this off from the Dialog example page and then modified it to suit a style that I’d like:

.inlinepopup-bg {
position: absolute;
background-color: #000;
top: 0;
left: 0;
}
.inlinepopup {
position: absolute;
background-color: #000;
width: 1000px; /* this gets replaced anyway via Closure JS */
overflow: hidden;
}
.inlinepopup-title {
position: relative;
background-color: #000;
color: #fff;
padding: 10px 15px;
font-size: 16px;
font-weight: bold;
vertical-align: middle;
cursor: hand;
}.inlinepopup-title-close {
position: absolute;
top: 10px;
right: 15px;
width: 15px;
height: 16px;
cursor: default;
}

.inlinepopup-content {   padding: 15px; }

Finally, we pull it all together in our HTML document:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<title>Google Closure Shadowbox!</title>

<script src="/path/to/closure/goog/base.js"></script>

<script src="inlinepopup.js"></script>

<link href=”style.css” />

</head>

<body>

<div><a href="http://arkandis.com/images/blog/google.gif&quot; class="inlinepopup-trigger" title="Google logo in Lego"><img src="pic.jpg" width="200" height="125" alt="Pic" /></a></div>

<div><a href="http://images.businessweek.com/ss/06/07/top_brands/image/google.jpg&quot; class="inlinepopup-trigger" title="Google logo on some dude's hand"><img src="pic.jpg" width="200" height="125" alt="Pic" /></a></div>

<script>

var popup = new neame.ui.InlinePopup();

popup.bindToClass( 'inlinepopup-trigger' );

</script>

</body>

</html>

Note: If you’re going to push this to a live site, don’t forget to compile your code. This stuff is just the source!

Anyway, hope you found the tutorial useful. Comments are very welcome. Next up is a Slideshow!

Dan