Created
July 1, 2011 20:15
-
-
Save arian/1059309 to your computer and use it in GitHub Desktop.
A Google+ row selection effect with MooTools
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Moog+ - Google+ active row effect</title> | |
<script src="https://ajax.googleapis.com/ajax/libs/mootools/1.3.2/mootools-nocompat.js"></script> | |
<script src="mootools-more.js"></script> | |
<script src="moog+.js"></script> | |
<script> | |
window.addEvent('domready', function(){ | |
new MoogPlus('wrapper'); | |
}); | |
</script> | |
<style> | |
#wrapper { | |
margin: 50px auto; | |
width: 60%; | |
} | |
.MoogPlus { | |
background: #0f3faa; | |
width: 2px; | |
top: 0; | |
left: 0; | |
position: absolute; | |
} | |
.row { | |
padding: 40px; | |
border-bottom: 1px #999 solid; | |
} | |
.active { | |
background: #EEE; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="wrapper"> | |
<div class="row"> | |
one | |
</div> | |
<div class="row"> | |
two<br />2.1<br />2.2 | |
</div> | |
<div class="row"> | |
three | |
</div> | |
<div class="row"> | |
four<br />4.1 | |
</div> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var MoogPlus = new Class({ | |
Extends: Fx.Morph, | |
options: { | |
rowSelector: '.row', | |
activeClass: 'active', | |
event: 'click', | |
'class': 'MoogPlus', | |
link: 'cancel', | |
duration: 'short' | |
}, | |
initialize: function(wrapper, options){ | |
wrapper = this.wrapper = document.id(wrapper); | |
var position = wrapper.getStyle('position'); | |
if (!(/(absolute|relative)/.test(position))) wrapper.setStyle('position', 'relative'); | |
var element = this.element = new Element('div').inject(wrapper); | |
this.parent(element, options); | |
element.addClass(this.options['class']); | |
var events = this.events = {}, self = this; | |
events[this.options.event + ':relay(' + this.options.rowSelector + ')'] = function(){ | |
self.activate(this); | |
}; | |
this.attach(); | |
}, | |
attach: function(){ | |
this.wrapper.addEvents(this.events); | |
}, | |
detach: function(){ | |
this.wrapper.removeEvents(this.events); | |
}, | |
activate: function(element){ | |
var coords = element.getCoordinates(this.wrapper), | |
activeClass = this.options.activeClass; | |
this.start({ | |
top: coords.top, | |
height: coords.height | |
}); | |
if (this.current) this.current.removeClass(activeClass); | |
this.current = element.addClass(activeClass); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MooTools: the javascript framework. | |
// Load this file's selection again by visiting: http://mootools.net/more/871ac4b04b709ecb76b75c6a0d6b8bb7 | |
// Or build this file again with packager using: packager build More/Element.Delegation | |
/* | |
--- | |
script: More.js | |
name: More | |
description: MooTools More | |
license: MIT-style license | |
authors: | |
- Guillermo Rauch | |
- Thomas Aylott | |
- Scott Kyle | |
- Arian Stolwijk | |
- Tim Wienk | |
- Christoph Pojer | |
- Aaron Newton | |
- Jacob Thornton | |
requires: | |
- Core/MooTools | |
provides: [MooTools.More] | |
... | |
*/ | |
MooTools.More = { | |
'version': '1.3.2.1', | |
'build': 'e586bcd2496e9b22acfde32e12f84d49ce09e59d' | |
}; | |
/* | |
--- | |
name: Events.Pseudos | |
description: Adds the functionality to add pseudo events | |
license: MIT-style license | |
authors: | |
- Arian Stolwijk | |
requires: [Core/Class.Extras, Core/Slick.Parser, More/MooTools.More] | |
provides: [Events.Pseudos] | |
... | |
*/ | |
Events.Pseudos = function(pseudos, addEvent, removeEvent){ | |
var storeKey = 'monitorEvents:'; | |
var storageOf = function(object){ | |
return { | |
store: object.store ? function(key, value){ | |
object.store(storeKey + key, value); | |
} : function(key, value){ | |
(object.$monitorEvents || (object.$monitorEvents = {}))[key] = value; | |
}, | |
retrieve: object.retrieve ? function(key, dflt){ | |
return object.retrieve(storeKey + key, dflt); | |
} : function(key, dflt){ | |
if (!object.$monitorEvents) return dflt; | |
return object.$monitorEvents[key] || dflt; | |
} | |
}; | |
}; | |
var splitType = function(type){ | |
if (type.indexOf(':') == -1 || !pseudos) return null; | |
var parsed = Slick.parse(type).expressions[0][0], | |
parsedPseudos = parsed.pseudos, | |
l = parsedPseudos.length, | |
splits = []; | |
while (l--) if (pseudos[parsedPseudos[l].key]){ | |
splits.push({ | |
event: parsed.tag, | |
value: parsedPseudos[l].value, | |
pseudo: parsedPseudos[l].key, | |
original: type | |
}); | |
} | |
return splits.length ? splits : null; | |
}; | |
var mergePseudoOptions = function(split){ | |
return Object.merge.apply(this, split.map(function(item){ | |
return pseudos[item.pseudo].options || {}; | |
})); | |
}; | |
return { | |
addEvent: function(type, fn, internal){ | |
var split = splitType(type); | |
if (!split) return addEvent.call(this, type, fn, internal); | |
var storage = storageOf(this), | |
events = storage.retrieve(type, []), | |
eventType = split[0].event, | |
options = mergePseudoOptions(split), | |
stack = fn, | |
eventOptions = options[eventType] || {}, | |
args = Array.slice(arguments, 2), | |
self = this, | |
monitor; | |
if (eventOptions.args) args.append(Array.from(eventOptions.args)); | |
if (eventOptions.base) eventType = eventOptions.base; | |
if (eventOptions.onAdd) eventOptions.onAdd(this); | |
split.each(function(item){ | |
var stackFn = stack; | |
stack = function(){ | |
(eventOptions.listener || pseudos[item.pseudo].listener).call(self, item, stackFn, arguments, monitor, options); | |
}; | |
}); | |
monitor = stack.bind(this); | |
events.include({event: fn, monitor: monitor}); | |
storage.store(type, events); | |
addEvent.apply(this, [type, fn].concat(args)); | |
return addEvent.apply(this, [eventType, monitor].concat(args)); | |
}, | |
removeEvent: function(type, fn){ | |
var split = splitType(type); | |
if (!split) return removeEvent.call(this, type, fn); | |
var storage = storageOf(this), | |
events = storage.retrieve(type); | |
if (!events) return this; | |
var eventType = split[0].event, | |
options = mergePseudoOptions(split), | |
eventOptions = options[eventType] || {}, | |
args = Array.slice(arguments, 2); | |
if (eventOptions.args) args.append(Array.from(eventOptions.args)); | |
if (eventOptions.base) eventType = eventOptions.base; | |
if (eventOptions.onRemove) eventOptions.onRemove(this); | |
removeEvent.apply(this, [type, fn].concat(args)); | |
events.each(function(monitor, i){ | |
if (!fn || monitor.event == fn) removeEvent.apply(this, [eventType, monitor.monitor].concat(args)); | |
delete events[i]; | |
}, this); | |
storage.store(type, events); | |
return this; | |
} | |
}; | |
}; | |
(function(){ | |
var pseudos = { | |
once: { | |
listener: function(split, fn, args, monitor){ | |
fn.apply(this, args); | |
this.removeEvent(split.event, monitor) | |
.removeEvent(split.original, fn); | |
} | |
}, | |
throttle: { | |
listener: function(split, fn, args){ | |
if (!fn._throttled){ | |
fn.apply(this, args); | |
fn._throttled = setTimeout(function(){ | |
fn._throttled = false; | |
}, split.value || 250); | |
} | |
} | |
}, | |
pause: { | |
listener: function(split, fn, args){ | |
clearTimeout(fn._pause); | |
fn._pause = fn.delay(split.value || 250, this, args); | |
} | |
} | |
}; | |
Events.definePseudo = function(key, listener){ | |
pseudos[key] = Type.isFunction(listener) ? {listener: listener} : listener; | |
return this; | |
}; | |
Events.lookupPseudo = function(key){ | |
return pseudos[key]; | |
}; | |
var proto = Events.prototype; | |
Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); | |
['Request', 'Fx'].each(function(klass){ | |
if (this[klass]) this[klass].implement(Events.prototype); | |
}); | |
})(); | |
/* | |
--- | |
name: Element.Event.Pseudos | |
description: Adds the functionality to add pseudo events for Elements | |
license: MIT-style license | |
authors: | |
- Arian Stolwijk | |
requires: [Core/Element.Event, Events.Pseudos] | |
provides: [Element.Event.Pseudos] | |
... | |
*/ | |
(function(){ | |
var pseudos = {}, | |
copyFromEvents = ['once', 'throttle', 'pause'], | |
count = copyFromEvents.length; | |
while (count--) pseudos[copyFromEvents[count]] = Events.lookupPseudo(copyFromEvents[count]); | |
Event.definePseudo = function(key, listener){ | |
pseudos[key] = Type.isFunction(listener) ? {listener: listener} : listener; | |
return this; | |
}; | |
var proto = Element.prototype; | |
[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); | |
})(); | |
/* | |
--- | |
script: Element.Delegation.js | |
name: Element.Delegation | |
description: Extends the Element native object to include the delegate method for more efficient event management. | |
credits: | |
- "Event checking based on the work of Daniel Steigerwald. License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz" | |
license: MIT-style license | |
authors: | |
- Aaron Newton | |
- Daniel Steigerwald | |
requires: [/MooTools.More, Element.Event.Pseudos] | |
provides: [Element.Delegation] | |
... | |
*/ | |
(function(){ | |
var eventListenerSupport = !(window.attachEvent && !window.addEventListener), | |
nativeEvents = Element.NativeEvents; | |
nativeEvents.focusin = 2; | |
nativeEvents.focusout = 2; | |
var check = function(split, target, event){ | |
var elementEvent = Element.Events[split.event], condition; | |
if (elementEvent) condition = elementEvent.condition; | |
return Slick.match(target, split.value) && (!condition || condition.call(target, event)); | |
}; | |
var bubbleUp = function(split, event, fn){ | |
for (var target = event.target; target && target != this; target = document.id(target.parentNode)){ | |
if (target && check(split, target, event)) return fn.call(target, event, target); | |
} | |
}; | |
var formObserver = function(eventName){ | |
var $delegationKey = '$delegation:'; | |
return { | |
base: 'focusin', | |
onRemove: function(element){ | |
element.retrieve($delegationKey + 'forms', []).each(function(el){ | |
el.retrieve($delegationKey + 'listeners', []).each(function(listener){ | |
el.removeEvent(eventName, listener); | |
}); | |
el.eliminate($delegationKey + eventName + 'listeners') | |
.eliminate($delegationKey + eventName + 'originalFn'); | |
}); | |
}, | |
listener: function(split, fn, args, monitor, options){ | |
var event = args[0], | |
forms = this.retrieve($delegationKey + 'forms', []), | |
target = event.target, | |
form = (target.get('tag') == 'form') ? target : event.target.getParent('form'); | |
if (!form) return; | |
var formEvents = form.retrieve($delegationKey + 'originalFn', []), | |
formListeners = form.retrieve($delegationKey + 'listeners', []), | |
self = this; | |
forms.include(form); | |
this.store($delegationKey + 'forms', forms); | |
if (!formEvents.contains(fn)){ | |
var formListener = function(event){ | |
bubbleUp.call(self, split, event, fn); | |
}; | |
form.addEvent(eventName, formListener); | |
formEvents.push(fn); | |
formListeners.push(formListener); | |
form.store($delegationKey + eventName + 'originalFn', formEvents) | |
.store($delegationKey + eventName + 'listeners', formListeners); | |
} | |
} | |
}; | |
}; | |
var inputObserver = function(eventName){ | |
return { | |
base: 'focusin', | |
listener: function(split, fn, args){ | |
var events = {blur: function(){ | |
this.removeEvents(events); | |
}}, self = this; | |
events[eventName] = function(event){ | |
bubbleUp.call(self, split, event, fn); | |
}; | |
args[0].target.addEvents(events); | |
} | |
}; | |
}; | |
var eventOptions = { | |
mouseenter: { | |
base: 'mouseover' | |
}, | |
mouseleave: { | |
base: 'mouseout' | |
}, | |
focus: { | |
base: 'focus' + (eventListenerSupport ? '' : 'in'), | |
args: [true] | |
}, | |
blur: { | |
base: eventListenerSupport ? 'blur' : 'focusout', | |
args: [true] | |
} | |
}; | |
if (!eventListenerSupport) Object.append(eventOptions, { | |
submit: formObserver('submit'), | |
reset: formObserver('reset'), | |
change: inputObserver('change'), | |
select: inputObserver('select') | |
}); | |
Event.definePseudo('relay', { | |
listener: function(split, fn, args){ | |
bubbleUp.call(this, split, args[0], fn); | |
}, | |
options: eventOptions | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Demo: http://jsfiddle.net/hMv6M/