Web Programming Step by Step, 2nd Edition

Lecture 25: Prototype and Scriptaculous

Reading: Ch. 10

Except where otherwise noted, the contents of this document are Copyright 2012 Marty Stepp, Jessica Miller, and Victoria Kirst. All rights reserved. Any redistribution, reproduction, transmission, or storage of part or all of the contents in any form is prohibited without the author's expressed written permission.

Valid HTML5 Valid CSS

Problems with JavaScript

JavaScript is a powerful language, but it has many flaws:

JavaScript libraries

<script src="libraryURL" type="text/javascript"></script>
library poll
js libraries

10.1: The Prototype Library

Prototype framework

<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.1.0/prototype.js" 
        type="text/javascript"></script>

Methods added to existing objects

10.1: Prototype and the DOM

The $ function

$("id")
$("footer").innerHTML = $("username").value.toUpperCase();

The $$ function

var arrayName = $$("CSS selector");
// hide all "announcement" paragraphs in the "news" section
var paragraphs = $$("div#news p.announcement");
for (var i = 0; i < paragraphs.length; i++) {
	paragraphs[i].hide();
}

Prototype form shortcuts

$F("formID")["name"]
$F("controlID")

Prototype's DOM element methods

Prototype adds the following methods to every DOM element object:

absolutize addClassName classNames cleanWhitespace clone clonePosition
cumulativeOffset cumulativeScr... empty extend fire firstDescendant
getDimensions getHeight getLayout getOffsetParent getStyle getStorage
getWidth hasClassName hide identify insert inspect
makeClipping makePositioned match measure observe positionedOffset
purge readAttribute recursive... relativize remove removeClassName
replace retrieve scrollTo select setOpacity setStyle
show stopObserving toggle toggleClassName undoClipping undoPositioned
update viewportOffset visible wrap writeAttribute

Prototype's DOM tree traversal methods

method(s) description
ancestors, up elements above this one
childElements, descendants, down elements below this one (not text nodes)
siblings, next, nextSiblings,
previous, previousSiblings, adjacent
elements with same parent
as this one (not text nodes)
DOM element
// alter siblings of "main" that do not contain "Sun"
var sibs = $("main").siblings();
for (var i = 0; i < sibs.length; i++) {
	if (sibs[i].innerHTML.indexOf("Sun") < 0) {
		sibs[i].innerHTML += " Sunshine";
	}
}

The each paradigm

// alter siblings of "main" that do not contain "Sun"
$$("div#main ul > li.new").each(function(e) {    // add a class to some elements
	e.classList.add("exciting");
});

$$("div#main ul > li.old").each(Element.hide);   // hide all 'old' elements

Accessing styles in Prototype

<button id="clickme">Click Me</button>
function biggerFont() {
	// turn text yellow and make it bigger
	var size = $("clickme").getStyle("font-size");   // "22px"
	$("clickme").style.fontSize = parseInt(size) + 4 + "pt";
}

Prototype and Events

Attaching event handlers the Prototype way

element.onevent = function;
element.observe("event", function);
// call the playNewGame function when the Play button is clicked
$("play").observe("click", playNewGame);

Mouse event features

mouse event
Prototype's mouse event methods (event)
property/method description
pointerX(), *
pointerY()
coordinates in entire web page
(replaces pageX, pageY)
isLeftClick(), **
isMiddleClick(),
isRightClick()
true if left/mid/right button was pressed
(replaces button and which)
$("game").observe("mousemove", move);

function move(event) {
	if (event.isLeftClick() && 
			event.pointerX() < this.getWidth() / 2) {
		gameOver();
	}
}

Key event features

Prototype's key code constants (event.keyCode)
Event.KEY_BACKSPACE Event.KEY_DELETE Event.KEY_ESC Event.KEY_RETURN
Event.KEY_TAB Event.KEY_DOWN,
Event.KEY_LEFT,
Event.KEY_RIGHT,
Event.KEY_UP
Event.KEY_HOME,
Event.KEY_END
Event.KEY_PAGEDOWN,
Event.KEY_PAGEUP
$("game").observe("keydown", typing);

function typing(event) {
	if (event.keyCode() == Event.KEY_ESC) {
		quitGame();
	}
}

Page/window and form events

