Section Your Content with Spinners


Use spinners to divide your page content into sections, each of which you can show or hide individually.

The Code

The code for index.php is shown in Example 1.

Example 1. PHP allowing for user selection of a specific spinner
<?php
function start_section( $id, $title )
{
?>
<table cellspacing="0" cellpadding="0">
<tr>
<td width="30" valign="top">
<a href="javascript: void twist('<?php echo($id); ?>');">
<img src="up.gif" border="0" id="img_<?php echo($id); ?>"/>
</a>
</td>
<td width="90%">
<h1><?php echo( $title ); ?></h1>
<div style="visibility:hidden;position:absolute;"
  id="<?php echo($id); ?>" class="spin-content">
<?php
}
function end_section( )
{
?>
</div>
</td>
</tr>
</table>
<?php
}
function spinner_header( )
{
?>
<style type="text/css">
 
body { font-family: arial, verdana; }
h1 { font-size: medium; border-bottom: 1px solid black; }
.spin-content{font-size:small;margin-left:10px;padding:10px;}
</style>
<script language="Javascript">
function twist( sid )
{
  imgobj = document.getElementById( "img_"+sid );
  divobj = document.getElementById( sid );
  if ( imgobj.src.match( "up.gif" ) )
  {
        imgobj.src = "down.gif";
        divobj.style.position = "relative";
        divobj.style.visibility = "visible";
  }
  else
  {
        imgobj.src = "up.gif";
        divobj.style.visibility = "hidden";
        divobj.style.position = "absolute";
  }
}
</script>
<?php
}
?>
<html>
<head>
<?php spinner_header( ) ?>
</head>
<body>
<?php start_section( "one", "Report part one" ) ?>
This report will tell you a lot of stuff you didn't
know before.
And that's good.
Because that's what a report should do.
But it will tell you so much that it needs to be
rolled up into sections
so that you don't have to gasp as you see it all at once.
<?php end_section( ) ?>
<?php start_section( "two", "Report part two" ) ?>
This is a table of numbers and such:<br/>
<table>
<tr><th>State</th><th>Total</th></tr>
<tr><td>CA</td><td>$35M</td></tr>
<tr><td>PA</td><td>$22M</td></tr>
<tr><td>NC</td><td>$5M</td></tr>
<tr><td>FL</td><td>$15M</td></tr>
</table>
<?php end_section( ) ?>
</body>
</html>

The script starts by defining the start_section and end_section functions, which bracket the blocks of content on the page that will be shown (or hidden) interactively. The top section also defines a spinner_header function that will define the CSS and JavaScript used by the DHTML portion of the system.

The start_section function creates a table where the first column has the spinner graphic, and the second has the title of the section and a div element that will hold the content. The div element is initially set to be invisible, and its position attribute is set initially to "absolute" (a div element that is positioned as "relative" will still be accounted for in the layout). To make the div element and its content disappear, the position attribute's value is set to "absolute"; it can then be set back to "relative" for a reappearance.

Running

Upload the code and images to the server, and point your browser at index.php. You should see something that looks like Figure 1

Figure 1. The sections closed up

Click on the arrow next to the first part of the report, and watch it spin to expose the report section (as shown in Figure 2).

Figure 2. The first section opened up

When writing code that does dynamic repositioning, as with this hack, it's very important to test it on every browser you intend to support. Positioning visible and invisible elements is one of those "gotcha" areas in DHTML.


Using DHTML, you can build graphs that change without requiring even a page refresh. The result? Your users can play with data in real time.

Something is fundamentally unsatisfying about the way the Web works. You click on a link, the page disappears, and that lovely spinning ball or ticking clock grinds by as a new page appears section by section, (hopefully) with the information you want. This certainly is not the interactivity we're all used to from our client-side applications.

But, thank goodness, you can make an application that works without a page refresh. This hack shows you how to make an interactive scatter plot using a few graphics, some PHP, and a whole slew of JavaScript.

The Code

The index file, index.php, is shown in Example 1.

Example 1. JavaScript, the real workhorse in this hack
<?php $states = array(
array("Alabama",4447100,1963711,52419.02,
1675.01,50744,87.6,38.7),
array("Alaska",626932,260978,663267.26,91316,
571951.26,1.1,0.5),
array("Arizona",5130632,2189189,113998.3,363.73,
113634.57,45.2,19.3),
array("Arkansas",2673400,1173043,53178.62,
1110.45,52068.17,51.3,22.5),
array("California",33871648,12214549,163695.57,
7736.23,155959.34,217.2,78.3),
array( "Colorado",4301261,1808037,104093.57,
376.04,103717.53,41.5,17.4 ),
…
array( "Washington",5894121,2451075,71299.64,
4755.58,66544.06,88.6,36.8 ),
array( "West Virginia",1808344,844623,24229.76,
152.03,24077.73,75.1,35.1 ),
array( "Wisconsin",5363675,2321144,65497.82,
11187.72,54310.1,98.8,42.7 ),
array( "Wyoming",493782,223854,97813.56,713.16,
97100.4,5.1,2.3 ),
array( "Puerto Rico",3808610,1418476,5324.5,
1899.94,3424.56,1112.1,414.2 )
);
?>
<html>
<head>
<script language="Javascript">
var width = 300;
var height = 300;
 
