A common pattern in javascript is the in-page popup. These little html boxes are used for messaging and to let users enter data snippets, etc. We have a whole slew of things that do this kind of thing, so dig in.
A "modal" window is a DHTML window (i.e. a dom element floating above the page content) that prompts the user for action and, at the same time, has a layer between it and the page content that obscures the content so the user has but one choice (to perform the task in the popup).
The Modalizer class is meant to be implemented into other classes (as we do in our StickyWin classes) to provide them this functionality.
Here's a quick and simple illustration (just click anywhere to make the modal layer go away again after you execute this code):
new Modalizer().modalShow();

Pretty simple, right?
There are several options including things like the color and opacity of the layer, its z-index, the option to hide the layer when it's clicked, and a callback to execute when the user does this. There are a few other options (see the docs) but here's an example with a bit more configuration:
new Modalizer().modalShow({ modalStyle:{ 'background-color':'#d6e1b9', 'opacity':.6 } });

Using the .implement function on a class you can add modal functionality to anything you author:
var Widget = new Class(...); Widget.implement(new Modalizer); Widget.modalShow();
By coupling this with the onModalHide option, you can allow your user to click anywhere on the modal layer to execute the close action both on your widget and on the layer. Here's an example from the Utilities section of our library:
simpleErrorPopup("Woops!", "Oh nos! I've got five Internets open!");