window.onload = function() { ... };
document.observe("dom:loaded", function() {
	// attach event handlers, etc.
});
activate clear disable enable
focus getValue present select

Stopping an event

<form id="exampleform" action="http://foo.com/foo.php">...</form>
document.observe("dom:loaded", function() {
	$("exampleform").observe("submit", checkData);
});

function checkData(event) {
	if ($F("city") == "" || $F("state").length != 2) {
		alert("Error, invalid city/state.");  // show error message 
		event.stop();
	}
}

Prototype and Ajax

Prototype's Ajax model

new Ajax.Request("url", {
	option : value,
	option : value,
	...
	option : value
});

Prototype Ajax options

option description
method how to fetch the request from the server (default "post")
asynchronous should request be sent asynchronously in the background? (default true)
parameters query parameters to pass to the server, if any (as a string or object)
onSuccess event: request completed successfully
onFailure event: request was unsuccessful
onException event: request has a syntax error, security error, etc.
others: contentType, encoding, requestHeaders; events: onCreate, onComplete, on### (for HTTP error code ###)

Prototype Ajax example

	new Ajax.Request("foo/bar/mydata.txt", {
		method: "get",
		onSuccess: myAjaxSuccessFunction
	});
	...

function myAjaxSuccessFunction(ajax) {
	do something with ajax.responseText;
}

Ajax response object's properties

property description
status the request's HTTP result code (200 = OK, etc.)
statusText HTTP status code text
responseText the entire text of the fetched file, as a string
responseXML,
responseJSON
the entire contents of the fetched file, in other formats (seen later)
function myAjaxSuccessFunction(ajax) {
	alert(ajax.responseText);
}

Handling Ajax errors

	new Ajax.Request("url", {
		method: "get",
		onSuccess: functionName,
		onFailure: ajaxFailure,
		onException: ajaxFailure
	});
	...
function ajaxFailure(ajax, exception) {
	alert("Error making Ajax request:" + 
	      "\n\nServer status:\n" + ajax.status + " " + ajax.statusText + 
	      "\n\nServer response text:\n" + ajax.responseText);
	if (exception) {
		throw exception;
	}
}

Passing query parameters to a request

new Ajax.Request("lookup_account.php", {
	method: "get",
	parameters: {name: "Ed Smith", age: 29, password: "abcdef"},
	onFailure: ajaxFailure,
	onException: ajaxFailure
});
...

Creating a POST request

new Ajax.Request("url", {
	method: "post",
	parameters: {name: value, name: value, ..., name: value},
	onSuccess: functionName,
	onFailure: functionName,
	onException: functionName
});

Prototype's Ajax Updater

new Ajax.Updater("id", "url", {
	method: "get"
});

Ajax.Updater options

new Ajax.Updater({success: "id", failure: "id"}, "url", {
	method: "get",
	insertion: "top"
});

PeriodicalUpdater

new Ajax.PeriodicalUpdater("id", "url", {
	frequency: seconds,
	name: value, ...
});

Ajax.Responders

Ajax.Responders.register({
	onEvent: functionName,
	onEvent: functionName,
	...
});

10.2: The Scriptaculous Library

Scriptaculous overview

Scriptaculous logo

Scriptaculous : a JavaScript library, built on top of Prototype, that adds:

Downloading and using Scriptaculous

<script src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.1.0/prototype.js"
        type="text/javascript"></script>

<script src="https://ajax.googleapis.com/ajax/libs/scriptaculous/1.9.0/scriptaculous.js"
        type="text/javascript"></script>

Scriptaculous Visual Effects

Visual effects

(appearing)


(disappearing)


(Getting attention)

scriptaculous logo Click effects above

Adding effects to an element

element.effectName();            // for most effects
new Effect.Name(element or id);   // for some effects
$("sidebar").shake();

var buttons = $$("results > button");
for (var i = 0; i < buttons.length; i++) {
	buttons[i].fade();
}

Effect options

element.effectName({
	option: value,
	option: value,
	...
});
$("my_element").pulsate({
	duration: 2.0, 
	pulses: 2
});

The morph effect

element.morph("CSS", {
	option: value,
	option: value,
	...
});
$("okaybutton").morph("color: #ff0000; width: 300px", {duration: 3.0});

Effect events