var axes = [
"population", "housing_units", "total_area",
"total_water", "total_
land", "people_density", "housing_density" ];
 
var data = [
<?php $first = true; foreach( $states as $state )
{ if ( !$first ) echo( "," );
?>
{ state: "<?php echo($state[0]); ?>",
population: <?php echo($state[1]); ?>,
housing_units: <?php echo($state[2]); ?>,
total_area: <?php echo($state[3]); ?>,
total_water: <?php echo($state[4]); ?>,
total_land: <?php echo($state[5]); ?>,
people_density: <?php echo($state[6]); ?>,
housing_density: <?php
echo($state[7]); ?> }
<?php $first = false; } ?>
];
 
var axmin = {};
var axmax = {};
 
for( axind in axes )
{
        axmin[ axes[axind] ] = 100000000;
        axmax[ axes[axind] ] = -100000000;
}
for( ind in data )
{
        row = data[ind];
        for( axind in axes )
        {
          axis = axes[axind];
          if ( row[axis] < axmin[axis] )
            axmin[axis] = row[axis];
 
          if ( row[axis] > axmax[axis] )
            axmax[axis] = row[axis];
        }
}
 
function cleargraph( )
{
        graph = document.getElementById( "graph" );
        graph.innerHTML = "";
}
 
function adddot( value, size, x, y, text )
{
      var left = x - ( size / 2 );
      var top = width - ( y + ( size / 2 ) );
 
      var cleft = "auto";
      var ctop = "auto";
      var cright = "auto";
      var cbottom = "auto";
 
      if ( left < 0 ) { cright = ( left * -1 ) + "px"; }
      if ( left + size > width ) { cleft = ( width - left ) + "px"; }
      if ( top < 0 ) { ctop = ( top * -1 ) + "px"; }
      if ( top + size > height ) { cbottom = ( height - top ) + "px"; }
 
      if ( value <= 0.25 )
             img = "ltgray.gif";
      else if ( value <= 0.50 )
             img = "gray.gif";
      else if ( value <= 0.75 )
             img = "dkgray.gif";
      else
             img = "black.gif";
 
    html = "<img src=\""+img+"\" width=\""+size+"\" height=\""+size+"\" ";
    html += "style=\"position:absolute;left:"+left+"px;top:"+top+"px;";
    html += "clip:rect( "+ctop+" "+cleft+" "+cbottom+" "+cright+" );";
    html += "\" onclick=\"alert(\'"+text+"\')\"/>";
 
    graph = document.getElementById( "graph" );
    graph.innerHTML += html;
}
 
function calculate_value( row, field, min, max )
{
      var val = row[ field ] - axmin[ field ];
      var scale = ( max - min ) / ( axmax[ field ] - axmin[ field ] );
      return min + ( scale * val );
}
 
function drawgraph( )
{
      cleargraph( );
 
      var xvar = document.getElementById( "bottom" ).value;
      var yvar = document.getElementById( "side" ).value;
      var sizevar = document.getElementById( "size" ).value;
      var valuevar = document.getElementById( "color" ).value;
 
      for( rowind in data )
      {
             var row = data[rowind];
             var x = calculate_value( row, xvar, 5, width - 5 );
             var y = calculate_value( row, yvar, 5, height - 5 );
             var size = calculate_value( row, sizevar, 5, 30 );
             var value = calculate_value( row, valuevar, 0, 1 );
             adddot( value, size, x, y, row.state );
        }
}
 
function buildselect( axis, current )
{
        var html = "<select id=\""+axis+"\" onchange=\"drawgraph( )\">";
        for( axind in axes )
        {
        var selected = "";
        if ( axes[axind] == current )
          selected = " selected=\"true\"";
        html += "<option value=\""+axes[axind]+"\""+selected+">"+axes[axind]+"
          </option>";
      }
      html += "</select>";
      document.write( html );
}
</script>
</head>
<body onload="drawgraph( );">
Side: <script language="Javascript">
buildselect( "side", "population" );</script>
Bottom: <script language="Javascript">
buildselect( "bottom", "housing_units" );</
script>
Size: <script language="Javascript">
buildselect( "size", "total_area" );</script>
Color: <script language="Javascript">
buildselect( "color", "total_water" );</
script>
<div style="position:relative;border:1px solid #eee; clip:rect(0px 0px 300px
300px); width:300px; height:300px;" id="graph">
</div>
</body>
</html>

The script starts by creating the data array, first in PHP and then in JavaScript. The data needs to be available to JavaScript so that the graph can be dynamically built on the fly using DHTML. Once the data is loaded into the page, the rest is left up to the browser.

The first thing the browser does is create the drop downs using the buildselect( ) function. Then, the onload() event fires on the body tag, which calls the drawgraph() function. This function in turn creates a new HTML string that's made up of lots of img tags, one for each state. The size of the image, as well as the position of the image on the graph, depends on which data attributes are assigned to what graph attributes via the drop down. Once the graph is created, the drawgraph() function sets the inner HTML of the graph <div> tag to the new HTML, rendering all of the images.