Clicking above will open a little popup (this is a StickyWin object which I demonstrate below) that has a modal layer. Clicking the layer will close the layer AND call the close function on the window, and vice versa (if you click the close "x" in the window, it'll close the layer).
The StickyWin class helps you create in-page popups. If you want to put a box on the page floating above your content, StickyWin just helps do that for you. Examples are always best to work with, so here you go.
First, we have to have some html to use in the popup:
Note the class="closeSticky" in the close href. The StickyWin class will attach its close function to this anchor.
new StickyWin({content: $('stickyWinContent').getValue()});

You can configure a StickyWin with a lot of options. I won't go through all of them here with examples (see the docs for a full list), but you can define callbacks for the show and hide events, specify an id and a className for the StickyWin element (a div) into which your content will be injected, width and height and z-index for the container div (which will basically be the size of your content otherwise, unless that content has a margin set), a timeout so that your popup goes away after a specified amount of time, whether or not you want to allow more than one on the page at the same time, and if it should use an IframeShim.
In addition to all these options, you can also specify the position of the StickyWin. Examples:
new StickyWin({ content: $('stickyWinContent').getValue(), relativeTo: $('fxTarget'), /*upper left corner of fxTarget */ position: 'upperLeft' });

new StickyWin({ content: $('stickyWinContent').getValue(), relativeTo: $('fxTarget'), /*upper right corner of fxTarget */ position: 'upperRight' });

new StickyWin({ content: $('stickyWinContent').getValue(), relativeTo: $('fxTarget'), /*center of fxTarget */ position: 'center' });

new StickyWin({ content: $('stickyWinContent').getValue(), relativeTo: $('fxTarget'), /*upper left corner of fxTarget */ position: 'upperLeft', offset: { x: -200, /*over to the left*/ y: 10 /*and down a little*/ }, useIframeShim: false });

Each instance of StickyWin has the methods .show(), .hide() - which do what you think they do, and .setContent() which will replace the content of the StickyWin with what you give it. In addition to this, .win is the dom element that contains your content.
Calling .show() on a StickyWin will re-position it relative to the element again (so if the relative element is the window and the user scrolls or resizes, you can call .show() again to replace the popup where it belongs).
stickyWinHTML() returns an html layout that is my default stickyWin skin. I got tired of authoring this over and over again, so it's just a quick way to wrap your content up in a nice window. Feel free to download it and change the styles to suit your needs.
var simpleLayoutExample = stickyWinHTML('the caption', 'this is the body', {width: '400px'}); $('swLayoutTarget').adopt(simpleLayoutExample);

The first argument is the caption, the second is the body. They can either be strings or dom elements.
The options are css (to override the default), width, and baseHref (currently cnet.com, where the images in the example reside).
Additionally, you can specify buttons with text and callbacks for any number of buttons:
var simpleLayoutExample = stickyWinHTML('the caption', 'this is the body', { width: '400px', buttons: [ { text: 'close', onClick: function(){alert('closed!')} }, { text: 'okey-dokey', onClick: function(){alert('ok!')} }, { text: 'blah', onClick: function(){alert('blah!')} } ] }); $('swLayoutTarget2').adopt(simpleLayoutExample);

So an example of this as a StickyWin would look something like:
new StickyWin({ content: stickyWinHTML('the caption', 'this is the body', { width: '400px', buttons: [ { text: 'pin', className: 'pinSticky' }, { text: 'close', onClick: function(){alert('closed!')} }, { text: 'okey-dokey', onClick: function(){alert('ok!')} } ] }) });

Here's a single button StickyWin:
new StickyWin({ content: stickyWinHTML('the caption', 'this is the body', { width: '400px', buttons: [{ text: 'okey-dokey', onClick: function(){alert('ok!')} }] }) });

Note: The onClick event here should not be confused with the onClose event for StickyWin. That gets fired also when the window is closed. If the user clicks the little "x" button in the corner (or any other item you have assigned as a close link - see StickyWin) the onClose event is fired for StickyWin, but NOT for this html block. So if you want to assign an event to the closing of the window, use the StickyWin onClose event, while if you want to assign an event to the user clicking a big button here - which can read whatever text you want ('cancel' or 'no thanks' or even 'ok'), then use this onClick event.
One more option is to add a drag handle to the caption area. If you don't use this option but are using StickyWinFx.Drag, the whole caption is draggable, but if you don't want that, you can add this little handle:
new StickyWinFx({ draggable: true, content: stickyWinHTML('the caption', 'this is the body', { width: '400px', cornerHandle: true, buttons: [{ text: 'okey-dokey', onClick: function(){alert('ok!')} }] }) });

StickyWinFx .Drag docs | svn The StickyWinFx class is a StickyWin that fades in an out (woot). In addition to that, if you also include svn|stickyWinFx.Drag.js, then you can make the popup resizable and/or draggable. The class (StickyWinFx) is used in all three cases. If you don't include stickyWinFx.Drag.js, then the options for dragging and resizing won't work, but it won't break or anything.
new StickyWinFx({ content: $('stickyWinContent').getValue(), fadeDuration: 600 });

new StickyWinFx({ content: $('stickyWinFxContent').getValue(), draggable: true, dragHandleSelector: 'div.handle', resizable: true, resizeHandleSelector: 'a.resizer', width: 300, height: 160 });

Ok, it's not pretty, but what it looks like is up to you. Just style it like you would anything else on your page.
Using my stickyWinHTML function that I demonstrate above, here's a prettier draggable box:
new StickyWinFx({ content: stickyWinHTML('the caption; drag here', 'this is the body', {width: '400px'}), draggable: true });

As outlined in the modalizer.js section, a modal window is one that grays out the page when it prompts the user to act. StickyWinModal is just modalizer+StickyWin:
new StickyWinModal({content: $('stickyWinContent').getValue()});

You can pass it the same kind of info you can a StickyWin with an additional modalOptions object with settings for modalizer:
new StickyWinModal({ content: $('stickyWinContent').getValue(), relativeTo: $('fxTarget'), /*upper left corner of fxTarget */ position: 'upperLeft', offset: { x: -200, y: 10 }, modalOptions: { modalStyle:{ 'background-color':'#d6e1b9', 'opacity':.6 } } });

All of the StickyWin classes have a .Ajax version that will allow you to retrieve their content from the server. The expected response from the server is HTML, but really it can be anything.
All StickyWin*.Ajax classes have the following options in addition to the options in their respective non-ajax versions:
Examples:
This one will use stickyWinHTML to wrap the response.
new StickyWin.Ajax({ url: '/wiki/stickyWinAjaxExample.html', wrapWithStickyWinDefaultHTML: true, caption: 'StickyWin.Ajax test' }).update();

This one will just display the response:
new StickyWin.Ajax({ url: '/wiki/stickyWinAjaxExample.html' }).update();

This one will handle the results itself, alerting them:
new StickyWin.Ajax({ url: '/wiki/stickyWinAjaxExample.html', handleResponse: function(response){ alert(response); } }).update();

In the sections above and in other pages, I described our simpleTemplateParser functionality and the StickyWin object. The PopupDetail class combines these things together into functionality that lets you hover some details next to something on the page. A quick example:
new PopupDetail($('popupDetailHTML').getValue(), { observer: $('raidersThumb'), data: { thumbnail: '/wiki/_media/cnet-libraries/jones1.jpg', name: 'Raiders of the Lost Ark', director: 'Steven Spielberg', writer: 'George Lucas & Philip Kaufman', starring: 'Harrison Ford & Karen Allen', duration: '115 min' }, stickyWinOptions: { position: 'upperRight', offset: { x: 10, y: -50 } } });

After you execute the code, mouse over the thumbnail of the movie poster above. Again, what it looks like is up to you.
Using the same example above, here's the same thing except using ajax to get the data. Here's our json file. So the PopupDetail instance would look like this:
new PopupDetail($('popupDetailHTML').getValue(), { observer: $('raidersThumbAjax'), useAjax: true, ajaxLink: '/wiki/_media/cnet-libraries/raiders.json', ajaxOptions: {method: 'get'}, stickyWinOptions: { position: 'upperRight', offset: { x: 10, y: -50 } } });

Watch your console and you'll see the ajax fire to get the json.
PopupDetail has several other options, so be sure to dig into the docs.
The default behavior for PopupDetail objects requires a template and a json object either pre-defined or returned via ajax. You can, however, use html in your response for Ajax requests and return the entire popup body instead of data in JSON/javascript object format.
Here's a super simple example to illustrate the concept.
new PopupDetail("the html argument is ignored", { observer: $('PopupDetailAjaxHTMLExample'), useAjax: true, ajaxLink: '/wiki/stickyWinAjaxExample.html', htmlResponse: true });

You can also use this functionality with the PopupDetailCollection class below.
The PopupDetailCollection class manages creating a bunch of PopupDetail instances on a page. Imagine a search results page or something similar where you have a list of objects and you want to show details for each one (when the user clicks or mouses over each result). PopupDetailCollection lets you configure things in a group.
var indianaJones = [ { name: 'Raiders of the Lost Ark', thumbnail: '/wiki/_media/cnet-libraries/jones1.jpg', director: 'Steven Spielberg', writer: 'George Lucas & Philip Kaufman', starring: 'Harrison Ford & Karen Allen', duration: '115 min' }, { name: 'Temple of Doom', thumbnail: '/wiki/_media/cnet-libraries/doom.jpg', director: 'Steven Spielberg', writer: 'George Lucas & Willard Huyck', starring: 'Harrison Ford & Kate Capshaw', duration: '118 min' }, { name: 'The Last Crusade', thumbnail: '/wiki/_media/cnet-libraries/crusade.jpg', director: 'Steven Spielberg', writer: 'George Lucas & Philip Kaufman', starring: 'Harrison Ford & Sean Connery', duration: '127 min' } ]; new PopupDetailCollection({ details: indianaJones, observers: $$('#popupDetailCollectionThumbs img'), template: $('popupDetailHTML').getValue(), popupDetailOptions: { stickyWinOptions: { position: 'upperRight', offset: { x: 0, y: -50 } } } });

We can make a collection that gets its data from ajax if we want:
new PopupDetailCollection({ ajaxLinks:[ '/wiki/_media/cnet-libraries/raiders.json', '/wiki/_media/cnet-libraries/doom.json', '/wiki/_media/cnet-libraries/crusade.json' ], observers: $$('#popupDetailCollectionAjaxThumbs img'), template: $('popupDetailHTML').getValue(), popupDetailOptions: { useAjax: true, stickyWinOptions: { position: 'upperRight', offset: { x: 0, y: -50 } } } });

Note that the PopupDetail won't go away if you move your mouse over it. When you mouse over the icon the popup appears and if you move your mouse over the popup it hangs around until you mouse out of the popup. This means that the popup can have interactive elements.
cnet-libraries/03-jswidgets/02-dhtml-popups.txt · Last modified: 2008/01/07 14:32 by aaron-n