$("my_element").fade({
	duration: 3.0, 
	afterFinish: displayMessage
});

function displayMessage(effect) {
	alert(effect.element + " is done fading now!");
}

Scriptaculous Drag-and-Drop

Drag and drop

Scriptaculous provides several objects for supporting drag-and-drop functionality:

Sortable

Sortable.create(element or id of list, {
	options
});

Sortable demo

<ol id="simpsons">
	<li>Homer</li>
	<li>Marge</li>
	<li>Bart</li>
	<li>Lisa</li>
	<li>Maggie</li>
</ol>
document.observe("dom:loaded", function() {
	Sortable.create("simpsons");
});
  1. Homer
  2. Marge
  3. Bart
  4. Lisa
  5. Maggie

Sortable list events

event description
onChange when any list item hovers over a new position while dragging
onUpdate when a list item is dropped into a new position (more useful)
document.observe("dom:loaded", function() {
	Sortable.create("simpsons", {
			onUpdate: listUpdate
	});
});

Sortable list events example

document.observe("dom:loaded", function() {
	Sortable.create("simpsons", {
			onUpdate: listUpdate
	});
});

function listUpdate(list) {
	// can do anything I want here; effects, an Ajax request, etc.
	list.shake();
}
  1. Homer
  2. Marge
  3. Bart
  4. Lisa
  5. Maggie

Subtleties of Sortable events

Draggable

new Draggable(element or id, {
	options
});

Draggable example

<div id="draggabledemo1">Draggable demo 1. Default options.</div>
<div id="draggabledemo2">Draggable demo 2.
	{snap: [40,40], revert: true}</div>
document.observe("dom:loaded", function() {
	new Draggable("draggabledemo1");
	new Draggable("draggabledemo2", {revert: true, snap: [40, 40]});
});
logo Draggable demo 1.
Default options.
Draggable demo 2.
{snap:[60, 60], revert:true}

Draggables

Droppables

Droppables.add(element or id, {
	options
});

Drag/drop shopping demo

<img id="shirt" src="images/shirt.png" alt="shirt" />
<img id="cup" src="images/cup.png" alt="cup" />
<div id="droptarget"></div>
document.observe("dom:loaded", function() {
	new Draggable("shirt");
	new Draggable("cup");
	Droppables.add("droptarget", {onDrop: productDrop});
});

function productDrop(drag, drop, event) {
	alert("You dropped " + drag.id);
}
shirt cup

Scriptaculous Ajax Features

Auto-completing text fields

autocomplete

Scriptaculous offers ways to make a text box that auto-completes based on prefix strings *:

* (won't be necessary once HTML5 datalist element is well supported)

Using Autocompleter.Local

new Autocompleter.Local(
	element or id of text box, 
	element or id of div to show completions,
	array of choices, 
	{ options }
);

Autocompleter.Local demo

<input id="bands70s" size="40" type="text" />
<div id="bandlistarea"></div>
document.observe("dom:loaded", function() {
	new Autocompleter.Local(
		"bands70s",
		"bandlistarea",
		["ABBA", "AC/DC", "Aerosmith", "America", "Bay City Rollers", ...], 
		{}
	);
});

Autocompleter styling

<input id="bands70s" size="40" type="text" />
<div id="bandlistarea"></div>
#bandlistarea {
	border: 2px solid gray;
}
/* 'selected' class is given to the autocomplete item currently chosen */
#bandlistarea .selected {
	background-color: pink;
}

Using Ajax.Autocompleter

new Ajax.Autocompleter(
	element or id of text box, 
	element or id of div to show completions,
	url, 
	{ options }
);

Ajax.InPlaceEditor

new Ajax.InPlaceEditor(element or id,
	url,
	{ options }
);

Makes it possible to edit the content of elements on a page "live" and send the edits back to the server using Ajax.

Ajax.InPlaceCollectionEditor

new Ajax.InPlaceCollectionEditor(element or id, url, {
	collection: array of choices,
	options
});

Playing sounds (API)

method description
Sound.play("url"); plays a sound/music file
Sound.disable(); stops future sounds from playing (doesn't mute any sound in progress)
Sound.enable(); re-enables sounds to be playable after a call to Sound.disable()
Sound.play("music/java_rap.mp3");
Sound.play("music/wazzaaaaaap.wav");

Other neat features