



How CSS can be used to allow images to stretch or compress within liquid layouts thus preventing layout breaks.
Author: Phil Smears
Date added: 5th June 2007
One of the problems with liquid layouts has been the affect of large images within a content area on layout. In small window sizes, these content area images prevent the layout shrinking to fit the window and can have catastrophic consequences. What would be nice is images that could shrink or expand whith the content area when necessary. This article shows how this can be achieved.
The home page which I did for Scotrail has three boxes, 'For business', 'For leisure', 'For commuters' all of which contain an image which is 235px in width.
The right hand content area belonging to <div id="rightContent"> has a left margin of 27%. So let's do a little maths here. On an 800 x 600 monitor we can reasonably assume we have 760px actually available for a web page. Take away 27% of that and we are left with 554.8 px for our right content area. As well as that the three boxes sit inside <div id="rightContent"> which has width 98%. So we only have 543px of width available.
Now last time I looked 3 times 235 doesn't go into 543 a whole number of times. Does the layout break at 800 x 600? No. Why not? Because the three box images start to compress when monitor size gets too small.
Here's the HTML:
<div id="threeBoxes"> <div class="box"> <div class="pic"> <img src="index_files/handshake.jpg" alt="" id="stretch"></div> <div id="box1"> <h2>For <span>business</span></h2> <ul> <li><a href="http://www.firstgroup.com/scotrail/content/travelinfo/station-info.php">Station facilities</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/travelinfo/firstclass.php">First class</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/caledoniansleeper/index.php">Caledonian sleepers</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/travelinfo/ticketsandfares.php?pageid=00000000071&ms=sub">Onboard facilities</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/timetables/index.php">Journey times</a></li> </ul> </div> <p><a href="http://www.firstgroup.com/scotrail/content/buytickets/index.php"><img src="index_files/buyTickets.jpg" class="butt" alt="Buy Tickets"></a></p> </div> <div class="box"> <div class="pic"><img src="index_files/football.jpg" alt=""></div> <div id="box2"> <h2>For <span>leisure</span></h2> <ul> <li><a href="http://www.firstgroup.com/scotrail/content/travelinfo/ticketsandfares.php">Fares and tickets</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/caledoniansleeper/index.php">Caledonian Sleepers</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/travelinfo/ticketsandfares.php?pageid=00000000068&ms=sub">Days out and attractions</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/promo-registration/index.php">Register for offers</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/eventsandattractions/index.php">Up and coming events</a></li> </ul> </div> <p><a href="http://www.firstgroup.com/scotrail/content/buytickets/index.php"><img src="index_files/buyTickets.jpg" class="butt" alt="Buy Tickets"></a></p> </div> <div class="box"> <div class="pic"> <img src="index_files/station.jpg" alt=""></div> <div id="box3"> <h2>For <span>commuters </span></h2> <ul> <li><a href="http://www.firstgroup.com/scotrail/content/journeyalert/index.php">Timetable alerts</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/travelinfo/ticketsandfares.php?pageid=00000000012&ms=sub">Season ticket information</a></li> <li><a href="http://www.firstgroup.com/scotrail/advance/index.php">Loyalty club</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/travelinfo/ticketsandfares.php">Special offers and promotions</a></li> <li><a href="http://www.firstgroup.com/scotrail/content/travelinfo/ticketsandfares.php">Fares and tickets</a></li> </ul> </div> <p><a href="http://www.firstgroup.com/scotrail/content/buytickets/index.php"><img src="index_files/buyTickets.jpg" class="butt" alt="Buy Tickets"></a> </p> </div> </div>
Here's the CSS:
#threeBoxes {
height: 1%; // kicks ie6 into hasLayout mode (http://www.satzansatz.de/cssd/onhavinglayout.html)
overflow:hidden;
width:98%;
}
#threeBoxes div.pic {
background-color:#060C35
}
#threeBoxes div.pic img{
width:100%;
height:7.4em;
max-width:235px;
}
#threeBoxes div.box {
float:left;
width:32.3%;
background: #0B1867 url(../images/template/NavyBack.jpg) repeat-x 0 0 ;
color:#fff;
margin-right: 1%;
}
The main thing to note is that each image width has been set to 100% of its container div but at the same time a maximum width of 235px, the 'true width', has been set so image doesn't look pixellated on large monitors, ie, 1024 and above. A fade in background colour of #0B1867 fills in the gap on the right hand side of each image on larger monitors. Users of smaller monitors get a decent layout although slightly compressed image which seems a reasonable balance to strike. I'd say that was better than enforcing a minimum width and users of 800 x 600 monitors having to scroll horizontally.
To get max-width to work on IE6 there are a number of solutions and I used the following conditional comment to serve up a bit of JavaScript. It's not my JavaScript as my JS is pretty limited, so author's details are included:
<!--[if lte IE 6]><link href="css/ie6win.css" rel="stylesheet" type="text/css"><script type="text/javascript" src="scripts/minMax.js"></script><![endif]--> // minmax.js: make IE5+/Win support CSS min/max-width/height // version 1.0, 08-Aug-2003 // written by Andrew Clover and@doxdesk.com, use freely @cc_on @if (@_win32 && @_jscript_version>4) var minmax_elements; minmax_props= new Array( new Array('min-width', 'minWidth'), new Array('max-width', 'maxWidth'), new Array('min-height','minHeight'), new Array('max-height','maxHeight') ); // Binding. Called on all new elements. If <body>, initialise; check all // elements for minmax properties function minmax_bind(el) { var i, em, ms; var st= el.style, cs= el.currentStyle; if (minmax_elements==window.undefined) { // initialise when body element has turned up, but only on IE if (!document.body || !document.body.currentStyle) return; minmax_elements= new Array(); window.attachEvent('onresize', minmax_delayout); // make font size listener em= document.createElement('div'); em.setAttribute('id', 'minmax_em'); em.style.position= 'absolute'; em.style.visibility= 'hidden'; em.style.fontSize= 'xx-large'; em.style.height= '5em'; em.style.top='-5em'; em.style.left= '0'; if (em.style.setExpression) { em.style.setExpression('width', 'minmax_checkFont()'); document.body.insertBefore(em, document.body.firstChild); } } // transform hyphenated properties the browser has not caught to camelCase for (i= minmax_props.length; i-->0;) if (cs[minmax_props[i][0]]) st[minmax_props[i][1]]= cs[minmax_props[i][0]]; // add element with properties to list, store optimal size values for (i= minmax_props.length; i-->0;) { ms= cs[minmax_props[i][1]]; if (ms && ms!='auto' && ms!='none' && ms!='0' && ms!='') { st.minmaxWidth= cs.width; st.minmaxHeight= cs.height; minmax_elements[minmax_elements.length]= el; // will need a layout later minmax_delayout(); break; } } } // check for font size changes var minmax_fontsize= 0; function minmax_checkFont() { var fs= document.getElementById('minmax_em').offsetHeight; if (minmax_fontsize!=fs && minmax_fontsize!=0) minmax_delayout(); minmax_fontsize= fs; return '5em'; } // Layout. Called after window and font size-change. Go through elements we // picked out earlier and set their size to the minimum, maximum and optimum, // choosing whichever is appropriate // Request re-layout at next available moment var minmax_delaying= false; function minmax_delayout() { if (minmax_delaying) return; minmax_delaying= true; window.setTimeout(minmax_layout, 0); } function minmax_stopdelaying() { minmax_delaying= false; } function minmax_layout() { window.setTimeout(minmax_stopdelaying, 100); var i, el, st, cs, optimal, inrange; for (i= minmax_elements.length; i-->0;) { el= minmax_elements[i]; st= el.style; cs= el.currentStyle; // horizontal size bounding st.width= st.minmaxWidth; optimal= el.offsetWidth; inrange= true; if (inrange && cs.minWidth && cs.minWidth!='0' && cs.minWidth!='auto' && cs.minWidth!='') { st.width= cs.minWidth; inrange= (el.offsetWidth<optimal); } if (inrange && cs.maxWidth && cs.maxWidth!='none' && cs.maxWidth!='auto' && cs.maxWidth!='') { st.width= cs.maxWidth; inrange= (el.offsetWidth>optimal); } if (inrange) st.width= st.minmaxWidth; // vertical size bounding st.height= st.minmaxHeight; optimal= el.offsetHeight; inrange= true; if (inrange && cs.minHeight && cs.minHeight!='0' && cs.minHeight!='auto' && cs.minHeight!='') { st.height= cs.minHeight; inrange= (el.offsetHeight<optimal); } if (inrange && cs.maxHeight && cs.maxHeight!='none' && cs.maxHeight!='auto' && cs.maxHeight!='') { st.height= cs.maxHeight; inrange= (el.offsetHeight>optimal); } if (inrange) st.height= st.minmaxHeight; } } // Scanning. Check document every so often until it has finished loading. Do // nothing until <body> arrives, then call main init. Pass any new elements // found on each scan to be bound var minmax_SCANDELAY= 500; function minmax_scan() { var el; for (var i= 0; i<document.all.length; i++) { el= document.all[i]; if (!el.minmax_bound) { el.minmax_bound= true; minmax_bind(el); } } } var minmax_scanner; function minmax_stop() { window.clearInterval(minmax_scanner); minmax_scan(); } minmax_scan(); minmax_scanner= window.setInterval(minmax_scan, minmax_SCANDELAY); window.attachEvent('onload', minmax_stop); @end @*/
I have to admit though, this kind of thing drives graphic designers up the wall. You should see the look on their face when you say 'Hey look, I've made your images compress and stretch man, cool isn't it?'. Maybe that's why I do it ..
//
//