MooTools: Extending and Implementing Elements

Exclusive offer: get 50% off this eBook here
MooTools 1.3 Cookbook

MooTools 1.3 Cookbook — Save 50%

Over 110 highly effective recipes to turbo-charge the user interface of any web-enabled Internet application and web page

$26.99    $13.50
by Jay Larry G. Johnston | July 2011 | Open Source

MooTools is a JavaScript framework that abstracts the JavaScript language. JavaScript itself, complex in syntax, provides the tools to write a layer of content interaction for each different browser. MooTools abstracts those individual, browser-specific layers to allow cross-browser scripting in an easy-to-read and easy-to-remember syntax.

In this article by Jay Larry G. Johnston, author of MooTools 1.3 Cookbook, we will take a look at how to extend and implement MooTools elements.

 

MooTools 1.3 Cookbook

MooTools 1.3 Cookbook

Over 100 highly effective recipes to turbo-charge the user interface of any web-enabled Internet application and web page

        Read more about this book      

(For more resources on this topic, see here.)

The reader can benefit from the previous article on Extending MooTools.

 

Extending elements—preventing multiple form submissions

Imagine a scenario where click-happy visitors may undo normalcy by double-clicking the submit button, or perhaps an otherwise normal albeit impatient user might click it a second time. Submit buttons frequently need to be disabled or removed using client-side code for just such a reason.

Users that double-click everything
It is not entirely known where double-clicking users originated from. Some believe that single-clicking-users needed to be able to double-click to survive in the wild. They therefore began to grow gills and double-click links, buttons, and menu items. Others maintain that there was a sudden, large explosion in the vapors of nothingness that resulted in hordes of users that could not fathom the ability of a link, button, or menu item that could be opened with just a single click. Either way, they are out there, and they mostly use Internet Explorer and are quickly identifiable by how they type valid URLs into search bars and then swear the desired website is no longer on the Inter-nets.

How to do it...

Extending elements uses the same syntax as extending classes. Add a method that can be called when appropriate. Our example, the following code, could be used in a library that is associated with every page so that no submit button could ever again be clicked twice, at least, without first removing the attribute that has it disabled:

Element.implement({
better_submit_buttons: function() {
if (
this.get('tag')=='input' &&
this.getProperty('type')=='submit') {

this.addEvent('click', function(e) {
this.set({
'disabled':'disabled',
'value':'Form Submitted!'
});
});
}
}
});
window.addEvent('load',function() {
$$('input[type=submit]').better_submit_buttons();
});

How it works...

The MooTools class Element extends DOM elements referenced by the single-dollar selector, the double-dollars selector, and the document.id selector. In the onLoad event, $$('input[type=submit]').submit_only_once(); all INPUT elements that have a type equal to submit are extended with the Element class methods and properties.

Of course, before that infusion of Moo-goodness takes place, we have already implemented a new method that prevents those elements from being clicked twice by adding the property that disables the element.

There's more...

In our example, we disable the submit button permanently and return false upon submission. The only way to get the submit button live again is to click the Try again button that calls the page again. Note that reloading the page via refresh in some browsers may not clear the disabled attribute; however, calling the page again from the URL or by clicking a link will.

On pages that submit a form to a second page for processing, the semi-permanently disabled button is desirable outright. If our form is processed via Ajax, then we can use the Ajax status events to manually remove the disabled property and reset the value of the button.

See also

Read the document on the MooTools Request class that shows the various statuses that could be used in conjunction with this extended element: http://mootools.net/docs/core/Request/Request.

 

Extending elements-prompt for confirmation on submit

Launching off the last extension, the forms on our site may also need to ask for confirmation. It is not unthinkable that a slip of the carriage return could accidentally submit a form before a user is ready. It certainly happens to all of us occasionally and perhaps to some of us regularly.

How to do it...

Mutate the HTML DOM FORM elements to act upon the onSubmit event and prompt whether to continue with the submission.

Element.implement({
polite_forms: function() {
if (this.get('tag')=='form') {
this.addEvent('submit',function(e) {
if(!confirm('Okay to submit form?')) {
e.stop();
}
});
}
}
});

How it works...

The polite_forms() method is added to all HTML DOM elements, but the execution is restricted to elements whose tag is form, if (this.get('tag')=='form') {...}. The onSubmit event of the form is bound to a function that prompts users via the raw JavaScript confirm() dialogue that either returns true for a positive response or false otherwise. If false, then we prevent the event from continuing by calling the MooTools-implemented Event.stop().

There's more...

In order to mix the submit button enhancement with the polite form enhancement only a few small changes to the syntax are necessary. To stop our submit button from showing in process... if the form submission is canceled by the polite form request, we create a proprietary reset event that can be called via Element.fireEvent() and chained to the collection of INPUT children that match our double-dollar selector.

