Monday, October 8, 2007

Scriptaculous

Scriptaculous is an MIT-licensed JavaScript library that provides visual effects, drag-and-drop support, and controls, such as sliders, to HTML pages. It is built on top of the Prototype JavaScript library, which provides AJAX support (and a number of other features) to the Ruby on Rail Web application framework. Because it's built on Prototype, scriptaculous has AJAX support, but its main focus is on providing highly interactive visual components that can take an AJAX application to the next level.

Installation
You can download scriptaculous from http://script.aculo.us. After extracting the archive, copy the contents of the src and lib subdirectories into a directory in the document root of your Web server. After doing that, you just need to include the prototype and scriptaculous libraries in your HTML files. The components of scriptaculous will be automatically included as needed as long as they are in the same directory as scriptaculous.js. An example of these includes is shown here:

<script src="/scriptaculous/prototype.js
"type="text/javascript"></script>
<script src="/scriptaculous/scriptaculous.js
"type="text/javascript"></script>

Visual Effects
One of the most exciting features of scriptaculous is its visual effects. These effects can be used to notify the user that an event has happened or that some content is updated. The effects can be applied to any DOM element, making them very versatile, because they will work no matter what the display type of the element is. To apply an effect, you create a new instance of a method of the Effects class, passing in the element to update. This element can be an ID or a DOM element accessed directly in JavaScript.

A wide variety of effects are provided. They perform two main tasks: showing or hiding elements and drawing attention to an element. Some of the show/hide effects are available in pairs and can be used with the Effect.toggle method to hide or show an element, doing the opposite of the element's current status. The rest of the functions can be used individually, like the simple examples in the following list of effects. An effects tester is also included so that you can see what each effect looks like. Scriptaculous also includes the lower-level methods that can be used to build new effects; the API for these methods is included on its Web site.

Hide/Show Pairs
BlindDown hides the element, and BlindUp shows it:
new Effect.toggle(element,'blind');
new Effect.BlindDown(element);
new Effect.BlindUp(element);

SlideDown hides the element, and SlideUp shows it:
new  Effect.toggle(element,'slide');
new Effect.SlideDown(element);
new Effect.SlideUp(element);

Fade hides the element, and Appear shows it:

new Effect.toggle(element,'appear');
new Effect.Fade(element);
new Effect.Appear(element);

A large number of nonpaired effects for hiding elements is also included:

new Effect.SwitchOff(element);
new Effect.DropOut(element);
new Effect.Squish(element);
new Effect.Shrink(element);
new Effect.Fold(element);

The Grow effect is the only unpaired effect for showing an element:
new Effect.Grow(element);

The Effects class also contains a number of methods for drawing attention to an element:
new Effect.Pulsate(element);
new Effect.Shake(element);
new Effect.Highlight(element);

The effects tester is located in the scriptaculousViewAllEffects.html file. Listing 8-9 shows a short example of how to apply various effects.

ScriptaculousViewAllEffects.html
1  <html>
2 <head>
3 <title>Script.aculo.us Visual Effects</title>
4 <script src="scriptaculous/prototype.js"
5 type="text/javascript"></script>
6 <script src="scriptaculous/scriptaculous.js"
7 type="text/javascript"></script>
8 </head>
9 <body>
10
11 <p>Reload the page to reset the effects.</p>
12
13 <div onclick="new Effect.Fade(this)">
14 Click Me to see a Fade Effect

15 </div>
16
17 <p>
18 <a href="#" onclick="new Effect.Puff(this)"
19 >Click to hide this link</a>
20 </p>
21
22 <p>
23 <a href="#" onclick="new Effect.Fold('cell')"
24 >Hide the table cell</a>
25 <a href="#" onclick="new Effect.Grow('cell')"
26 >Show the table cell</a>
27 </p>
28
29 <table border=1>
30 <tr>
31 <td>A cell</td>
32 <td id="cell">Cell to Hide</td>
33 </tr>
34 </table>
35
36 <p>
37 <a href="#" onclick=
38 "new Effect.toggle('box','blind')"
39 >Toggle the box below</a>
40 <div id="box"
41 style="border: solid 1px black;
42 width: 50px; height: 50px">
43 BOX
44 </div>
45 </p>
46 </body>
47 </html>

