Mootools Enhanced Elements

December 12 2008

Mootools Enhanced Elements is a little script that I wrote that allows me to add whatever inline styles, class names, attributes, properties or events I want to any HTML Elements I want. I want the same kind of freedom in my XHTML markup that I have in ALL of the other programming languages that I use, I mean, it's been around for-freakin-ever, right? Oh, that's right, I can't have what I want I can only have what W3C wants because they make the rules and I have to follow them. Well, don't get me wrong, I truly do love what W3C has brought to the Internet because I was knee deep in the browser wars when coding a cross-browser website literally meant building four (or more) websites and sending users to the appropriate one based on their user agent strings. My god did that suck! But it's time to move on and regain developmental freedom.

Examples

I had written this article once before and tried to explain all of the merits to what I was doing, but I suck at writing and I don't think that my point was getting across. So I'm changing tactics and from now on I'm going to start writing a lot more examples than explanations (they just turn into rants anyway). =)

Example #1 Code (functional, legal)
<span id="hoverSpan">hover over me</span>
<script type="text/javascript">
/*<![CDATA[*/
$('hoverSpan').addEvents({
  'mouseover': function(){ this.set('text', 'you are hovering over me') },
  'mouseout': function(){ this.set('text', 'hover over me') }
});
/*]]>*/
</script>

Demo
hover over me

Pros
  • It works
  • It validates
Cons
  • Requires custom JavaScript to be written for each individual span element

Example #2 Code (functional, not legal)
<span textover="you are hovering over me">hover over me</span>
<script type="text/javascript">
/*<![CDATA[*/
$$('span[textover]').each(function(el){
  el.set('textout', el.get('text'));
  el.addEvents({
    'mouseover': function(){ this.set('text', this.get('textover')) },
    'mouseout': function(){ this.set('text', this.get('textout')) }
  });
});
/*]]>*/
</script>

Demo
hover over me

Pros
  • It works
  • It scales
Cons
  • It doesn't validate

Example #3 Code (not functional, not legal)
<span mouseover="this.set('text', 'you are hovering over me');" mouseout="this.set('text', 'hover over me');">hover over me</span>

Demo
hover over me

Pros
  • It's simple and easy to understand
Cons
  • It doesn't work
  • It doesn't validate
  • The mouseout text has to be repeated

Example #4 Code (functional, legal)
<span>hover over me<!--{'textover':'you are hovering over me'}--></span>
<script type="text/javascript">
/*<![CDATA[*/
window.addEvent('domready', function(){
  $$('span[textover]').each(function(el){
    el.set('textout', el.get('text'));
    el.addEvents({
      'mouseover': function(){ this.set('text', this.get('textover')) },
      'mouseout': function(){ this.set('text', this.get('textout')) }
    });
  });
});
/*]]>*/
</script>

Demo
hover over me

Pros
  • It works
  • It validates
  • It scales
Cons
  • It requires EnhancedElement.js
  • It requires waiting for domReady

Advantages

There are obviously a million ways to do this but I found myself going this route for several key reasons:

  • W3C Validation
    I am free to enhance elements the way I see fit without affecting W3C specifications.

  • Accessibility
    Hidden comments are ignored so screen reader accessibility is not adversely impacted.

  • Searchability
    Hidden comments are ignored so search engine crawlers are not impacted.

  • Scalability
    Enhanced instructions are located in direct relation to their respective elements so implementing this within any content based system is possible as long as HTML content is allowed. Much like extending through class name groupings this practice lends itself nicely to unobtrusive JavaScript without the limitations of using class names (or even rel attributes for that matter).

The Code

EnhancedElement.js
/*
Script: EnhancedElement.js
  Define additional class names, styles, events, attributes, and properties to
  apply to html elements whenever JavaScript is enabled. These definitions
  are written into a JavaScript object inside an html comment block within the
  particular html element(s) being enhanced.

  Note: Since these instructions are ignored by search engines and screen
  readers they should never contain content or anything else that would
  prevent the current page from being contextually understandable.

  Usage:
  <tag>
    tag content
    <!--{
      'styles': {
        'color': '#f00',
        'background-color': '#000'
      },
      'class': 'harry',
      'className': 'monkey',
      'classes': ['harry', 'monkey', 'fever'],
      'attributes': {
        'checked': 'checked',
        'temperature': '99.7'
      },
      'property1': 'hello',
      'property2': ['hello', 'world'],
      'property3': {
        'key1': 'hello',
        'key2': 'there',
        'key3': 'world'
      },
      'events': {
        'mouseover': function(){ this.set('src', 'on.gif') }
        'mouseover': function(){ this.set('src', 'off.gif') }
      }
    }-->
  </tag>

License:
  MIT-style license
*/

var EnhancedElement = {
  enhance: function(){
    var selector = (arguments.length > 0) ? arguments[0] : ['address','a','big','blockquote','body','b','caption','center','cite','code','dd','dfn','dir','div','dl','dt','em','form','h1','h2','h3','h4','h5','h6','i','kbd','li','menu','ol','option','param','pre','p','samp','select','small','strike','strong','sub','sup','table','td','th','title','tr','tt','ul','u','var'];
    $$(selector).each(function(el){
      var html = el.innerHTML;
      el.getChildren().each(function(child){ html = html.replace(child.innerHTML, '') });
      html = html.replace(/[\n\r]*/g, '');
      var match = html.match(/^.*<!--\s*\{(.*)\}\s*-->.*$/);
      if (match){
        var enhanced = eval('({' + match[1] + '})');
        for (var name in enhanced){
          switch(name){
            case 'class':
            case 'className':
              el.addClass(enhanced[name]);
              break;
            case 'classes':
              for (var i = 0; i < enhanced[name].length; i++) el.addClass(enhanced[name][i]);
            case 'styles':
              for (var n in enhanced[name]) el.setStyle(n, enhanced[name][n]);
              break;
            case 'events':
              for (var n in enhanced[name]) el.addEvent(n, enhanced[name][n]);
              break;
            case 'attributes':
              for (var n in enhanced[name]) el.setAttribute(n, enhanced[name][n]);
              break;
            default:
              if (name.length > 0) el.setProperty(name, enhanced[name]);
              break;
          }
        }
      }
    });
  }
};
window.addEvent('domready', function(){ EnhancedElement.enhance() });

 

I'm always looking for ways to improve on things so if you have any suggestions let me know.