Tag Archives: Jssimple

Cross-browser event hookup made easy

I seem to be on a roll. I’m coding and might as well be sharing some of what I’m working on while I do it. In that sense, here goes another small bit of code that I’ll be adding to jsSimple on github.

We all know how to hook up events cross-browser. We know that we have both attachEvent and addEventListener. Both have varying syntax and we’ve all written this code a million times. Let’s just make this simple. Here’s how to call this:

Event.add(element, 'click', callback, false);
// .. 
Event.remove(element, 'click', callback, false);

The remove looks like you’re passing around the same data, so I added a return value to “add” that allows you to capture the event and pass it in as a single object to the remove function as follows:

var evt = Event.add(element, 'click', callback, false);
// .. 
Event.remove(evt);

So, without further delay, here is the object definition:

/*!
 * jsSimple v0.0.1
 * www.jssimple.com
 *
 * Copyright (c) Tobin Titus
 * Available under the BSD and MIT licenses: www.jssimple.com/license/
 */
var EventObj = function (target, type, listener, capture) {
  return {
    'target' : target,
    'type' : type,
    'listener' : listener,
    'capture' : capture
  };
};

var Event = (function() {
  'use strict';
  function add (target, type, listener, capture) {
    capture = capture || false;
    if (target.addEventListener) {
      target.addEventListener(type, listener, capture);
    } else if (target.attachEvent) {
      target.attachEvent("on" + type, listener);
    }
    return new EventObj(target, type, listener, capture);
  }
  
  function remove(object, type, listener, capture) {
    var target;
    if (object && !type && !listener && !capture) {
      type = object.type;
      listener = object.listener;
      capture = object.capture;
      target = object.target;
    } else {
      capture = capture || false;
      target = object;
    }
    
    if (target.removeEventListener) {
      target.removeEventListener(type, listener, capture);
    } else if (target.detachEvent) {
      target.detachEvent("on" + type, listener);
    }
  }
  
  return {
    'add' : add,
    'remove' : remove
  };
}());

You can see this all in action on jsFiddle.

JavaScript animation timing made easy (requestAnimationFrame, cancelRequestAnimationFrame)

I often find myself writing a lot of the same code from project to project. One of those pieces of code is around using requestAnimationFrame and cancelRequestAnimationFrame. I wrote a quick and dirty animation object that allows me to register all of my rendering functions, start the animation and cancel it. Here’s an example of how to use this object:

function render () {
   // Do your rendering work here
}
Animation.add(render);
Animation.start();

It can also be used to add several animations before and even after Animation has started, you can add new rendering routines on the fly. This is useful in a number of instances where objects have their own render routines and you want to add rendering routines at different times.

I put in a few workarounds. The typical hack to allow setTimeout/cancelTimeout fallbacks are in there. There is also a work around to deal with the missing mozCancelRequestAnimationFrame and the fact that mozRequestAnimationFrame doesn’t return an integer per spec.

So here’s the code for the Animation object:

/*!
 * jsSimple v0.0.1
 * www.jssimple.com
 *
 * Copyright (c) Tobin Titus
 * Available under the BSD and MIT licenses: www.jssimple.com/license/
 */
var Animation = (function() {
  "use strict";
  var interval, moz, renderers = [];

// PRIVATE
  function __mozFix (val) {
    /* Mozilla fails to return interger per specification */
    if (!val) { 
      moz = true;
      val = Math.ceil(Math.random() * 10000);
    } 
    return val;
  }

  function __renderAll() {
    var r, length;

    // execute all renderers
    length = renderers.length;
    for (r = 0; r < length; ++r) {
       renderers[r]();
     }
     if (interval) {
       interval = __mozFix(window.requestAnimationFrame(__renderAll));
     }
   }
   function __registerAnimationEventHandlers() {
     var pre, prefixes = ['webkit', 'moz', 'o', 'ms'];
     // check vendor prefixes
     for (pre = prefixes.length - 1; 
          pre >= 0 && !window.requestAnimationFrame; --pre) {
      window.requestAnimationFrame = window[prefixes[pre] 
            + 'RequestAnimationFrame'];
      window.cancelAnimationFrame = window[prefixes[pre] 
            + 'CancelAnimationFrame'] 
            || window[prefixes[pre] + 'CancelRequestAnimationFrame'];
    }

    // downlevel support via setTimeout / clearTimeout
    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = function(callback) {
        // could likely be an adaptive set timeout
        return window.setTimeout(callback, 16.7);
      };
    }

    if (!window.cancelAnimationFrame) {
      window.cancelAnimationFrame = function(id) {
        clearTimeout(id);
      };
    }
  }
  __registerAnimationEventHandlers(); 

// PUBLIC
  function add(renderer) {
    if (renderer) {
      renderers.push(renderer);
    }
  }

  function isRunning() {
    return !!interval;
  }

  function start() {
    interval = __mozFix(window.requestAnimationFrame(__renderAll));
  }

  function stop() {
    if (!moz) {
      window.cancelAnimationFrame(interval);
    }
    interval = null;
  }

// OBJECT DEFINITION
  return {
    "add": add,
    "isRunning": isRunning,
    "start": start,
    "stop": stop
  };
}());

I’ve put this code on jsFiddle to test out.

I’ve also put this on github as jsSimple. I’ll likely add a few other bits of code to this as I move along.