// extend all elements with the method polite forms
Element.implement({
better_submit_buttons: function() {
if (this.get('tag')=='input'&&this.getProperty('type')=='submit') {
this.addEvents({
'click':function(e) {
this.set({'disabled':'disabled','value':'in process...'});
},
'reset':function() {
this.set({'disabled':false,'value':'Submit!'});
}
});
}
},
polite_forms: function() {
if (this.get('tag')=='form') {
this.addEvent('submit',function(e) {
if(!confirm('Okay to submit form?')) {
e.stop();
this.getChildren('input[type=submit]').fireEvent('reset');
}
});
}
}
});
// enhance the forms
window.addEvent('load',function() {
$$('input[type=submit]').better_submit_buttons();
$$('form').polite_forms();
});

 

Extending typeOf, fixing undefined var testing

We could not properly return the type of an undeclared variable. This oddity has its roots in the fact that undefined, undeclared variables cannot be dereferenced during a function call.

In short, undeclared variables can not be used as arguments to a function.

Getting ready

Get ready to see how we can still extend MooTools' typeOf function by passing a missing variable using the global scope:

// will throw a ReferenceError
myfunction(oops_var);
// will not throw a ReferenceError
myfunction(window.oops_var);

How to do it...

Extend the typeOf function with a new method and call that rather than the parent method.

// it is possible to extend functions with new methods
typeOf.extend('defined',function(item) {
if (typeof(item)=='undefined') return 'undefined';
else return typeOf(item);
});

//var oops_var; // commented out "on purpose"
function report_typeOf(ismoo) {
if (ismoo==0) {
document.write('oops_var1 is: '+typeof(oops_var)+'<br/>');
} else {
// concat parent to avoid error from derefrencing an
undeclared var
document.write('oops_var2 is: '+typeOf.defined(
window.oops_var)+'<br/>');
}
}

The output from calling typeof() and typeOf.defined() is identical for an undefined, undeclared variable passed via the global scope to avoid a reference error.

<h2>without moo:</h2>
<script type="text/javascript">
report_typeOf(0);
</script>

<h2><strong>with</strong> moo:</h2>

<script type="text/javascript">
report_typeOf(1);
</script>

The output is:

without moo:
oops_var1 is: undefined

with moo:
oops_var2 is: undefined

How it works...

The prototype for the typeOf function object has been extended with a new method. The original method is still applied when the function is executed. However, we are now able to call the property defined, which is itself a function that can still reference and call the original function.

There's more...

For those that are not satisfied at the new turn of syntax, the proxy pattern should suffice to help keep us using a much similar syntax.

// proxying in raw javascript is cleaner in this case
var oldTypeOf = typeOf;
var typeOf = function(item) {
if (typeof(item)=='undefined') return 'undefined';
else return oldTypeOf(item);
};

The old typeOf function has been renamed using the proxy pattern but is still available. Meanwhile, all calls to typeOf are now handled by the new version.

See also

 

MooTools 1.3 Cookbook Over 110 highly effective recipes to turbo-charge the user interface of any web-enabled Internet application and web page
Published: July 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

 

        Read more about this book      

(For more resources on this topic, see here.)

Extending images—add captions based on ALT attributes

Often we have to work with images, changing how we display metadata related to the images.

Getting ready

Get ready to extend images on our pages by beginning with a base class to mutate downstream classes.

var imgmootater = new Class({
initialize: function() {
this.imgs = $$('img');
},
make_titles: function() {
this.imgs.each(function(el){
el.set('title',el.get('alt'));
});
}
});

To prevent this from being just a boring interface with no real station in life, add a common function, which is to duplicate the IMG tag required ALT attribute into the TITLE attribute so that mouseover actions will display the otherwise hidden metadata.

How to do it...

Extend this class while maintaining inheritance from the parent object; use the Implements keyword. The initialize function of the parent class is executed before the construct method of the new, extended object, thereby populating this.imgs.

var imgcaptions = new Class({
Implements: imgmootater,
initialize: function() {

At this point, we may now diverge from the original class. We have taken advantage of the functionality the original class designer had access to and are ready to move on. Still, that bit about adding the title text was pretty good.

this.make_titles();

There, now we can go on with our new functionality. Wrap each image in a DIV that has the image itself, a break, and the metadata from the ALT tag.

this.imgs.each(function(el) {

// handle the excessive margins of the imgs
var margin = el.getStyle('margin');
el.setStyle('margin',0);

// wrap the img, set styles, insert caption
var c = new Element('div');
c.inject(el,'before').grab(el).setStyles({
'margin':margin, 'text-align':'center'
}).set('html',c.get('html')+'<br/>'+el.get('alt'));
});
}
});

How it works...

Now that we have a base class to make titles, we can instantiate that as follows.

//usage for imgmootater:
var cpt = new imgmootater();
cpt.make_titles();

The cool thing about inheritance, is that we took that first guy's class object and implemented it into our own. To have that functionality AND our own, we need only instantiate our own, newly extended class object instead. That might look like this.

new imgcaptions();

Because we put all our functionality for the imgcaptions object within the construct, there is no need to call a function on the instantiation. It does all the work required, including that which imgmootater would do if we called make_titles().

There's more...

The next recipe shows how yet a third developer might reuse imgmootater for his or her purposes.

 

Extending images—lazy load

Here again, we extend our base object imgmootater.

Getting ready

And, again, the preparation for this recipe is the imgmootater class itself. Sound the trumpets!

