Development Blog
  • Over at GetHiFi.com: Automatic file minification on nginx

    For our HiFi content management system we wanted to automatically combine and compress CSS and Javascript files. On complex sites, minification can have a significant impact on page load times. In this post I am going to discuss the technical details of how we set things up on our nginx server.

    Continue reading on the GetHiFi.com blog…

    Categorized in: ,

  • A jQuery Flickr Feed Plugin

    View the Demo | Download the Zip

    We often work with clients that maintain accounts with Twitter, Flickr, Youtube and other services in addition to their website. Often they will want to pull in data from one of their accounts to their website.  With Flickr, this is pretty easy because they make a simple API available.  Having worked with it a few times, we decided to make it even easier to pull photos from a public feed.

    flickr dogs

    There are a few examples of this in use on the demo page.  All of the photos on this page have been used with the generous permission of John Roberts (@jroberts13).

    Plugin Overview

    This plugin works by pulling a JSON feed from Flickr and applying the data it gets back to a template.  For example, we can generate this list of pictures:

    <ul>
    	<li><img alt="Photo Title" src="http://farm4.static.flickr.com..." /></li>
    	<li><img alt="Second Photo Title" src="http://farm4.static.flickr.com..." /></li>
    </ul>
    

    From the following jQuery:

    $('ul').jflickrfeed({
    	limit: 2,
    	qstrings: {
    		id: '44802888@N04'
    	},
    	itemTemplate: '<li><img alt="{{title}}" src="{{image_s}}" /></li>'
    });
    

    The plugin gets the feed from Flickr using AJAX and applies each image it gets back to the provided template.

    The Plugin Options

    There are a number of options available on the plugin, these are the defaults:

    flickrbase: 'http://api.flickr.com/services/feeds/',
    feedapi: 'photos_public.gne',
    limit: 20,
    qstrings: {
    	lang: 'en-us',
    	format: 'json',
    	jsoncallback: '?'
    },
    cleanDescription: true,
    useTemplate: true,
    itemTemplate: '',
    itemCallback: function(){}
    

    Here is a description of each one:

    • flickrbase: This is unlikely to be needed.  It is used to set up the url the jQuery AJAX call will need to be made to.
    • feedapi: There are a number of feeds that flickr makes available.  The default is the public feed.  Here is the list of all that are available: http://www.flickr.com/services/feeds/. This has only been tested on feeds that return photos.  So, for example, don't expect good results using this plugin on the Forum discussion feeds.
    • limit: Set how many items you want to loop through.  Flickr seems to limit its feeds to 20, so that is the default.
    • qstrings: This is the most important setting. This is used to request the correct feed.  In my examples I use this to set the user id.  These are automatically added to the request url. Depending on which feed you use (http://www.flickr.com/services/feeds/) there are different sets of query parameters available: http://www.flickr.com/services/feeds/.
    • cleanDescription: Flickr puts all kinds of junk in the description it returns. By default this plugin is set to remove everything but the plain photo description.
    • useTemplate: Set this to false if you don't want to use the plugins templating system.
    • itemTemplate: The template rules are described below.
    • itemCallback: You can add a callback on each item.  The scope is set to the container and the item object is made available.

    Typically, the only things that will need to be set are limit, qstrings.id and itemTemplate.

    Using the Templates

    In order to make it really easy to use any kind of markup needed with this plugin, a simple templating system has been build in.  Here is an example of a template:

    <li>
    	<a href="{{image_b}}"><img src="{{image_s}}" alt="{{title}}" /></a>
    </li>

    You can see that this is just basic html with a few special tags mixed in.  All of the tags are surrouned by double curly braces. The plugin works by putting in the correct information for each tag and each item into the template.

    The tags that are available depend on what flickr returns for each item.  For the Public Photos API these are: title, link, date_taken, description, published, author, author_id, tags, and image.  For the image property, the plugin uses the Flickr URL scheme to make several sizes available.  You can read about this at http://www.flickr.com/services/api/misc.urls.html.  The image tags available are: image_s, image_t, image_m, image, image_b.

    The Callback Parameter and Integration

    The plugin's second parameter is a callback.  This has the scope of the containing element and has the entire data response available. This is a great feature if you want to integrate this plugin with others.  Let's say you want to integrate it with colorbox. If you call $('selector').colorbox() and then this plugin it won't work.  This is becuase the plugin is adding elements to the page after the colorbox call.  This is even true if you call colorbox after this plugin.  Since this uses ajax, your images won't be added to the page until well after most of your javascript has run.  (In computer time).

    Instead, the trick is to use the callback function.  This allows you to pass in code that you want to run after the images have loaded.  So for example, you can do this:

    $('#cbox').jflickrfeed({
    	limit: 14,
    	qstrings: {
    		id: '37304598@N02'
    	},
    	itemTemplate: '...example...'
    }, function(data) {
    	$('#cbox a').colorbox();
    });
    			

    Now the colorbox call won't be made on the photos until they are loaded.

    Demo and Download

    You can see a demo of this plugin on the demo page.  Additionally you can download a zip of this plugin and the example.

    As always, if you have any questions or comments, leave them below.  Enjoy!

    Categorized in: ,

  • Big Relaunch of the GetHiFi.com Site

    For some time now we've been working on our new Web Marketing platform, HiFi.  We first launched a marketing site for it back in September.  At that point it was just a simple landing page with a cool rainbow header.

    This time around, we worked with a local designer, Lenny Terenzi to get a really killer design.  We just re-launched GetHiFi.com on the design last week.  Today, we put up a 38-slide slideshow that documented the process of taking Lenny's complex design and turning it into a website.  We thought it would be interesting to followers of this blog since it covers HTML5, CSS Sprites and a bit of jQuery.  So if that is something you're interested in, head over to the post to read more about it:

    Building a Complex, High-Performance Site with HTML5, CSS, and jQuery

    HiFi Slideshow

    Categorized in: , , ,

  • Reminder: Join Us Today at 4:00 EST for Our Virtual Open House

    Today's the day, folks!  We're hosting our inaugural Virtual Open House at 4 pm EST.  All you have to do is go to newmediacampaigns.com/openhouse at 4 EST and our entire team will be in there waiting.

    The open house is being held in the chat program, 37signals' campfire, we use all day at NMC.  Our team consists of highly regarded designers, developers, and online strategists with experience in every vertical.  Not to mention, we consider ourselves pretty friendly. We're happy to answer any questions you may have, whether they're related to your website, jQuery, PHP, Buzz, anything!

    If you're a reader of this blog or a follower or fan of ours in social media, today's an excellent opportunity to meet the folks behind NMC.  Really hope to see you there!

    Categorized in: , ,

  • NMC Holds Virtual Open House on Friday February 12th at 4:00 EST

    nmc real office

    Last week, Clay Schossow was interviewed by Inc. about our "virtual" work environment.  You can read the article online on the Inc. Blog.  As a quick summary, back in March one of our top developers, Josh Lockhart moved to Madison, WI but stayed with us, working virtually.  It started as an experiment for us but has become a great success, leading to more freedom for our NC employees.  This is partially due to our use of a Campfire and Basecamp, two products made by 37Signals.

    Campfire is a company chat room that we all log in to each morning.  Whether we are working in our offices, at the coffee shop next door, or in Madison, WI, it allows everyone at NMC to keep in touch.  One of the great features of Campfire is that it allows guests to join the chat.

    This Friday, we're going to open up our chat room to our customers, blog subscribers and anyone else that wants to join.  Everyone from NMC will be there, ready to answer any questions you might have.  Whether it is about your website, one of our jQuery plugins, what it is like to work virtually -- anything.

    All you'll need to do to join is head to newmediacampaigns.com/openhouse on Friday at 4:00.  We look forward to this experiment and getting to know you all better.

    Categorized in: , ,

  • Tips for Developing a Custom Design for HubSpot CMS

    At NMC, we develop 95% of our projects on our own Content Management System.  Our system gives designers total flexibility and is very intuitive for end users, so it ends up being a good fit for just about all of our clients.  However, occasionally clients come to us with a strong backend preference for certain reasons.  This has happened a few times with the HubSpot CMS, due to the system's helpful business analytics and supporting community.  

    The HubSpot CMS wasn't originally developed for custom designs and is more geared toward the company's templates.  However, with some elbow grease, you can get a custom look on there that follows HubSpot's requirements.  We have now designed a couple of custom HubSpot sites (including SLX for Sun Microsystems) and put together this brief tutorial on some shortcuts to freely developing on the system.

    Developing on an unfamiliar platform has it challenges, most of which is relinquishing control. For me, not having control of front-end conventions is frustrating. As a web standards enthusiast I’ve established patterns during the build-out of sites designed to minimize browser errors and cut down on development time.

    So when it came time to dive in and work with the Hubspot CMS for the Yamaha Music School of Boston and Sun Microsystems' SLX, I went into the projects with the mild hesitation of having no idea what my experience would be like. Thankfully, I was pleasantly surprised with what I was able accomplish through some tricks and ingenuity. Here are a few tips and recommendations I’ve learned from my experience from using the Hubspot’s Business Website Manager:

    1. Utilize the Color Scheme Configuration settings
    Hubspot Color Scheme Configuration
    Hubspot’s system generates a CSS styles that are placed within the body tags, taking precedence of entire styles sheets I load to the DOM. Since the design specs I had to work with matched one of the systems page templates I thought it would be best to work within the confines of the system. This turned out to save me a lot of headaches later.

    2. !important important important
    Everything I couldn’t accomplish within the Color Scheme Configuration I wrote into a new CSS file based on some id and class selectors created by Hubspot’s templating system. After uploading a file through the system’s File Manager and linking that file into the header using the Website Setting, I noticed some of file’s declarations weren’t rendering in any browsers. After a few minutes of digging I realized the styles were being overwritten by styles loaded further down in the cascading order. Adding ’!important’ to the end of css declaration value give it precedence over all other ‘author’ and ‘user agent’ styles, or styles created by the developer and browser:

    h1{font-weight:bold!important;}

    3. Use HTML / JavaScript to add custom code
    Using JavaScript on HubSpot
    When you need to add static content or custom images, you need to add a new page Module. Given all Module options, I recommend using plain markup. You can create things with a fresh slate and not be tied down by the templating system’s css conventions and markup. When you’re configuring your new module make sure you select the options to not use the module title and to use plain formatting. With those two items selected, you now have that clean slate to work with.

    If any of you are charged with developing a site on HubSpot, hopefully these tips are helpful in tackling the project.  While HubSpot's system isn't ideal for totally custom designs, with some extra work, you can still put together an attractive site.

    Categorized in: ,

  • AutoSprites - A jQuery Menu Plugin

    Update 1/22/2009 - This plugin now contains support for an active state. It is just as easy to use and all of the animation still works with it. Read below to see how it works. (This was originally posted 10/28/2009)

    AutoSprites jQuery Menu Plugin

    Download the Files (Zip) | View a Demo

    We have written and released a jquery dropdown menu plugin as well as a CSS Sprites2 Plugin -- this post is along the same lines.  Its purpose is to allow you to build an image-based menu with animated hover states as easily as possible and by using the most concise descriptions possible.  To see the results on both a horizontal and vertical menu, check out the demo.

    Setting up the jQuery Menu Plugin

    The first component when doing something with sprites is a combined image that contains all menu states.  For the menu above, this is the image below was used.  (This was designed by Liaison Design Group, one of our Partners)

    sprites

    The image contains, the normal state, the hover state and the active state. The value to doing things this way is that it allows your site to load faster. Rather than downloading an image for each nav item its hover state and its active state, only a single image needs to be downloaded. This minimizes the overhead of many http requests.

    The next thing to do, is set up the HTML for the nav bar:

    <ul id="hnav"> 
    	<li id="hnavhome"><a href="#">Home</a></li> 
    	<li id="hnavlocal"><a href="#">Local Industry</a></li> 
    	<li id="hnavhigher"><a href="#">Higher Education</a></li> 
    	<li id="hnavcomm"><a href="#">Our Community</a></li> 
    	<li id="hnavnews"><a href="#">News</a></li> 
    
    </ul>
    

    Then we need to set up the CSS. There are a couple of things to note here. We are applying the background image to the containing element so that we don't need to respecify background positioning. This also makes the menu usable if javascript is disabled. Each element needs to have its size defined specifically as well.

    #hnav { position: absolute; top: 0; left: 0; width: 615px; height: 72px; background: url('horiz_sprites.gif') no-repeat; }
    #hnav li { position: absolute; left: 0; height: 72px; }
    	#hnav #hnavhome { width: 82px; left: 0px; }
    	#hnav #hnavlocal { width: 146px; left: 82px; }
    	#hnav #hnavhigher{ width: 162px; left: 228px; }
    	#hnav #hnavcomm { width: 143px; left: 390px; }
    	#hnav #hnavnews { width: 82px; left: 533px; }
    #hnav li a { display: block; position: absolute; top: 0; left: 0; width: 100%; height: 72px; text-indent: -9999em; }
    

    Notice how much less complicated the CSS is than what is typical with sprites. There is no need to define background positioning for each element and its hover state. The last piece you'll need to do is enable the autosprites plugin:

    $(document).ready(function(){
    	$('#hnav').autosprites();
    });
    

    There are no required options. The plugin defaults to a horizontal menu and fading for the animation. It infers everything else from the CSS. If you would like to customize things, here are the options that are available:

    settings = $.extend({
    	offset: '100%',
    	orientation: 'horizontal',
    	over: { opacity: 'show' },
    	overSpeed: 500,
    	out: { opacity: 'hide' },
    	outSpeed: 500,
    	activeState: false,
    	activeClass: 'active',
    	activeSprites: false
    }, settings);
    

    The only bit worth explaining is the active state. In the image above, I show three states. By default, you only need two, a normal state and a hover state. If you set 'activeState' to true, it will use the hover state by default. If you want to specify your own active state, simply set 'activeSprites' to true as well.

    So that's it! You can specify the minimum amount of information about your menu and the sprites will be built automatically. Be sure to check out the demo and download the zip for your own projects. As always leave a comment if you have any complements, insults, suggestions or questions.

    A Special Note on Compiling Javascript

    The purpose of using sprites is to minimize HTTP requests.  It would be foolish to use this plugin by including jQuery, the minimized plugin, and a setup script.  Instead, it is best practice on a production site to bring all of your scripts together into one file, just as sprites bring your images into one file.

    Categorized in: ,

  • Tutorial: A Horizontal jQuery Accordion using Custom Event Binding

    This is the second of two posts about a site we recently launched for a non-profit called Striving for More. This is a great new organization that has started with the goal of improving overall care for young cancer patients. It was a fun and deserving project with a great design:

    Striving for More

    The first post was about how to build a custom jQuery Slideshow on top of some popular jQuery plugins. It covered the required html, css and js needed and it also gave a look into the through process that went into it. This post will take the same approach, but instead look at the custom jQuery horizontal accordion that is on the homepage. Unlike the last project, this one will be build without the help of any plugins. It was created entirely from scratch using jQuery custom events.

    Here is a look at the final product.

    Horizontal jQuery Accordion

    What is it?

    Essentially, this is a horizontal accordion that also changes the text below it on each transition. The one difference between this and a traditional accordion is that when a slide becomes active or inactive, it doesn't just shrink. Instead it slides behind the other slides. Click around on it above to see what I mean.

    The HTML

    The html for this project is really straightforward. There is a group of slides and a group of content:

    <div id="slideshow">
    	<ol id="slides">
    		<li class="slide open" id="slide-[SLIDE NUMBER]">
    			<a href="[SLIDE LINK]">
    				<img src="[SLIDE IMAGE]" alt="" />
    			</a>
    			<a class="slidebutton" href="javascript:void(0);"><img src="[SLIDE BUTTON]" alt="" /></a>
    		</li>
    		...
    	</ol>
    	<ol id="slidecontents">
    		<li class="slidecontent" id="content-slide-[SLIDE NUMBER]">
    			[SLIDE CONTENT]
    		</li>
    		...
    	</ol>
    </div>
    

    The html is in two logical groups, the slide images/navigation and the slide content. These are paired by convention using programatic id attributes. Notice that each of the image slides and the content slides has an id with the [SLIDE NUMBER] in it. This makes the javascript much simpler.

    Ideally the content would be grouped in with the images. This would have made for better markup, as the related items would be put together. This would have required a lot of css positioning trickery since the content wasn't supposed to move with the slide. For the sake of overall simplicity, some html complexity was added.

    The last thing worth noting here is that the slide button text is actually a rendered image. That text can't be rotated reliably in all browsers is really sad. I am confident that this will be accomplished shortly with Canvas. If you're wondering which browsers are the offenders here, you'll probably be surprised. Text can be rotated reliably in EVERY major browser except for Firefox 2.0. It even works in IE. Alas.

    The CSS

    The CSS for this project is probably the simplest part. You'll only notice one little quirk:

    #slides { position: absolute; top: 21px; left: 22px; width: 577px; height: 285px; overflow: hidden; }
    #slides .slide { position: absolute; top: 0px; width: 541px; }
    #slides .slide img { position: absolute; top: 0px; left: 0px; }
    #slides .slide .slidebutton { display: block; position: absolute; top: 0px; right: 0px; height: 285px; width: 21px; background: #693d5e; text-decoration: none; border-right: 1px solid white; }
    #slides .active .slidebutton { background: #55354a; }
    #slides .slide .slidebutton img { position: absolute; top: auto; display: block; bottom: 5px; left: 5px; }
    /* Manually place slides to begin */
    #slide-1 { position: absolute; top: 0; right: 44px; z-index: 3; }
    #slide-2 { position: absolute; top: 0; right: 22px; z-index: 2; }
    #slide-3 { position: absolute; top: 0; right: 0px; z-index: 1; }
    

    It's all straighforward except for that bit at the bottom. I've hardcoded the number of slides. This is typically something I like to avoid, but there were a couple of reasons for it on this project:

    1. I was assured that these slides would be unchanged for quite some time.
    2. At least some CSS Positioning would be required so the page wouldn't look broken while the JS loaded. By being exact there would be no jarring readjustment at all.
    3. It was easier.

    These reasons made the decision obvious. As a quick aside and bonus, here is the css that could be used to rotate the text in CSS. Note that this depends on slightly different markup and it just won't work in Firefox 2.0. So remeber that until FFox2.0 disappears this is useless, though fascinating.

    #slides .slide .slidebutton span 
    { display: block; text-align: right; -webkit-transform: rotate(90deg);  -moz-transform: rotate(90deg);
    						position: absolute; bottom: 100px; left: -90px; width: 200px;
    						color: #f2ecd0; font-family: verdana, sans-serif; font-size: 12px; }
    /* Put the following in your IE stylesheet */
    #slides .slide .slidebutton span { bottom: 190px; left:2px; filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); }
    

    The jQuery

    The jQuery for this little project was the most fun. It ended up being very compact due to the intial positioning of the slides using CSS and because I used custom events.

    What are custom events? In short, they are functions that you can bind to jQuery selectors. This lets you associate actions with the objects that do them, rather than with what triggers them. As an example, you could bind the event 'toggle' to a lightbulb and then trigger it from any number of switches. So if you have multiple switches, they all don't need to keep track of the lightbulb's state. They can just call toggle and the bulb will handle it. (If you like this example, don't credit me. It's borrowed from a great article on jQuery Custom Events written by Rebecca Murphey).

    So here is the entirety of the jQuery code:

    /* Binding Events to the slides */
    $('.slide')
    	.bind('open', function(){
    		if(! $(this).hasClass('open')){
    			$(this).next().trigger('open');
    			$(this).addClass('open');
    			$(this).animate({right: "-=511px"});
    		}
    		else{
    			$(this).prev().trigger('close');
    		}
    		$(this).siblings().removeClass('active');
    		$(this).addClass('active');
    	})
    	.bind('close', function(){
    		if($(this).hasClass('open')){
    			$(this).removeClass('open');
    			$(this).animate({right: "+=511px"});
    			$(this).prev().trigger('close');
    		}
    	});
    		
    /* Binding Events to the Slide Contents */
    $('.slidecontent').bind('show', function(){
    	$('.slidecontent').removeClass('open');
    	$(this).addClass('open');
    });
    
    /* Triggering from the buttons */
    $('.slidebutton').click(function(){
    	$(this).parent().trigger('open');
    	$('#content-' + $(this).parent().attr('id')).trigger('show');
    });
    

    This is certainly terse, so it is worth explaining. I'll start with the events we are binding to '.slide', open and close. To set the stage, remember that each slide is either open (navigation on the right), or closed (navigation on the left). I am keeping track of this by applying an 'open' or 'closed' class to each slide. This is a fairly standard technique.

    Where things might get a bit tricky lie within how an accordion works. The visibile slide isn't the only slide that is open. The slides that come after it must be open as well (their navigation is on the right). So when the second slide is active, the first must be closed and the second and third must be open. This problem gets handled with a little big of recursion.

    The Event Binding

    Let's look at the close event, as it is the simpler of the two. When we close a slide, we first check that it is open. If it is closed, there is nothing to do. If it is open, we close it and then close the slide immediately to its left. This repeats until there isn't a slide to the left or it reaches a slide that has already been closed.

    The open event is only a little more complicated. It works in the exact same way as the close event, except trickles to the right. It also updates which slide is active at each stop along the way.

    Finally you can see how the 'show' event is bound to the slide contents. All this does is remove an open class from every slide content, and then add it back to the one that has been triggered.

    As you can see, a lot of complexity has been encapsulated under these events. This makes it really easy to jump to a particular slide. All that needs to happen is to trigger the 'open' event on the desired slide and the 'show' event on the correct content. This is exactly what the '.slidebutton' code does.

    Putting it all together

    With all of the pieces in place you can see it in action below. Be sure to leave any questions or comments you have about this in our comments below.

    jQuery Horizontal Accordion

    Categorized in: ,

  • Tutorial: A jQuery Slideshow for a Good Cause

    We recently launched a site for a new non-profit called http://www.strivingformore.org" target="_blank">Striving for More. The project was a lot of fun. The site is great-looking, has some cool features, and more importantly it was for a good cause.

    In this post, I am going to cover one of the more important features of the site: an interactive gallery that tells Colleen's Story. I'll breakdown how it was built and the reasons for doing some of the things the way I did. It ends up being very simple and a great example of the power that can be had by combining and tweaking jQuery plugins.

    Here is the final product so you can see where this is headed:

    What is it?

    Colleen's Story is a simple slideshow that can be navigated in any order with an introduction screen. Each slide has a nice navigation button, some narrative and a cool graphic. Because we didn't want the navigation to move around, the content needed to scroll in place, another requirement. For something that looked this good, overflow: auto; wouldn't cut it. After doing some looking around, I decided to use the following plugins for the project:

    • Cycle - This is one of my favorite plugins. If you haven't checked it out you need to. With some improvments that have come in the last 6 months or so it is the only plugin you'll ever need for rotating content. We've rolled our own cycle-like jquery code in the past, but that is rarely necessary. On this project, Cycle will handle all of the slide transitions as well as the navigation.
    • jScrollPane - I'm not generally a fan of replacing the default behavior of browsers, but this situation required it. I played around with serveral plugins before settling on this one which seems to have the fewest downsides.

    The HTML

    To use these plugins will require that we structure our html in a certain way. Here is how it is setup:

    <div id="gallery">
    	<div id="gallery-intro">
    		[Introduction Content]
    		<a href="javascript:void(0);" id="startslides">Begin</a>
    	</div>
    	<div id="gallery-slides" class="hidden">
    		<ol>
    			<li id="slide-[slidenum]">
    				<div class="slide-content">
    					<h2>[Slide Title]</h2>
    					<div class="scroll-content">
    					[Slide Content]
    					</div>
    				</div>
    				<img class="slide-image" src="[Slide Image]" alt="" />
    			</li>
    			...
    		</ol>
    	</div><!-- gallery-slides -->
    	<div id="gallery-nav">
    		<ol>
    			<li id="slide-1-nav">1. Monkey</li>
    			<li id="slide-2-nav">2. Necklace</li>
    			<li id="slide-3-nav">3. IV Beads</li>
    			<li id="slide-4-nav">4. Bag</li>
    			<li id="slide-5-nav">5. Blistex</li>
    			<li id="slide-6-nav">6. Notebook</li>
    			<li id="slide-7-nav">7. Pink Hair</li>
    		</ol>
    	</div><!-- gallery-nav -->
    </div><!-- gallery -->
    

    There are some things to point out here:

    • The Gallery Intro

      Before the slideshow is shown, a description of its purpose is shown. This is grouped independently of the slides. The plan is to make this disappear once the slideshow is started.

    • The group of slides

      All Cycle asks for is a group of slides that are children to one element. In this case it is the ol. We'll end up calling cycle on "#gallery-slides ol". Also notice that I have given this the class "hidden". I do this so that I can hide the slides with css rather than javascript. This is important to do, otherwise while the page loads they will flash.

    • The Navigation

      Cycle provides a navigation (pager) generator. For this project I wanted to have all of my html in one place and give good text descriptions for each slide. Having this dynamicly match the number of slides didn't matter either because these were set in stone. Instead, I just used an id attribute convetion so I could write some simple javascript to link the navigation to the slides.

    • Scroll-Content Class

      I wrapped the content of each slide in a container with the class "scroll-content". This is so that we can target it if necessary using the jScrollPane plugin.

    The CSS

    A concern with this site due to the texture used was download size. While the end result is not perfect, it was taken into consideration when putting this together. The overall strategy was to use one background image for the slideshow frame and let the slide text, images and navigation float over it. One concession was to also include some of the background in the slide images. While this made for some redundant information, the use of jpg compression instead of a 24-bit or even 8-bit png more than made up for it. Finally, sprites were used for the navigation.

    Here is the major layout css:

    /* Container */
    #gallery { position: relative; width: 696px; height: 494px; background: url('gallery-background.jpg'); }
    
    /* Intro Container */
    #gallery-intro { position: absolute; top: 8px; left: 9px; height: 268px; width: 459px; padding: 70px 110px;
    				font: italic 16px Georgia,serif; color: #1e5584; line-height: 32px; text-align: center; }
    #gallery-intro strong { color: #bc9906; display: block; font-weight: normal; }
    #startslides { width: 116px; height: 30px; display: block; margin: 20px auto; background: url('gallery-begin.jpg'); text-indent: -9999em; }
    
    /* Slides */
    #gallery-slides { position: absolute; top: 8px; left: 9px; height: 408px; width: 679px; overflow: hidden; background: transparent; }
    #gallery-slides ol { position: absolute; top: 0; left: 0; list-style: none; margin: 0; padding: 0; width: 679px; height: 408px; background: transparent; }
    #gallery-slides ol li { width: 629px; height: 338px; padding: 25px; }
    
    /* Slide Components */
    #gallery-slides .slide-content { width: 340px; padding-right: 20px; height: 338px; line-height: 18px; z-index: 2; }
    #gallery-slides .scroll-content { overflow: auto; width: 320px; padding-right: 20px; height: 315px; }
    #gallery-slides .slide-content p { margin: 6px 0; }
    #gallery-slides .slide-content h2 { margin: 0px 0 6px 0; font: italic 25px Georgia,serif; color: #bc9906;  }
    #gallery-slides .slide-image { position: absolute; bottom: -25px; right: 1px; z-index: 1; }
    

    The quick things to notice are:

    • The container is positioned relative so that everything else can be placed absolute inside of it.
    • ".scroll-content" has overflow set to auto. This is required for the jScrollPane plugin.

    The navigation was also positioned absolute and set up with a basic CSS sprites technique. You can see the image used here. I am using '.on' in addition to the ':hover' pseudo-class so that we can get the hover states to work in IE6 using javascript. You can get more information on this technique in detail in a classic article on A List Apart. For those of you interested in using the technique for navigation menus, I've also written a plugin to make that dead simple called jQuery AutoSprites.

    #gallery-nav { position: absolute; top: 425px; left: 8px; width: 680px; height: 50px; }
    #gallery-nav ol { position: absolute; margin: 0; padding: 0; top: 0; left: 0; width: 680px; height: 50px; background: url('gallery-sprites.jpg'); }
    #gallery-nav ol li { list-style: none; margin: 0; padding: 0; position: absolute; top: 0; left: 100px; width: 85px; height: 50px; text-indent: -9999em; cursor: pointer; cursor: hand; }
    #gallery-nav ol li.activeslide, #gallery-nav ol li.hov { background-image: url('images/gallery-sprites.jpg'); }
    	#gallery-nav #slide-1-nav { left: 0px; width: 100px; }
    	#gallery-nav #slide-1-nav.activeslide, #gallery-nav #slide-1-nav.hov{ background-position: 0px -50px; }
    	#gallery-nav #slide-2-nav { left: 97px; width: 100px; }
    	#gallery-nav #slide-2-nav.activeslide, #gallery-nav #slide-2-nav.hov { background-position: -97px -50px; }
    	#gallery-nav #slide-3-nav { left: 194px; width: 100px; }
    	#gallery-nav #slide-3-nav.activeslide, #gallery-nav #slide-3-nav.hov { background-position: -194px -50px; }
    	#gallery-nav #slide-4-nav { left: 291px; width: 100px; }
    	#gallery-nav #slide-4-nav.activeslide, #gallery-nav #slide-4-nav.hov { background-position: -291px -50px; }
    	#gallery-nav #slide-5-nav { left: 388px; width: 100px; }
    	#gallery-nav #slide-5-nav.activeslide, #gallery-nav #slide-5-nav.hov{ background-position: -388px -50px; }
    	#gallery-nav #slide-6-nav { left: 485px; width: 100px; }
    	#gallery-nav #slide-6-nav.activeslide, #gallery-nav #slide-6-nav.hov { background-position: -485px -50px; }
    	#gallery-nav #slide-7-nav { left: 582px; width: 100px; }
    	#gallery-nav #slide-7-nav.activeslide, #gallery-nav #slide-7-nav.hov { background-position: -582px -50px; }
    

    The Javascript

    Since our plugins come with quite a bit out of the box it is really simple to get the jQuery running. The first step was to handle the items hidden by css I described above. We can do this right away since the javascript will execute quickly once it gets going as it will be replaced bya jQuery hide.

    $('.hidden').removeClass('hidden');
    $('#gallery-slides').hide();
    

    Next I got the navigation hover states working in IE6 which needs javascript since it doesn't recognize the :hover psuedo-class on non-anchor elements.

    $('#gallery-nav ol li').hover(
    	function(){ $(this).addClass('hov'); },
    	function(){ $(this).removeClass('hov'); }
    );
    

    Next was the simple step of enabling jScrollPane. Don't forget to set these containers as overflow:auto in the css too.

    $('#gallery-slides .scroll-content').jScrollPane({
    	scrollbarWidth: '10'
    });
    

    Setting up a jQuery Cycle is easy, but there was a trick needed for IE. Because I used the fade effect, Cycle sets a background color during the transition to get the text fading to work correctly. I reset this to transparent once the transition is over using the after callback. While this may seem like a pain, little features like this are part of the reason to use established plugins over something homegrown.

    $('#gallery-slides ol').cycle({
    	timeout: 0,
    	fx: 'fade',
    	after: function(){
    		$('#gallery-slides ol li').css('background', 'transparent');
    	}
    });
    

    The last step is adding the custom navigation features. You'll notice that the id of the nav item is used to figure out which slide to show. I also set up the swap between the intro and the slides to occur if either the navigation or "Begin" button is pressed.

    $('#gallery-nav ol li').click(function(){
    	$('#gallery-slides').fadeIn();
    	$('#gallery-intro').fadeOut();
    	$(this).parent().children().removeClass('activeslide');
    	$(this).addClass('activeslide');
    	var slidenum = Number($(this).attr('id').substr(6,1)) - 1;
    	$('#gallery-slides ol').cycle(slidenum);	
    });
    $('#startslides').click(function(){
    	$('#gallery-slides').fadeIn();
    	$('#gallery-intro').fadeOut();
    	$('#slide-1-nav').trigger('click');
    });
    

    Conclusion

    So that's it. Once again you can check out the final product on the Striving for More site. Next week I'll post a tutorial about how the homepage slider was done entirely from scractch. If you want to read about that when it comes out, you should subscribe to the blog.

    Categorized in: ,

  • Season's Greetings and Happy New Year from the NMC Team!

    With the holiday season and a lot of projects wrapping up, it's been rather quiet on the blog for the past few weeks.  Just wanted to wish all of our readers a Happy 2010 and thank them for their support over the past year.  Below is a blast email we sent out to clients earlier today.  One of our priorities in 2010 is to send quarterly updates to our clients, readers, and others.  If you'd like to receive these, please fill use the form in the sidebar to receive our newsletter.

    Season's greetings and happy New Year from the NMC team!

    2009 was a fun and busy year at New Media Campaigns. Our team doubled in size, we launched over 100 sites, we recommitted to our blog, and we even had two people get engaged! Through all of these happenings, we were proud to be able to stay true to our core principles of quality work at affordable prices with great service.

    However, we recognize none of this would have been possible without your support, feedback, and business. We truly appreciate it all, and we want to wish you a happy holiday season and a joyous 2010!

    Below are some more highlights from the past year. We look forward to making 2010 an even bigger success!

    Best,

    The New Media Campaigns Team

    PS - To keep up with our daily happenings in 2010, follow us on Twitter, join our somewhat lonely Facebook page, and subscribe to our Blog.

    NMC News Highlights from 2009

    Categorized in: , ,