MooTools Element 1.3

By Ryan Florence, published 2010-11-13

Part of the issue MooTools 1.3.

The Element module in MooTools is your gateway to the DOM. In addition to creating, selecting, and manipulating elements, it features element getters, setters, and storage.

As discussed in previous articles in this issue, MooTools provides places to put things that make a lot of sense. The Element module, along with Slick, provide a few more of these places: pseudo selectors, element properties, and element methods.

Selecting Elements

MooTools has two (3-ish) functions for selecting elements.

MooTools uses the brand new Slick parser and selector engine. It is the most accurate selector engine out there. The authors gathered every spec and test out there from every other selector engine, added them to an already enormous list of their own tests, and wrote code until all 2922 of them passed. “It doesn’t matter how fast you get the wrong answer.”

HTML

<nav id="main-nav">
  <ul>
    <li><a>Home</a></li>
    <li><a href="about/">About</a></li>
    <li><a href="contact/">Contact</a></li>
  </ul>
</nav>

JavaScript

var nav       = $('main-nav'),
    listItems = $$('nav > ul > li')
    currentLi = $$('li > a:not([href]) ! li'); // what?!

The first function returns the main-nav element. MooTools returns a real element so things like $('my-input').value still work.

The second function gets all of the list items that are direct children of an unordered list, that is a direct child of a navigation element.

The third function first finds anchors, without an href attribute, that are direct children of a list item, then goes back up the tree and finds the parent li. Pretty, ahem, Slick.

Reverse combinators?! Play with them on jsFiddle

Yes, you read that last function right. Slick features reverse combinators. ! is roughly opposite of > and !~ is roughly opposite of +. These allow you to get exactly what you want without having to write a bunch of code to filter against a more generic result set.

Pseudo Selectors

Slick allows you to create your own pseudo selectors. Here’s a silly example:

View on jsFiddle.net

Slick.definePseudo('is-square', function(n){
  var size = this.getSize();
  return (size.x == size.y);
});

var squares = $$('div:is-square');

You can then place any of your custom pseudos outside of the application where you can test them and reuse them across pages, and across projects.

Element methods

Once you’ve got an element it’s just like any other object upon which you can call methods.

var el = $('el');

el.addClass('hi')
	.removeClass('bye');

el.fade('hide')
  .fade('in')

el.morph({
  'left': [0, 150],
  'top': [0, 200]
});

el.load('/some/ajax/url');

In MooTools core there are 68 element methods: tween, fade, highlight, morph, scrollTo, getSize, getScrollSize, getScroll, getPosition, setPosition, getCoordinates, getOffsetParent, setStyle, getStyle, setStyles, getStyles, getElement, getElements, getElementById, set, get, erase, match, contains, inject, grab, adopt, wraps, appendText, dispose, clone, replaces, hasClass, addClass, removeClass, toggleClass, getPrevious, getAllPrevious, getNext, getAllNext, getFirst, getLast, getParent, getParents, getSiblings, getChildren, empty, destroy, toQueryString, getSelected, getProperty, getProperties, setProperty, setProperties, removeProperty, removeProperties, store, retrieve, eliminate, hasChild, addEvent, removeEvent, addEvents, removeEvents, fireEvent, cloneEvents, send, load.

Looking at those method names reminds me of something great about MooTools: stuff is named well.

Element Storage

If you have kids, you’ve taken a crib apart a few times. Eventually you learn that the best place to store the hardware is with the crib, taped to it. It doesn’t matter how much time has passed, your screws will always be where they needs to be, with the crib.

JavaScript is often similar. Instead of creating new variables to store information that are often more global than they need to be, you can just store information on an element and retrieve it when you need it again.

$('crib').store('screws', ['long', 'short', 'medium']);
// later
$('some-el').retrieve('screws'); // => ['long', 'short', 'medium']

You can store anything on an element: arrays, objects, numbers, strings, other elements, doesn’t matter.

Here’s a possible use case without element storage:

var container = $('some-els'),
    children = container.getChildren(),
    heights = []; // extra place to store things :(

children.each(function(item){
  heights.push(item.getStyle('height'));
  item.setStyle('height', 0);
});

container.addEvent('click:relay(.something)', function(event, target){
  var hidden = target.getStyle('height') == 0;
      index = children.indexOf(target); // slow-ish, extra work
  // have to make sure `heights` is in the same scope here
  target.setStyle('height', hidden ? 0 : heights[index]);
});

Inside of the container’s click event you’ve got to have access to the heights variable. Sometimes it’s tricky to make sure it’s in the same scopre. If you’re in a class you’d probably just make it a property like this.heights. That’s all unnecessary; check this out with element storage:

var container = $('some-els'),
    children = container.getChildren(),

children.each(function(item){
  item.store('height', item.getStyle('height'))
      .setStyle('height', 0)
});

container.addEvent('click:relay(.something)', function(event, target){
  var hidden = target.getStyle('height') == 0;
  target.setStyle('height', hidden ? target.retrieve('height') : 0);
});

Element setters and getters

You can set or get any “normal” element property quite easily:

el.set('text', 'Hello there');
el.get('text'); // => 'Hello there'

You can also define new properties for an element, typically used to extend Element with new methods.

Here’s how I use it to create a couple handy element method shortcuts for my SlideShow plugin.

1. Create a new property for an element

Element.Properties.slideshow = {

  set: function(options){
    this.get('slideshow').setOptions(options, true);
    return this;
  },

  get: function(){
    var instance = this.retrieve('slideshow');
    if (!instance){
      instance = new SlideShow(this);
      this.store('slideshow', instance); // proverbial use of element storage
    }
    return instance;
  }

};

2. Extend element with new methods

Element.implement({

  playSlideShow: function(options){
    var instance = this.get('slideshow');
    if (options) instance.setOptions(options);
    instance.play();
    return this;
  },

  pauseSlideShow: function(){
    this.get('slideshow').pause();
    return this;
  }

});

3. Use it!

// verbose example
var container = $('slideshow-container');
container.set('slideshow', { duration: 1000 });
container.playSlideShow();
var slideshow = container.get('slideshow');

// quick example: playSlideShow takes an options argument
$('slideshow-container').playSlideShow({ duration: 1000 });

Creating elements

When you create elements in MooTools, you can define anything you want about them. Here, we continue on from the last section and even set our custom slideshow property.

// create an element and define LOTS of stuff
var el = new Element('div', {
  'class': 'awesome',
  title: 'Way awesome',
  id: 'dynamically-created',
  styles: {
   background: '#000',
    color: '#fff'
  },
  events: {
    mouseenter: function(){
      // do something
    }
  },
  // yep, can set any element property
  slideshow: {
    duration: 1000,
    transition: 'blindLeftFade'
  }
});

// inject into the dom
el.inject(document.body, 'top');

Continue on to the next article in this issue: MooTools Packager.

Go back to the last article in this issue: MooTools Events 1.3

Hi, I'm Ryan!

Location:
South Jordan, UT
Github:
rpflorence
Twitter:
ryanflorence
Freenode:
rpflo

About Me

I'm a front-end web developer from Salt Lake City, Utah and have been creating websites since the early 90's. I like making awesome user experiences and leaving behind maintainable code. I'm active in the JavaScript community writing plugins, contributing to popular JavaScript libraries, speaking at conferences & meet-ups, and writing about it on the web. I work as the JavaScript guy at Instructure.