Articles: 843 | Categories: 148   
   
   
Home Articles Contact Us
 
 
 
 
Beginning with Prototype (0 Comments)
Admin: Posted Date: April 4, 2010

Prototype is a JavaScript framework used to help with development of cross-browser code that is easy to maintain and extend.This article contains the fundamentals of Prototype, which you will hopefully find useful for all JavaScript code you write.

Beginning with Prototype

Introduction

Prototype is a JavaScript framework used to help with development of cross-browser code that is easy to maintain and extend.This article contains the fundamentals of Prototype, which you will hopefully find useful for all JavaScript code you write.

Firstly, I will show you how to download and install Prototype into your own web applications.

Next I will show you how to select elements from the DOM (Document Object Model). That is, you'll learn how to access any element from your HTML document (such as a particular link or image). There are several ways to select elements, each of which will be covered.

In the last part of this article I will show you how to create new elements real-time and add them to the DOM. I will also show you how to remove elements from the DOM.

Downloading and Installing Prototype

Prototype is a single JavaScript file that you download and use as a file in your web site. To make use of Prototype, you simply need to load this one file in your HTML code.

Downloading Prototype

The official home page of Prototype is http://prototypejs.org. You can download the current version (which at time of writing is 1.6.0.2) from the download page. The file you download is called prototype-1.6.0.2.js, which you then need to save in your web site file structure.

For the purpose of this article (and subsequent articles in this series), I will assume you have saved this JavaScript file to a directory called /js, and renamed the file to prototype.js (meaning you can upgrade Prototype in the future without having to change your HTML).

For example, if your site was www.example.com the file would be accessible from www.example.com/js/prototype.js. Creating a separate directory in which to hold this file also gives you a place to store your other JavaScript files as required.

Loading Prototype on Your Web Site

Once you have saved the Prototype JavaScript file on your web site you can use it on any of your HTML pages simply by loading that one file. Listing 1 shows the code you would use to load Prototype if you have saved it in the /js directory as described above.

Listing 1 Loading Prototype and viewing the loaded version (listing-1.html)
<html>
<head>
<title>Loading Prototype and viewing the loaded versiontitle>
<script type="text/javascript" src="/js/prototype.js">script>
<script type="text/javascript">
alert('Prototype ' + Prototype.Version + ' loaded');
script>
head>
<body>
body>
html>

In this code listing I have made an alert box appear which shows the version of Prototype loaded using the internal Prototype.Version variable.

Prototype Documentation and Resources

The Prototype web site contains extensive API documentation for Prototype, which you should refer to frequently to help with your own development.

The $() Function

The first function we will look at is the $() function. This function behaves very similarly to document.getElementById() (the non-Prototype way of retrieving a DOM element using JavaScript), except that it returns elements with extended Prototype functionality (covered in the second article of "Eight Weeks of Prototype"). This extended functionality is a series of methods added to each element that simplify your JavaScript development.

Although typically you will only pass one argument to $(), you can in fact pass multiple arguments to it. If one argument is used, then a single element is returned. If more than one argument is used, then an array of elements is returned.

To retrieve an element (or multiple elements) with $(), you can use either the ID of the element you want to select, or the element itself. By passing an element to $(), you can ensure it has been extended with the extra functionality Prototype provides.

Listing 2 shows an example of using $(), in which the div element with an ID of exampleDiv is selected. The content of this div is then modified using the update() method (which is one of the extended methods Prototype provides, covered in part 2 of this series).

Listing 2 Selecting an element with $() and updating its contents (listing-2.html)
<html>
<head>
<title>Selecting an element with $() and updating its contentstitle>
<script type="text/javascript" src="/js/prototype.js">script>
head>
<body>
<div id="exampleDiv">div>
<script type="text/javascript">
$('exampleDiv').update('Div content updated!');
script>
body>
html>

In the code above we know the #exampleDiv element exists, and therefore we don't worry about any error checking, however, you should really ensure the element is correctly returned before trying to perform any further operations on it. If the given element was not found then null is returned. Thus, you can use code similar to that of Listing 3 to ensure the given element was found.

Listing 3 Ensuring the element was successfully returned before using it (listing-3.html)
<html>
<head>
<title>Ensuring the element was successfully returned before using ittitle>
<script type="text/javascript" src="/js/prototype.js">script>
head>
<body>
<div id="exampleDiv">div>
<script type="text/javascript">
var elt = $('exampleDiv');
if (elt)
elt.update('Div content updated!');
script>
body>
html>

