As per Nate's recommendation, I have rewritten the Custom Events for Scrolling Towards Element Margins YUI module to leverage the plugin system of YUI. This way you can add it to any existing widget, instead of running it as a standalone.
Getting ready
Here is the complete source code (ScrollActionerPlugin.js):
YUI.add('scroll_actioner_plugin', function(Y) { var BOUNDING_BOX = 'boundingBox', VIEWPORT_REGION = 'viewportRegion', Lang = Y.Lang; // A plugin class designed to animate Widget's show and hide methods. function ScrollActionerPlugin(config) { this.host = config.host; ScrollActionerPlugin.superclass.constructor.apply(this, arguments); } // Define Static properties NAME (to identify the class) and NS (to identify the namespace) ScrollActionerPlugin.NAME = 'scrollActionerPlugin'; ScrollActionerPlugin.NS = 'scrap'; ScrollActionerPlugin.EVT_BOTTOM_SCROLL = 'bottom_scroll'; ScrollActionerPlugin.EVT_LEFT_SCROLL = 'left_scroll'; ScrollActionerPlugin.EVT_RIGHT_SCROLL = 'right_scroll'; ScrollActionerPlugin.EVT_TOP_SCROLL = 'top_scroll'; // Attribute definitions for the plugin ScrollActionerPlugin.ATTRS = { activate: { validator: Lang.isBoolean, value: true }, bottomMargin: { validator: Lang.isNumber, value: 200 }, leftMargin: { validator: Lang.isNumber, value: 200 }, rightMargin: { validator: Lang.isNumber, value: 200 }, topMargin: { validator: Lang.isNumber, value: 200 } }; // Extend Plugin.Base Y.extend(ScrollActionerPlugin, Y.Plugin.Base, { _lastScrollOffset: null, _lastScrollRegion: null, _scrollHandle: null, activate: function() { var that = this; if (that.host.get('rendered')) { if (! that._scrollHandle) { that.host.bindUI(); } } else { that.host.render(); } }, _handleScroll: function() { var that = this, elBb = that.host.get(BOUNDING_BOX), aCurrScrollOffset = that.getScrollOffset(), aLastScrollOffset = that._lastScrollOffset, oRegion = elBb.get('region'), oVieportRegion = elBb.get(VIEWPORT_REGION), isScrollingDown = aLastScrollOffset[1] < aCurrScrollOffset[1], isScrollingLeft = aLastScrollOffset[0] > aCurrScrollOffset[0], isScrollingRight = aLastScrollOffset[0] < aCurrScrollOffset[0], isScrollingUp = aLastScrollOffset[1] > aCurrScrollOffset[1], iBottomScrollTrigger = oRegion.bottom - that.get('bottomMargin'), iLeftScrollTrigger = oRegion.left + that.get('leftMargin'), iRightScrollTrigger = oRegion.right - that.get('rightMargin'), iTopScrollTrigger = oRegion.top + that.get('topMargin'); Y.log('isScrolling (Down|Left|Right|Up)=(' + isScrollingDown + '|' + isScrollingLeft + '|' + isScrollingRight + '|' + isScrollingUp + ')'); Y.log('scrollTrigger (Down|Left|Right|Up)=(' + iBottomScrollTrigger + '|' + iLeftScrollTrigger + '|' + iRightScrollTrigger + '|' + iTopScrollTrigger + ')'); if (isScrollingDown && iBottomScrollTrigger < oVieportRegion.bottom) { Y.log('Firing ScrollActionerPlugin bottom'); that.fire(ScrollActionerPlugin.EVT_BOTTOM_SCROLL, oRegion, oVieportRegion); } if (isScrollingLeft && iLeftScrollTrigger > oVieportRegion.left) { Y.log('Firing ScrollActionerPlugin left'); that.fire(ScrollActionerPlugin.EVT_LEFT_SCROLL, oRegion, oVieportRegion); } if (isScrollingRight && iRightScrollTrigger < oVieportRegion.right) { Y.log('Firing ScrollActionerPlugin right'); that.fire(ScrollActionerPlugin.EVT_RIGHT_SCROLL, oRegion, oVieportRegion); } if (isScrollingUp && iTopScrollTrigger > oVieportRegion.top) { Y.log('Firing ScrollActionerPlugin top'); that.fire(ScrollActionerPlugin.EVT_TOP_SCROLL, oRegion, oVieportRegion); } that._lastScrollOffset = aCurrScrollOffset; }, deactivate: function() { var that = this; if (that._scrollHandle) { that._scrollHandle.detach(); that._scrollHandle = null; } }, bindUI: function() { var that = this, elDoc = that.host.get(BOUNDING_BOX).get('ownerDocument'); that._scrollHandle = elDoc.on('scroll', Y.bind(that._handleScroll, that)); }, destructor: function() { this.deactivate(); }, getScrollOffset: function() { var elBb = this.host.get(BOUNDING_BOX); return [elBb.get('docScrollX'), elBb.get('docScrollY')]; }, initializer: function() { var that = this, elBb = that.host.get(BOUNDING_BOX); that._scrollHandle = null; that._lastScrollOffset = that.getScrollOffset(); that._lastScrollRegion = elBb.get(VIEWPORT_REGION); that.afterHostMethod('bindUI', that.bindUI); if (that.get('activate')) { that.activate(); } } }); Y.ScrollActionerPlugin = ScrollActionerPlugin; }, '', {requires: ['base','widget', 'node', 'plugin']});
How to do it…
Here is a sample code that listens for the bottom margin:
YUI({ modules: { 'scroll_actioner_plugin': { fullpath: '/assets/js/yahoo-3-ext/ScrollActionerPlugin.js', requires: ['base','widget', 'node', 'plugin'] } } }).use('event', 'dom', 'node', 'widget', 'scroll_actioner_plugin', function(Y) { Y.on('load', function() { var elDoc = Y.one('#doc'), oScrollActioner = new Y.Widget({boundingBox: elDoc}); oScrollActioner.plug(Y.ScrollActionerPlugin, {}); oScrollActioner.scrap.on(Y.ScrollActionerPlugin.EVT_BOTTOM_SCROLL, function() { // turn off until DOM response is complete (usually AJAX request or loading new elements) oScrollActioner.scrap.deactivate(); // slight delay for effect setTimeout(function() { var value = elDoc.get('region').height; elDoc.setStyle('height', value + 100); // turn on when DOM response is complete oScrollActioner.scrap.activate(); }, 1000); }); }); });
How it works…
Under the hood, this code works exactly the same as the standalone version. The key difference is how developers work with the plugin. First you create a Widget instance. Then you plugin the ScrollActionerPlugin into that Widget instance. This will attach the scrap
property directly on the Widget instance, which is a reference to the instantiated plugin. You now need to subscribe to scroll events on the plugin object, instead of the widget object. Additionally, the activate
and deactivate
functions are attached to the plugin instance as well.
Overall the plugin code has changed little. The few differences are because it no longer uses the Y.Base.create
function, and it references the host object for finding the bounding box.