One way to attach an event is to tie it to the click event of a DOM element; this passes the element being clicked and performs the effect directly on the current element. This approach is easy to do and is shown on line 13 against a block-level element, and on line 18 against an inline element. There are few cases where this direct attachment is useful; in most cases, you'll want the effect to be performed against another element on the page because the point of the effect is to draw attention to the action that is happening. Line 23 hides the element with an ID of cell by using the Fold effect, whereas line 25 shows the same element using the Grow effect. Line 38 shows the toggle utility method, which alternately shows and hides an element. This method is useful for building interface elements that show optional information.

Drag-and-Drop
Drag-and-drop gives you the ability to visually drag elements around the page and have other elements that accept the drop. The scriptaculous implementation separates the drag-and-drop components into two parts, allowing you to make elements draggable without providing a place to drop them. This can be useful for adding palettes or note elements that can be moved anywhere within the window by the user. To create a draggable element, create a new instance of the Draggable class, passing in the element to drag and any options. A common option is revert; when it is set to true, the item returns to its original position when the user lets up on the mouse:

new Draggable(element,{revert:true});

In the second half of drag-and-drop, the drop target is provided by the Droppables class. Drop targets are useful in a number of cases, from building a visual shopping cart to allowing you to visually move mail to a new folder. Drop targets can be any element and can take a number of options, including an accept parameter that limits the elements that can dropped to those with a matching class. They can also include an onDrop handler, which is run when an element is added to the drop target:


Droppables.add(el, { onDrop: function(e) { alert(e);
});


Listing 8-10 shows a small drag-and-drop application. In this listing, there are three draggable boxes and one drop target. Only the first two boxes can be dropped on the target because the third box has a class that isn't in the accept list of the drop target. This example also uses the $() alias function; it works the same way as document.getElementById. Formatting for this example is done with CSS, which is included in a separate file to decrease the amount of noise.

ScriptaculousDragNDrop.html
1 <html>
2 <head>
3 <title>Script.aculo.us Drag and Drop</title>
4 <script src=""scriptaculous/prototype.js"
5 type="text/javascript"></script>
6 <script src="scriptaculous/scriptaculous.js"
7 type="text/javascript"></script>
8
9 <link rel="stylesheet" href="dnd.css"
10 type="text/css">
11 </head>
12 <body>
13 <div id="box1" class="box">Box 1</div>
14 <div id="box2" class="box">Box 2</div>
15 <div id="box3" class="other">Box 3</div>
16
17 <br style="clear: both">
18 <div id="drop">Drop Target</div>
19
20
21 <script type="text/javascript">
22 new Draggable('box1',{revert:true});
23 new Draggable('box2',{revert:false});
24 new Draggable('box3',{revert:true});
25
26 Droppables.add('drop', {accept: 'box',
27 onDrop: function(el) {
28 $('drop').innerHTML =
29 'Dropped: '+el.id;
30 }
31 });
32 </script>
33
34 </body>
35 </html>

Most of this page is set up in HTML with a small amount of JavaScript code to activate the drag-and-drop functionality. The page starts with a basic setup. Lines 47 include the scriptaculous JavaScript library, and lines 910 include a CSS file to do some basic formatting. Lines 1318 create the basic user interface; it is made up of three 200x200 pixel boxes that are floated next to each other. Below that is a 100x400 pixel drop target.

Lines 2132 make these boxes draggable and create a drop target for them. Lines 2224 create the draggable boxes; the first parameter is the ID of the box, and the second is a hash of options. On line 23, we set the revert property of the second box to false; this lets us drag it around the screen. This property isn't very useful for dragging to a drop target, but it can be useful for other use cases. Lines 2630 create the drop target; the first parameter is the ID of the element, and its second parameter is a hash of options. Here we're setting two options. The first is the accept variable, which takes a class to accept; in this case, it's set to box, which allows box 1 and 2, but not box 3, to be dropped. The second option is the onDrop function; this is called when a draggable element is released while over the drop target. The function displays some simple feedback displaying the ID of the dropped element in the drop target.

Sortables
A sortable is a predefined component built from the drag-and-drop building blocks that scriptaculous provides. Sortables make it easy to build graphically reorderable lists and can even be used to let you move items between multiple lists. Sortables are usually used with HTML lists, but they can also be used with floated elements. To create a sortable list, you simply run Sortable.create, passing in an ID and any options you want to specify, like so:

Sortable.create("element",{ghosting:true});

Some of the more commonly used properties are overlap, ghosting, and onChange:

  • The overlap property, which takes the values of horizontal, vertical, or false, limits how you can drag the elements around; the false setting has no limits.

  • Setting the ghosting property to TRue leaves the element in its current position; the user then drags a faded version until it is dropped.

  • The onChange property lets you set a callback function, which is called after an item has been moved.

If the elements in your sortable have the ID property set using the naming convention of name_item, you can use the Sortable.serialize method to quickly build a query string, which can be sent to the server and used to update the order on the server. An example output from the serialize method is this:



list[]=one&list[]=three&list[]=two&list[]=four

If you used this string as the query string on a request to a PHP page, $_GET['list'] will be populated with an array that contains the updated positions of the list. The array is ordered in its new position, with the value being the specified ID. Listing 8-11 shows an example of this operation.

ScriptaculousSortable.php
1  <html>
2 <head>
3 <title>Script.aculo.us Sortables</title>
4 <script src="scriptaculous/prototype.js"
5 type="text/javascript"></script>

6 <script src="scriptaculous/scriptaculous.js"
7 type="text/javascript"></script>
8
9 <style type="text/css">
10 #list {
11 cursor: pointer;
12 }
13 </style>
14 </head>
15 <body>
16
17 <ul id="list">
18 <li id="i_one">One</li>
19 <li id="i_two">Two</li>
20 <li id="i_three">Three</li>
21 <li id="i_four">Four</li>
22 </ul>
23
24 <a href="javascript:updateList()"
25 >Send Changes to server</a>
26
27 <pre><?php
28 if (isset($_GET['list'])) {
29 var_dump($_GET['list']);
30 }
31 ?></pre>
32
33 <script type="text/javascript">
34 Sortable.create("list"});
35
36 function updateList() {
37 var update = Sortable.serialize('list');
38
39 window.location = '?'+update;
40 }
41 </script>
42
43 </body>
44 </html>