Often JavaScript classes you write with Prototype (covered in the sixth article of "Eight Weeks of Prototype") will rely on one or more elements in your HTML document. Checking that the element was selected successfully as in Listing 3 means you can write error-free code and accordingly notify the user (or developer) that there was a problem if the element was not found.

When writing your own functions you may not necessarily know whether you have been passed an element or just an ID. Therefore, you should typically call $() on any arguments that are supposed to be DOM elements. Listing 4 shows a user-defined function and two different (yet identical) ways of calling it.

Listing 4 Using $() to extend function arguments (listing-4.html)
<html>
<head>
<title>Using $() to extend function argumentstitle>
<script type="text/javascript" src="/js/prototype.js">script>
head>
<body>
<div id="exampleDiv">div>
<script type="text/javascript">
function myFunction(elt, message)
{
elt = $(elt);
elt.update(message);
}
myFunction('exampleDiv', 'Div updated');
myFunction($('exampleDiv'), 'Div updated again');
script>
body>
html>

While this example is somewhat trivial, it does demonstrate one of the ambiguities that may arise with JavaScript development: whether a function accepts an element or an element ID. Using $() as in myFunction()above means it doesn't matter either way.

The $$() Function

One of the most powerful functions provided by Prototype is the $$() function. This function allows you to select a series of elements from the DOM by using CSS selectors. The value returned from calling $$() is an array containing each found element, in the order each element appears in the HTML document.

To better demonstrate how $$() works, here are some examples:

  • $$('img') - select all image elements in a document.
  • $$('#container a') - select all links within the element that has the ID container.
  • $$('div.someClass input[type=submit]') - retrieve all submit buttons inside a div with the class someClass.

If no matching elements are found then an empty array is returned.

Listing 5 shows an example of using $$(). In this example we loop over the returned elements and write a number to each element. In the third article in this Prototype article series we will see an easier way to loop over arrays, but for now a simple for loop will suffice.

Listing 5 Selecting a series of elements using $$() and looping over them (listing-5.html)
<html>
<head>
<title>Selecting a series of elements using $$() and looping over themtitle>
<script type="text/javascript" src="/js/prototype.js">script>
head>
<body>
<div>
<ul id="someElement">
<li>li>
<li>li>
<li>li>
ul>
div>
<div id="exampleDiv">div>
<script type="text/javascript">
var items = $$('#someElement li');
for (var i = 0; i < items.size(); i++) {
items[i].update('Element ' + i);
}
script>
body>
html>

Note: The above code uses the size() method to determine the number of elements in the array. This is extended functionality for arrays provided by Prototype. This is covered more extensively in the third article of "Eight Weeks of Prototype".

As a general rule of thumb, $$() is much more efficient when the selection is qualified by including an element ID at the start. For example, if you want to find all elements in the document with the class of someClass and you know all of these occur within an element with ID someId, it is more efficient to use $$('#someId .someClass') rather than $$('.someClass'). The reason the search is more efficient is because rather than checking the class of every element in the DOM, only elements within #someId need to be checked.

When using the $$() function, you can provide more than one argument if required. This allows you to select elements based on multiple CSS selectors. Regardless of which selector is used to find an element, all elements are returned in a single array in document order (just as when using a single argument).

The select() Method

One of the extended methods provided by Prototype is the select() method (formerly called getElementsBySelector()). Just like the $$() function, this method accepts one or more CSS selectors as arguments, however the key difference between select() and $$() is that select() only searches within the element on which it is called.

In the previous section, I discussed including an element's ID in $$() to speed up the search. The example given was to use $$('#someId .someClass'). The equivalent of this search using select() would be to use $('someId').select('.someClass').

Listing 6 shows an example of using select() rather than using $$(). You will find select() most useful when writing classes or functions to which an element has been passed in which you want to find specific elements.

Listing 6 Using select() to search for elements within a given element (listing-6.html)
<html>
<head>
<title>Using select() to search for elements within a given elementtitle>
<script type="text/javascript" src="/js/prototype.js">script>
head>
<body>
<div>
<ul id="someElement">
<li>li>
<li>li>
<li>li>
ul>
div>
<div id="exampleDiv">div>
<script type="text/javascript">
var elt = $('someElement');
var items = elt.select('li');
for (var i = 0; i < items.size(); i++) {
items[i].update('Element ' + i);
}
script>
body>
html>