If the user selects a different attribute with one of the drop downs, the drawgraph( ) function is called again, and the graph is updated.

Running

Upload the PHP page and the images to the server, and then navigate to the page in your web browser. You should see something along the lines of Figure 1. Now use the combo boxes to assign different variables to the different portions of the graph. The Side drop down changes the Y axis. The Bottom drop down changes the X axis. The Size drop down sets which of the attributes will alter the size of the ball, and the Color drop down assigns a data attribute to the color of the ball.

The Y axis represents housing units, and land area increases along the X axis. While most of the plot appears closely packed, there are a few outliers. Click on one of these to see which data point is represented; for instance, clicking on the black ball in the lower righthand corner results in the pop up shown in Figure 2.

This tells me that, not surprisingly, Alaska has the largest total size but has very few housing units. The size of the ball tells me that Alaska has a large amount of land mass as well. I'm not surprised by the size of the land mass, but the very low housing units number is somewhat interesting. There are also two other outliers: the medium gray ball in the middle of the graph (Texas), and the ball at the very top of the graph (California).

Figure 1. The scatter plot after some experimentation

Figure 2. After clicking on the ball in the lower-right corner



Use JavaScript, DHTML, and PHP to create and use drag-and-drop lists.

Creating an interface that allows the user to prioritize a list has always been a problem when working with HTML. With PHP, though, this is no longer the case. This hack uses an open source drag-and-drop library from ToolMan (http://tool-man.org/) to create drag-and-drop lists.

The Code

Enter the code shown in Example 1 and save it as index.html.

Example 1. Building a drag-and-drop list with HTML and CSS
<html>
<head>
 
<style>
#states li { margin: 0px; }
        
ul.boxy li { margin: 3px; }
 
ul.sortable li {
               position: relative;
}
 
ul.boxy {
               list-style-type: none;
               padding: 0px;
               margin: 2px;
               width: 20em;
               font-size: 13px;
               font-family: Arial, sans-serif;
}
ul.boxy li {
               cursor:move;
               padding: 2px 2px;
               border: 1px solid #ccc;
               background-color: #eee;
}
.clickable a {
                display: block;
                text-decoration: none;
                cursor: pointer;
                cursor: hand;
}
.clickable li:hover {
               background-color: #f6f6f6;
}
 
</style>
 
<script language="JavaScript" type="text/javascript"
         src="source/org/tool-man/core.js"></script>
<script language="JavaScript" type="text/javascript"
         src="source/org/tool-man/events.js"></script>
<script language="JavaScript" type="text/javascript"
         src="source/org/tool-man/css.js"></script>
<script language="JavaScript" type="text/javascript"
         src="source/org/tool-man/coordinates.js"></script>
<script language="JavaScript" type="text/javascript"
         src="source/org/tool-man/drag.js"></script>
<script language="JavaScript" type="text/javascript"
         src="source/org/tool-man/dragsort.js"></script>
<script language="JavaScript" type="text/javascript"
         src="source/org/tool-man/cookies.js"></script>
 
<script language="JavaScript" type="text/javascript">
<!--
var dragsort = ToolMan.dragsort( )
var junkdrawer = ToolMan.junkdrawer( )
 
window.onload = function( )
{
dragsort.makeListSortable(document.getElementById("states"),
verticalOnly, saveOrder)
}
 
function verticalOnly(item)
{ item.toolManDragGroup.verticalOnly( ) }
 
function saveOrder(item) { }
 
function prepFields( )
{
document.getElementById( "states_text" ).value = junkdrawer.
serializeList( document.getElementById( "states" ) );
               return true;
}
//-->
</script>
</head>
<body>
 
<ul id="states" class="boxy">
<li>California</li>
<li>Texas</li>
<li>Alaska</li>
</ul>
 
<form method="post" action="tellme.php">
<input type="hidden" name="states" value="" id="states_text" />
<input type="submit" onclick="return prepFields( );">
</form>
 
</body>
 
</html>

The simple code in Example 2 saved as tellme.phpprints out values in an array.

Example 2. PHP used to print out some values from the list
<body>
<html>
You chose: <?php echo( $_POST['states'] ); ?>
</html>
</body>

Running

Download and unpack the drag-and-drop libraries onto your web server. Then upload the files and navigate to the index.html page. You should see something that looks like Figure 1.

Figure 1. The drag-and-drop list

Now drag and drop the lines to rearrange the items however you like; then click the Submit button. At that point, the contents of the list will be transferred into a hidden form variable called states, and uploaded to the server. The tellme.php script then prints the values from that variable in the order you specified (as shown in Figure 2).

Figure 2. After clicking on the Submit button

Dynamic, little interface widgets such as this one can differentiate between your web application and others in terms of ease of use. And sometimes it's good having one just for a little perk during demos! With the results of a drag stored in a form variable, your PHP can easily retrieve the data and do anything you want it to do.


top