This page is mainly an HTML/JavaScript page with a small amount of PHP mixed in to show how a server-side language parses the output of Sortable.serialize(). The script starts with a basic setup, with lines 47 including the scriptaculous library. Then, on lines 912, we include a small amount of CSS, which gives all the sortable elements a pointer cursor. This is an important usability step; without it, the elements will have a text select cursor, and the user won't realize they are sortable. Lines 1722 build the list that will be sorted; each item has an ID in it, which defines the value that will be returned to the server. Lines 2425 complete the user interface, creating a link that reloads the page and sends the list's new order to the server.

Lines 2731 contain the small amount of PHP code in this script. If the list variable has been passed in by the query string, its outputs are echoed out using a debugging function. PHP and many other Web development languages automatically turn the query string provided by Sortable.serialize into an array; from here you could update the database with the new order.

Lines 3340 contain the JavaScript for this example. On line 34, we make the list element sortable, using most of the default options because they are optimized for use with HTML lists. Then, on lines 3640, we build a small function that builds a query string using Sortable.serialize (line 37) and then reloads the page by setting window.location.

Slider Control
Scriptaculous also provides a slider control, which is useful for selecting values that are in a range. This control can be used in its basic state to build something like a color selector. It can also be used as a building block for more advanced elements, such as a JavaScript-powered scrollbar for an AJAX grid. An example of the slider control in both horizontal and vertical modes is shown in Figure 8-1

The scriptaculous slider control shown in both horizontal and vertical modes

Scriptaculous provides only the behavior of the slider, not its looks. As long as you follow the pattern of a container element with a slide handle inside of it, you can make the slider look any way you want. Because you control the look of the sliders, you also control their usability. One simple usability tip is to set the cursor of the slide handle to a value of move. This gives you the browser's standard cursor icon for items that can be moved around, which helps users understand how to move the control. The slider returns a value from 0 to 1 as you scroll across its range; to translate this to a more usable value, you simply multiply it by the maximum value of your target range, rounding it if you want an integer, like so:

var outputValue = Math.round(100*sliderValue);

Listing 8-12 shows an example page that implements both a horizontal slider and a vertical slider.

ScriptaculousSlider.html
1  <html>
2 <head>
3 <title>Script.aculo.us Slider</title>
4 <script src="scriptaculous/prototype.jsv
5 type="text/javascript"></script>
6 <script src="scriptaculous/scriptaculous.js"
7 type="text/javascript"></script>
8 </head>
9 <body>
10
11 <h3>Horizontal Slider</h3>
12 <div id="track1" style="
13 width: 200px;

14 background-color: rgb(170, 170, 170);
15 height: 5px;">
16 <div id="handle1" style="
17 width: 5px;
18 height: 10px;
19 background-color: rgb(255, 0, 0);
20 cursor: move;
21 "> </div>
22 </div>
23 <div id="debug1"></div>
24
25 <h3>Vertical Slider</h3>
26 <div id="track2" style="
27 height: 100px;
28 background-color: rgb(170, 170, 170);
29 width: 5px;">
30 <div id="handle2" style="
31 width: 10px;
32 height: 5px;
33 background-color: rgb(255, 0, 0);
34 cursor: move;
35 "> </div>
36 </div>
37 <div id="debug2"></div>
38
39 <script type="text/javascript">
40 var d1 = document.getElementById('debug1');
41 var d2 = document.getElementById('debug2');
42
43 new Control.Slider('handle1','track1',{
44 onSlide:function(v){d1.innerHTML='slide: '+v},
45 onChange:function(v){d1.innerHTML='changed! '+v}
46 });
47
48 new Control.Slider('handle2','track2',{
49 axis:'vertical',
50 onSlide:function(v){d2.innerHTML=Math.round(100*v)},
51 onChange:function(v){d2.innerHTML=Math.round(100*v)}
52 });
53 </script>
55 </body>
56 </html>

Like the rest of the scriptaculous examples (Listings 8-98-11), this page includes the JavaScript library files in its header (lines 47). After that, the HTML for the sliders is laid out: first the horizontal slider (lines 1123) and then the vertical slider (lines 2537). Both sliders follow a similar pattern; first the slider track is defined, setting its ID, width, height, and color (lines 1215 and 2629). Then, the handles for the sliders are defined (lines 1621 and 3035). The handles set most of the same basic style elements as the track, adding a cursor of move for improved usability. The HTML definitions are finished by creating empty DIV elements to display the current value of the slider (lines 23 and 37).

The next section of the page is the JavaScript that turns these DIV groups into sliders. We start this process by assigning the debug DIV elements to variables so that we can easily reference them later (lines 4041). Then we create a slider instance for the horizontal slider control (lines 4346). The Control.Slider function takes three parameters: the track element, the handle element, and any options. In this case, we are setting two options: the onSlide and onChange event handlers. The onSlide handler is called as we move the handle around; the onChange handler is called when we're done dragging the handle. The onSlide handler is usually used to provide feedback, whereas the onChange handler is used to make the value of the slide accessible to other parts of the page, storing its value in an input box or JavaScript variable.

Lines 4852 follow much of the same process for the vertical slider. In this case, we set an extra option, axis, to the value of vertical setting. This does what it suggests and makes the slider work in a vertical fashion. We also translate the value of the slider to a 0100 scale in the onSlide and onChange handlers.

Scriptaculous Development Tips

Scriptaculous contains functionality for creating visually impressive Web sites. While using it, keep these tips in mind:

  • Most scriptaculous functions have further documentation and examples at http://script.aculuo.us, so if you're not sure how to make a function operate, start there.

  • Scriptaculous contains a variety of prepackaged effects and components, but if they don't meet your needs, it also provides the tools to build new ones.

  • Besides the Web site, you can find more scriptaculous examples in the tests directory, in the scriptaculous download. The functional tests are very useful in this regard.

  • Scriptaculous contains a number of additional controls that you should explore before building your own. These include the following:

    • Autocompleter: Provides Google Suggest style auto completing text fields

    • InPlaceEditor: Provides click-to-edit content with AJAX saving the changes

No comments: