I was trying to write a toy program in Django this morning, something kinda silly and spicy, and one of the things I wanted was a horizontal scrolling special effect, using the animation tools in, say, Dojo or jQuery.

And I realized, as I was writing it, that it had been almost two years since I'd worked with CSS viewports, and how they work, and getting it to work was a huge struggle. At first, I crafted a simple javascript engine to do non-animated transitions, and that didn't work.

So, the basics.

As I'm using it here, a viewport is a rectangular portion of the HTML display that contains more HTML objects larger than itself, and through which only a portion can be seen at any time. The idea is that you use the viewport to show a given portion, and then use some other form of interaction (drag & drop, or the arrow keys, or buttons/links onscreen, or some other tool) to move the underlying HTML around, exposing different parts of it to the user through the viewport.

In the example, there is a panel, and for my purposes the panel is the same size as the viewport. I want a kind of slideshow. (In order to make sure everything fits, I have a CSS reset line at the top). I've placed an "inner" block, with some color, to let me know that everything's working. In order for jQuery to be able to do positioning math correctly, the viewport must be declared position: relative;

Inside the viewport, I create an unordered list, with each list item containing a panel. The list is unstyled, and the list items are inline and floated.

At the bottom is some javascript, using the jQuery engine (I've worked with prototype, mooTools, EXT-JS, and Dojo, but never jQuery, so it was time to learn!) to hook up a pair of anchors that will slide the objects back and forth. This code uses a lot of specific knowledge about the viewports: getting it more generalized (i.e. figuring out the panel sizes from the panels themselves, through CSS detection) will be important for my project, as will figuring out how to turn this into a circular queue (i.e. when you get to the top, go back to the bottom and make it look ring-like).  It would be nice if the scroll handler knew about panels, and not about offsets; the latter is epiphenomenal to the former.

A couple of things to note:  The list is set at 'left: -300px', which brings the second item of the list into view.  That was for testing purposes; I've left it as-is on purpose.  This code is the basic ideas behind all sliders, rotating displays, etc. etc.  I've made it as simple and obvious as possible, to help you implement your own versions yourself.

View running code

The  code below has been defanged by wordpress:

<html>
<head>
<title>Viewport Demonstration</title>
<script type="text/javascript" src="jquery-1.3.2.js"></script>
<style type="text/css">

body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,
input,textarea,p,blockquote,th,td{ margin:0; padding:0; }

body { background-color: white; }

.panel {
        width: 300px;
        height: 200px;
}

.inner {
        width: 240px;
        height: 160px;
        margin: 20 auto;
        background-color: lightblue;
}

#viewport {
  position: relative;
  margin: 10px 0 0 10px;
  overflow: hidden;
  width: 300px;
  height: 200px;
  background-color: lightgrey;
}

#viewport ul {
        position: relative;
        width: 1260px;
        height: 200px;
        left: -300px;
        list-style-image:none;
        list-style-position:outside;
        list-style-type:none;
}

#viewport li {
        position: relative;
        float: left;
        display: inline;
}

</style>
</head>

<body width="2000">

<div id="viewport">
<ul>
  <li><div class="panel"><div class="inner">Panel 1</div></div></li>
  <li><div class="panel"><div class="inner">Panel 2</div></div></li>
  <li><div class="panel"><div class="inner">Panel 3</div></div></li>
  </ul>
</div>

<div><a href="#" id="ml">Left</a> <a href="#" id="mr">Right</a></div>

<script type="text/javascript">
$(document).ready(function() {
    $('#ml').click(function() {
        var obj = $('ul', $('#viewport'));
        var pos = obj.position().left;
        if (pos > -600) {
            pos = pos - 300;
	    obj.animate({left: pos + "px"}, 1000);
        }
    });

    $('#mr').click(function() {
        var obj = $('ul', $('#viewport'));
        var pos = obj.position().left;
        if (pos < 0) {
            pos = pos + 300;
	    obj.animate({left: pos + "px"}, 1000);
        }
    });
});
</script>
</body>
</html>