Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Tuesday, March 4, 2008

Simple Image Popouts

In-page popouts are a powerful tool you can use to make your website feel more like a desktop application. There are a lot of great libraries with powerful, full-featured in-page popouts, and if you want the kitchen sink, I suggest you use one like Yui’s Container. However, sometimes you just want something simple, that works out-of-the-box, requiring little code and next to no setup. For those times I have created a Simple Image Popout widget.

Example 1: Simple Image Popout JavaScript

var ImagePopout = function(bd, src, emap) { // Module Constant Variables var STYLES_DIV = 'position: absolute; top: 0; left: 0; overflow: visible; height: 0; z-index: 100', STYLES_IMG = 'position: absolute; top: 0; left: 0; display: none;'; // Module Local Variables var F = function() {}, that = null, img = new Image(), nextNode = bd.firstChild; img.src = src; // find first non-text while (nextNode.nodeValue) { nextNode = nextNode.nextSibling; } // Libary Functions // will pass in the evet returned by addListener, and expects to return an object where x is the left position of event and y is the top position of event var getEventPos = function(e) { return {x: YAHOO.util.Event.getPageX(e), y: YAHOO.util.Event.getPageY(e)}; }; var Event = YAHOO.util.Event; var addListener = Event.addListener; // expects params : function(element, targetEvent, callbackFunction) var viewportSize = function() { // expects to return an object where x is the viewport with and y is the viewport height return {x: YAHOO.util.Dom.getViewportWidth(), y: YAHOO.util.Dom.getViewportHeight()}; }; var $ = YAHOO.util.Dom.get; // Module Dom Variables var dom = { div: bd.insertBefore(document.createElement('div'), nextNode), img: $(document.createElement('img')) }; dom.img.src = img.src; dom.div.setAttribute('style', STYLES_DIV); dom.img.setAttribute('style', STYLES_IMG); dom.div.appendChild(dom.img); // Public Methods F.prototype = { /** * Hides the popout */ hide: function() { dom.img.style.display = 'none'; }, /** * Shows the popout */ show: function() { var wsize = viewportSize(), left = (wsize.x - 800) / 2, top = (wsize.y - 400) / 2; if (left < 0) {left = 0;} if (top < 0) {top = 0;} dom.img.style.left = left + 'px'; dom.img.style.top = top + 'px'; dom.img.style.display = 'block'; } }; that = new F(); // attach the click event to the popout addListener.call(Event, dom.img, 'click', function(e) { var epos = getEventPos(e), x = epos.x, y = epos.y, coords = YAHOO.util.Dom.getRegion(dom.img), ox = coords.left, oy = coords.top; // iterate on the emaps, to see if event is valid for (var i = 0, j = emap.length; i < j; i += 1) { var l = emap[i]; // inside coordinates if (ox + l.left < x && oy + l.top < y && ox + l.right > x && oy + l.bottom > y) { l.fn(e); } } }); return that; };

This widget handles the position, displaying, and interaction of an image file that you want to use as a popout. Simply create the perfect image of your static popout, in Photoshop, and include it in your project. To use this widget, call as follows:

Example 2: Calling ImagePopout

var popup = null; var fn = function() { popup.hide(); }; var eventRegion1 = { top: 60, right: 190, bottom: 80, left: 90, fn: fn }; var eventRegion2 = { top: 110, right: 330, bottom: 130, left: 230, fn: fn }; var eventRegion3 = { top: 175, right: 145, bottom: 195, left: 45, fn: fn }; var emap = [ eventRegion1, eventRegion2, eventRegion3 ]; var popup = new ImagePopout('referenceToBodyTag', 'urlToPopupSource', emap);

When you call ImagePopout, you need to pass a reference to the body tag and the src to your image file. ImagePopout will create a new, absolutely-positioned node as the first element in the body and insert a new image tag, with your src, into that element. The constant styles are those that I have found work best, in most browsers, for positioning and hiding the popout. The public Functions on a new ImagePopout are ‘hide’ and ’show’, where ’show’ positions the popout in the center of the viewport, or top-left if the veiwport is too small. Lastly, the image has an onclick listener, which will iterate through the event regions that you passed in the ‘emaps’ variable, firing the appropriate callback Function.

The mouse click point of the event is compared to the position of the popout plus any coordinates you have provided, and when the mouse event occurs inside one of your regions, then the provided callback Function is fired. In Example 2, eventRegion1 could be some textual link that triggers a new page to open, and perhaps the eventRegion2 is an ‘X’ in the top right corner that closes the popout. ‘emaps’ is a collection of as many event regions as you desire, and will work even if you pass in an Array of length 0 (although you probably shouldn’t). Also, you may have overlapping regions and multiple callback Functions for the same point (say you want all clicks to close the popout, but you want specific clicks to also trigger another callback).

I have built this on YUI, but have also made it easy to use your own library by setting Library methods to local Functions. Simply, update the shortcut methods that I have used with the ones of your library (you may need to wrap them as I did with ‘getEventPos’ and ‘viewportSize’ to return the values in the right format).

Some improvements that you might think about are: a page mask, so that the page is not interact-able when the popout is visible; hiding the page ’select’ tags when viewed in IE 6 and lower, as they will most likely bleed through the image; changing the cursor to ‘pointer’ when hovering over a link; and passing a config property to allow better positioning and styling of the popout.

……………………….

Revised March 30, 2008
As it turns out I didn’t properly test the library function shortcuts with YUI. By assigning ‘YAHOO.util.Event.addListener’ to ‘addListener’ it breaks the scope of the execution of ‘addListener’, so referencing ‘this’ inside the Function does not work. To fix this, we need to use the ‘call’ method of the ‘addListener’ Function and pass in the scope of the object containing the ‘addListener’ method (in this case ‘YAHOO.util.Event’).

I have also added a test page to illustrate how this technqiue working.

posted by Matt Snider at 10:10 am  

6 Comments »

  1. great share. just a suggestion.
    you should add a demo page

    Comment by anthony — March 17, 2008 @ 8:28 am

  2. Thanks for sharing. But, cant get it to work. I get this error: this._isValidCollection is not a function. in the event.js file

    This occurs after the addListener(dom.img, ‘click’, function(e)… call in your code.
    Can you help? I would love to get this working

    Comment by Yannis — March 28, 2008 @ 11:36 am

  3. Yannis, I fixed your issue *_^

    Comment by Matt Snider — March 30, 2008 @ 5:17 pm

  4. This is great! really good explanation and commenting! I just want to have the image not show at first, until the link is clicked. In my case, I’m having a thumbnail link to a bigger picture.

    And also, is there a way to hide the link part?

    Again, I love this, it’s great!

    Comment by Keith — June 15, 2008 @ 3:31 am

  5. Keith,

    If you change the declaration to:

    var ImagePopout = function(bd, src, emap, hideOnLoad) {

    and add the following between lines 43 and 45:

    if (hideOnLoad) {
    dom.img.style.display = ‘none’;
    }

    then passing true as the 4th parameter will cause the popup to be hidden initially.

    Second, you don’t have to use the event map, you can just pass an empty array and no events will be assigned. However, you should probably assign an event to the whole image that hides so the user isn’t stuck with an image that they can’t get rid of.

    Comment by Matt Snider — June 17, 2008 @ 10:45 pm

  6. This is nice. I was looking for such script. I will be adding it to ‘popup’ section of http://www.downloadjavascripts.com

    Comment by deep — August 15, 2008 @ 12:51 am

RSS feed for comments on this post. TrackBack URI

Leave a comment

Powered by WordPress