0 Replies - 566 Views - Last Post: 26 March 2014 - 06:29 AM

#1 Dormilich  Icon User is offline

  • 痛覚残留
  • member icon

Reputation: 3479
  • View blog
  • Posts: 10,020
  • Joined: 08-June 10

Capturing Event handling for IE < 9

Posted 26 March 2014 - 06:29 AM

In case you need to support capturing event handlers on IE8 and older.

/* based upon an addEvent() implementation from Tino Zijdel */
var Events = (function ()
{
	/**
	 * workaround for Array.lastIndexOf()
	 *
	 * @param (mixed) val          value to search for
	 * @param (Array) arr          array to search in
	 * @return (int)               array index of val or -1
	 */
	function array_search(val, arr) 
	{
		try {
			return arr.lastIndexOf(val);
		}
		catch (e) {
			var i = arr.length;
			while (i--) { 
				if (arr[i] && arr[i] === val) {
					break;
				} 
			}
			return i;
		}
	}

	/**
	 * build the array of elements to loop through in capturing phase
	 *
	 * @param (Node) elem          element from which to search up
	 * @return (Array)             list of elements starting with input
	 *                             element until top node
	 */
	function getParents(elem)
	{
		var p = [elem];
		while (elem.parentNode) {
			elem = elem.parentNode;
			p.push(elem);
		}
		return p;
	}

	/**
	 * workaround for Function.call()
	 *
	 * @param (Function) Fn        function reference
	 * @param (Node)               node on which the handler is to execute
	 * @param (Event)              current event object
	 * @return (mixed)             return value of the function Fn
	 */
	function callHandler(Fn, obj, evt)
	{
		var retValue = true;
		
		if (Function.call) {
			retValue = Fn.call(obj, evt);
		} 
		else {
			try { 
				obj.__fn = Fn;
				retValue = obj.__fn(evt);
				delete obj.__fn; 
			} 
			catch (f) { 
				obj.__fn = null; 
			}
		}
		return retValue;
	}

	/**
	 * function that executes events according to DOM
	 *
	 * @param (mixed) e            Event object, if passed
	 * @return (bool)              combined return value of bubble handlers
	 */
	function IEHandler(e)
	{
		// if no Event object is passed (IE)
		e = e || window.event;
		// add .stopPropagation()
		if (!e.stopPropagation) {
			e.stopPropagation = function () 
			{
				this.cancelEvent  = true;
				this.cancelBubble = true;
			}
		}
		    // get Event Target
		var base      = e.target || e.srcElement,
			// get Event Type
			evTypeRef = '__' + e.type, 
			// return values for bubbling handlers
			retValue  = true, 
			i, j, l, elem, chain, evPhase;
		
		// execute only once
		if (base == this) {
			// get elements for capturing
			chain = getParents(base);
			for (i = chain.length; i--;)/> {
				elem = chain[i];
				// if there are functions attached
				// to execute in capturing context
				if (elem[evTypeRef] && elem[evTypeRef].capture) {
					evPhase = elem[evTypeRef].capture;
					// execute each function
					for (j = 0, l = evPhase.length; j < l; j++) {
						if (evPhase[j]) {
							// exit on stopPropagation()
							if (e.cancelEvent === true) {
								return null;
							}
							// there can be no value returned due to the
							// executing element of this loop is the Event 
							// target and not the element the handler
							// executes on.
							callHandler(evPhase[j], elem, e);
						}
					}
				}
			}
		}
		// if there are functions attached to execute in bubbling context
		if (this[evTypeRef] && this[evTypeRef].bubble) {
			evPhase = this[evTypeRef].bubble;
			for (j = 0, l = evPhase.length; j < l; j++) {
				if (evPhase[j]) {
					// this time we're in the correct element context so we can
					// safely return something
					retValue = callHandler(evPhase[j], this, e) && retValue;
				}
			}
		}
		return retValue;
	}
	
	/**
	 * return interface
	 *
	 * @public add
	 * @public remove
	 */
	return {
		add : function (obj, evType, fn, useCapture) 
		{
			if (typeof fn !== "function") {
				throw new TypeError("Function expected!");
			}
			// make useCapture a Boolean
			useCapture = !!useCapture;
			
			// W3C
			if (obj.addEventListener) {
				obj.addEventListener(evType, fn, useCapture);
			} 
			else {
				var evTypeRef = '__' + evType,
					phase     = useCapture ? "capture" : "bubble";
				
				// create ".__event" property
				if (!obj[evTypeRef]) {
					obj[evTypeRef] = {};
				}
				// search in ".__event.bubble"/".__event.capture"
				// if function is already registered
				if (obj[evTypeRef][phase]) {
					if (array_search(fn, obj[evTypeRef][phase]) > -1) {
						return;
					}
				}
				// create ".__event.bubble"/".__event.capture" property
				else {
					obj[evTypeRef][phase] = [];
					if (!useCapture && obj['on' + evType] && obj['on' + evType] != IEHandler) {
						// add any previous function assigned to .onevent
						obj[evTypeRef][phase][0] = obj['on' + evType];
					}
				}
				// add function to stack
				obj[evTypeRef][phase].push(fn);
				// set "global" handler
				obj['on' + evType] = IEHandler;
			}
		},

		remove : function (obj, evType, fn, useCapture) 
		{
			useCapture = !!useCapture;
			
			if (obj.removeEventListener) {
				obj.removeEventListener(evType, fn, useCapture);
			} 
			else {
				var evTypeRef = '__' + evType,
					phase     = useCapture ? "capture" : "bubble",
					evPhase, i;
				
				if (obj[evTypeRef] && obj[evTypeRef][phase]) {
					evPhase = obj[evTypeRef][phase];
					i = array_search(fn, evPhase);
					if (i > -1) {
						try {
							delete evPhase[i];
						} 
						catch (e) {
							evPhase[i] = null;
						}
					}
				}
			}
		}
	}
})();



Usage:
// e.g. form validation
Event.add(document.form_name, "submit", validateForm);

// make a table unclickable
Event.add(document.getElementById("a_table"), "click", function cancel(e) {
	e.stopPropagation();
}, true);


Is This A Good Question/Topic? 0
  • +

Page 1 of 1