var imgmootater = new Class({
initialize: function() { this.imgs = $$('img'); },
make_titles: function() {
this.imgs.each(function(el){
el.set('title',el.get('alt'));
});
}
});

How to do it...

Extend the imgmootater class. Perhaps somewhat academically, we are extending something that already does an action we need. In this case, at least academically, we are demonstrating a recipe that shows that imgmootater does a great deal of important, reusable work. In this example, what it does is duplicate the ALT attribute metadata into the TITLE attribute so that mouse over shows the metadata.

The new implementation of the class will be called lazyloader and will focus on IMG elements that have a place-holding image as their SRC. Add an onScroll event to the window and bind to it the class function, which checks to load all images that have now been scrolled above the fold.

Above the Fold
This terminology is said to have been brought about by the newsprint industry. Any items that were found on the newspaper without unfolding the paper or turning it over were "above the fold". This made much more sense in that industry given that all news papers were folded identically. Web browsers and monitors play a variable role in where exactly the "fold" will appear at any given time. We are gentle with clients that request that things show up "above the fold" while also recognizing that if our script must load images "above the fold" then we must dynamically determine just quite where it is for each individual user.

var boundload;
var lazyloader = new Class({
Implements: imgmootater,
initialize: function(spacer) {
this.make_titles();
this.spacer = spacer;
// we need to bind "this" to this.loadimgs
boundload = this.loadimgs.bind(this);
// now we can pass the bound function to addEvent
window.addEvent('scroll',boundload);
window.addEvent('load',boundload);
},
loadimgs: function() {
// always redefine which elements to loop over
this.imgs = $$('img[src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original='+this.spacer+']');
// stop eternal calling to this method once complete
if (this.imgs.length==0)
window.removeEvent('scroll',boundload);
// what is the viewable area?
var wscor = window.getCoordinates();
var wspos = window.getScroll();
// stop any browser loading of imgs below fold
this.imgs.each(function(el){
var pos = el.getPosition();
if (pos.y<=wscor.bottom+wspos.y) {
el.set('src',el.get('longdesc'));
el.removeProperty('longdesc');
}
});
}
});

How it works...

  • Instantiate the Extended Object
    We will not require the return value of instantiation. Simply use the new keyword, and be sure to pass the path to the spacer image, which is acting as a place holder.
    new lazyloader('10_spacer.gif');
  • Determine the Fold
    The "fold", as it were, is determined by using the MooTool Core included window.getCoordinates() and adding it to the current scroll of the page window.getScroll(). Any element can use these two methods, not just the window.
  • Determine IMG position
    One other method of Element.Dimensions is getPosition(). Once the IMG element in question has a y position less than the window's scrolled position, the actual image source hidden in the XHTML valid LONGDESC attribute is switched into the SRC attribute and the image loads.
  • Save browser memory
    To prevent the script from continually checking to load images, once all images have been loaded, the boundload method of lazyloader is removed from the onScroll event of our window.

There's more...

Say, what happened to the nifty extended class that put captions on our images? For the purposes of trying to keep the recipe short, the intermediary class and its captioning functionality were left out. Have no fear, though, the code snippets include a version that implements both classes to demonstrate MooTools' superior inheritance properties.

var lazyloader = new Class({
Implements: [imgmootater,imgcaptions],
...

Here is a screen capture showing how imgmootater is duplicating the metadata attributes, imgcaptioner is placing the IMG tags in captioned DIVs, and lazyloader is only loading the IMGs that are above the (current) fold.

MooTools 1.3 Cookbook

See also

Follow up on the documentation for Element.Dimensions in the MooTools documentation: http://mootools.net/docs/core/Element/Element.Dimensions.

Summary

In this article we took a look at how to extend and implement MooTools elements.


Further resources on this subject:


MooTools 1.3 Cookbook Over 110 highly effective recipes to turbo-charge the user interface of any web-enabled Internet application and web page
Published: July 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Jay Larry G. Johnston

Jay's first web work was in 1996 for the United States Army where he served the 5/5 Battalion Headquarters with the 2nd Infantry Division in South Korea.

Currently full time as Senior PHP Developer for ICGLink, Inc. managing high-end, custom development for the 2,000+ client base, Jay holds certifications in Linux, MySQL, and PHP5.

Introduced to the MooTools framework in 2007 while on permanent contract at Marshall Space Flight Center, Jay has incorporated Moo into every single new development since and frequently writes canned moo-solutions for use within the company.

Books From Packt


iPhone JavaScript Cookbook
iPhone JavaScript Cookbook

Apache Wicket Cookbook
Apache Wicket Cookbook

Joomla! 1.5 JavaScript jQuery
Joomla! 1.5 JavaScript jQuery

Java EE 6 with GlassFish 3 Application Server
Java EE 6 with GlassFish 3 Application Server

JavaScript Testing Beginner's Guide
JavaScript Testing Beginner's Guide

Apache Maven 3 Cookbook
Apache Maven 3 Cookbook

NetBeans IDE 7 Cookbook
NetBeans IDE 7 Cookbook

Learning jQuery 1.3
Learning jQuery 1.3


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
B
8
E
r
L
G
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software