DOM Traversal Methods

In addition to being able to select elements in the DOM using $$() and select(), Prototype also allows you to select elements using the up(), down(), next() and previous() methods. These methods help you to easily find elements relative to a given element.

Unlike $$() and select(), each of these methods is used to retrieve exactly one element (if found). Because of this (and because each element is returned with the extended Prototype functionality), you can chain these calls together, as you will see at the end of this section.

The up() method

To find an element further up the DOM tree from a given element (that is, to find one of its ancestors) you can use the up() method. If no arguments are passed to up(), then the element's parent is returned.

If you don't just want an element's parent element but rather one of its other ancestors there are several different combinations of arguments that can be used. Firstly, you can specify a numerical index. For instance, using up(0) will retrieve the element's parent (the same as omitting the argument), using up(1) will return the element's grandparent, and using up(2) will return the great-grandparent.

Alternatively, you can pass a CSS selector to up(). The first matched element is returned. For example, if you have an image inside a HTML table (e.g.

), you can use imgElt.up('table') to retrieve the table element (in this case using just imgElt.up() might return the element instead).

You can also specify a numerical index along with a CSS selector. For example, if you have an image within two nested div elements, you can select the outer div by using imgElt.up('div', 1) (the first element from the target has an index of 0, which is the default value if the second argument isn't specified).

Listing 7 shows some examples of how to use the up() method to find an element's ancestors.

Listing 7 Examples of using up() to find an element's ancestors (listing-7.html)
<html>
<head>
<title>Examples of using up() to find an element's ancestorstitle>
<script type="text/javascript" src="/js/prototype.js">script>
head>
<body>
<div id="main" class="foo">
<table class="foo">
<tr>
<td>
<a href="#"><img src="someImage.png" id="someImage" />a>
td>
tr>
table>
div>
<script type="text/javascript">
var img = $('someImage');
// these are all equivalent ways of retrieving the link:
var link = img.up();
var link = img.up(0);
var link = img.up('a');
// similarly, there are several ways to retrieve the surrounding cell
var cell = img.up(1);
var cell = img.up('td');
// the foo class is used in two different places
var table = img.up('.foo');
var div   = img.up('.foo', 1);
script>
body>
html>

One of the most useful aspects of up() is that you can easily find an element without caring which elements lie between the element you want to find and the element you're searching on. That is, because you can use selectors to find the parent, you don't mind whether the element is the parent, the grandparent or otherwise.

For example, if you have a generic JavaScript class that relies on there being elements named in a particular way (by way of element IDs or class names), you don't have to worry about the specific structure of the elements in the DOM.

The down() method

The down() method is the opposite of the up() method, in that it searches within an element's descendants rather than in its ancestors. That is, it looks for elements within the target element.

Just like up(), you can either specify no arguments, a numerical index, a CSS selector, or a CSS selector with a numerical index. Specifying no arguments will result in the first child being returned.

Using down() is very similar to using the select() method covered earlier in this article, except that only a single element is returned using down() (remember that select() returns an array). Because of this, we can deduce that someElt.down('.foo') is effectively equivalent to someElt.select('.foo')[0].

The important difference is that trying to reference an particular element when using select() is that a JavaScript error will occur if the select() call returns an empty array. This is not an issue when using down().

Listing 8 shows some examples of using down() to find an element's descendants.

Listing 8 Selecting an element's descendants using down() (listing-8.html)
<html>
<head>
<title>Selecting an element's descendants using down()title>
<script type="text/javascript" src="/js/prototype.js">script>
head>
<body>
<div id="main" class="foo">
<table class="foo">
<tr>
<td>
<a href="#"><img src="someImage.png" id="someImage" />a>
td>
<td>
Second cell
td>
tr>
table>
div>
<script type="text/javascript">
var div = $('main');
// there are several ways to find the table element
var table = div.down();
var table = div.down('table');
var table = div.down('.foo');
// you can specify an index to find a particular match
var secondCell = div.down('td', 1);
// complex selectors can be used
var image = div.down('table.foo a img#someImage');
script>
body>
html>

The next() and previous() methods

You can find sibling elements (that is, any element with the same parent element as the search target) using the next() and previous() methods. As suggested by their names, next() finds siblings elements that appear in the document after the search target, while previous() finds only siblings that appear before the search target.

The arguments used for next() and previous() work in the same manner as with up() and down(). That is, you can use a numerical index or a CSS selector.

Listing 9 shows several examples of using next() and previous().

Listing 9 Using next() and previous() to find sibling elements (listing-9.html)
<html>
<head>
<title>Using next() and previous() to find sibling elementstitle>
<script type="text/javascript" src="/js/prototype.js">script>
head>
<body>
<div>
<ul>
<li id="first">First itemli>
<li id="second">Second itemli>
<li id="third">Third itemli>
<li id="fourth">Fourth itemli>
<li id="fifth">Fifth itemli>
ul>
div>
<script type="text/javascript">
var secondElement = $('first').next();
var secondElement = $('third').previous();
var thirdElement = $('third').previous().next();
// this call will return null since #fifth is the
// final child of the unordered list
var nullElement = $('fifth').next();
script>
body>
html>

Chaining traversal calls together

Because calls to up(), down(), next() and previous() each return a single element that has been extended with extra Prototype functionality, we can chain calls to these functions together.

For example, calling elt.down().up() will return the original element elt (note, however, that calling elt.up().down() will not necessarily return the original element; this will depend on the ordering of elements within elt's parent). Similarly, elt.next().previous() will also return elt.

Obviously there is little use for these examples in particular, however you may encounter situations where chaining these calls together is extremely useful. One such example might be to search all siblings of an element. Using elt.next(someSelector) only finds siblings before the given element, while elt.previous(someSelector) only finds siblings after the element. If you wanted to search either before or after, you could do so by using elt.up().down(someSelector).

Note: Depending on your CSS selector, this may also return the search target, not just its siblings. You may need to check for this in your code if this is a problem.

When chaining calls together, there is a risk that one of the later calls in the chain may cause an error due to an earlier call not returning an element (for instance, calling previous() on an element with no siblings will not return a valid element). Because of this, you should only chain your calls together when you know it cannot fail. Otherwise, you should make each call in a separate statement and check the return values accordingly.

Creating New Elements and Inserting Them into the DOM

New in Prototype 1.6.0 is the ability to easily create new DOM elements that will work across all browsers. An element is created by instantiating the Element class. The first argument to this class is the type of element to be created, while the second argument is used to specify the element's attributes.

Listing 10 shows how you would dynamically create a new hyperlink (that is, the equivalent of the using the HTML ). In this example, we call update() on the created element to set the link text.

After an element has been created, it must be inserted into the document. This is achieved by calling the insert() method on another element that already exists in the document.

<html>
<head>
<title>Creating a new element in the DOMtitle>
<script type="text/javascript" src="/js/prototype.js">script>
head>
<body>
<div id="main">
<div>Some itemdiv>
div>
<script type="text/javascript">
var attrs = {
href   : 'http://www.example.com',
target : '_blank'
};
var link = new Element('a', attrs);
link.update('Visit this web site');
$('main').insert(link);
script>
body>
html>

Note: When you create the attributes for a new element, some attribute names need to be quoted. For example, using { class : 'MyClass' } will cause an error in some browsers. Instead, you should use { 'class' : 'MyClass'}.

If you were to manually create the HTML as generated by this JavaScript, it would like that in Listing 11.

<html>
<body>
<div id="main">
<div>Some itemdiv>
<a href="http://www.example.com" target="_blank">Visit this web sitea>a>
div>
body>
html>

When you call the insert() method with just the new element as an argument, that new element then becomes the last child of the target element. Alternatively, you can place the new element before the target, after the target, or as the first child of the target. This is done by slightly changing the argument to insert().

When we used $('main').insert(link), this was the equivalent of calling $('main').insert({ bottom : link }), meaning it was inserted as the last child (as you can see in Listing 11, since it appears after the "Some Item" div). To make it appear before the "Some item" div, you would use $('main').insert({ top : link }). This would result in HTML as in Listing 12.

 
 
Add a Comment:
 
(You must be signed in to comment on an article. Not a member? Click here to register)
   
Title:

Comments: