[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/node-scroll-info/ -> node-scroll-info-debug.js (source)

   1  /*
   2  YUI 3.17.2 (build 9c3c78e)
   3  Copyright 2014 Yahoo! Inc. All rights reserved.
   4  Licensed under the BSD License.
   5  http://yuilibrary.com/license/
   6  */
   7  
   8  YUI.add('node-scroll-info', function (Y, NAME) {
   9  
  10  /*jshint onevar:false */
  11  
  12  /**
  13  Provides the ScrollInfo Node plugin, which exposes convenient events and methods
  14  related to scrolling.
  15  
  16  @module node-scroll-info
  17  @since 3.7.0
  18  **/
  19  
  20  /**
  21  Provides convenient events and methods related to scrolling. This could be used,
  22  for example, to implement infinite scrolling, or to lazy-load content based on
  23  the current scroll position.
  24  
  25  ### Example
  26  
  27      var body = Y.one('body');
  28  
  29      body.plug(Y.Plugin.ScrollInfo);
  30  
  31      body.scrollInfo.on('scrollToBottom', function (e) {
  32          // Load more content when the user scrolls to the bottom of the page.
  33      });
  34  
  35  @class Plugin.ScrollInfo
  36  @extends Plugin.Base
  37  @since 3.7.0
  38  **/
  39  
  40  var doc = Y.config.doc,
  41      win = Y.config.win;
  42  
  43  /**
  44  Fired when the user scrolls within the host node.
  45  
  46  This event (like all scroll events exposed by ScrollInfo) is throttled and fired
  47  only after the number of milliseconds specified by the `scrollDelay` attribute
  48  have passed in order to prevent thrashing.
  49  
  50  This event passes along the event facade for the standard DOM `scroll` event and
  51  mixes in the following additional properties.
  52  
  53  @event scroll
  54  @param {Boolean} atBottom Whether the current scroll position is at the bottom
  55      of the scrollable region.
  56  @param {Boolean} atLeft Whether the current scroll position is at the extreme
  57      left of the scrollable region.
  58  @param {Boolean} atRight Whether the current scroll position is at the extreme
  59      right of the scrollable region.
  60  @param {Boolean} atTop Whether the current scroll position is at the top of the
  61      scrollable region.
  62  @param {Boolean} isScrollDown `true` if the user scrolled down.
  63  @param {Boolean} isScrollLeft `true` if the user scrolled left.
  64  @param {Boolean} isScrollRight `true` if the user scrolled right.
  65  @param {Boolean} isScrollUp `true` if the user scrolled up.
  66  @param {Number} scrollBottom Y value of the bottom-most onscreen pixel of the
  67      scrollable region.
  68  @param {Number} scrollHeight Total height in pixels of the scrollable region,
  69      including offscreen pixels.
  70  @param {Number} scrollLeft X value of the left-most onscreen pixel of the
  71      scrollable region.
  72  @param {Number} scrollRight X value of the right-most onscreen pixel of the
  73      scrollable region.
  74  @param {Number} scrollTop Y value of the top-most onscreen pixel of the
  75      scrollable region.
  76  @param {Number} scrollWidth Total width in pixels of the scrollable region,
  77      including offscreen pixels.
  78  @see scrollDelay
  79  @see scrollMargin
  80  **/
  81  var EVT_SCROLL = 'scroll',
  82  
  83      /**
  84      Fired when the user scrolls down within the host node.
  85  
  86      This event provides the same event facade as the `scroll` event. See that
  87      event for details.
  88  
  89      @event scrollDown
  90      @see scroll
  91      **/
  92      EVT_SCROLL_DOWN = 'scrollDown',
  93  
  94      /**
  95      Fired when the user scrolls left within the host node.
  96  
  97      This event provides the same event facade as the `scroll` event. See that
  98      event for details.
  99  
 100      @event scrollLeft
 101      @see scroll
 102      **/
 103      EVT_SCROLL_LEFT = 'scrollLeft',
 104  
 105      /**
 106      Fired when the user scrolls right within the host node.
 107  
 108      This event provides the same event facade as the `scroll` event. See that
 109      event for details.
 110  
 111      @event scrollRight
 112      @see scroll
 113      **/
 114      EVT_SCROLL_RIGHT = 'scrollRight',
 115  
 116      /**
 117      Fired when the user scrolls up within the host node.
 118  
 119      This event provides the same event facade as the `scroll` event. See that
 120      event for details.
 121  
 122      @event scrollUp
 123      @see scroll
 124      **/
 125      EVT_SCROLL_UP = 'scrollUp',
 126  
 127      /**
 128      Fired when the user scrolls to the bottom of the scrollable region within
 129      the host node.
 130  
 131      This event provides the same event facade as the `scroll` event. See that
 132      event for details.
 133  
 134      @event scrollToBottom
 135      @see scroll
 136      **/
 137      EVT_SCROLL_TO_BOTTOM = 'scrollToBottom',
 138  
 139      /**
 140      Fired when the user scrolls to the extreme left of the scrollable region
 141      within the host node.
 142  
 143      This event provides the same event facade as the `scroll` event. See that
 144      event for details.
 145  
 146      @event scrollToLeft
 147      @see scroll
 148      **/
 149      EVT_SCROLL_TO_LEFT = 'scrollToLeft',
 150  
 151      /**
 152      Fired when the user scrolls to the extreme right of the scrollable region
 153      within the host node.
 154  
 155      This event provides the same event facade as the `scroll` event. See that
 156      event for details.
 157  
 158      @event scrollToRight
 159      @see scroll
 160      **/
 161      EVT_SCROLL_TO_RIGHT = 'scrollToRight',
 162  
 163      /**
 164      Fired when the user scrolls to the top of the scrollable region within the
 165      host node.
 166  
 167      This event provides the same event facade as the `scroll` event. See that
 168      event for details.
 169  
 170      @event scrollToTop
 171      @see scroll
 172      **/
 173      EVT_SCROLL_TO_TOP = 'scrollToTop';
 174  
 175  Y.Plugin.ScrollInfo = Y.Base.create('scrollInfoPlugin', Y.Plugin.Base, [], {
 176      // -- Protected Properties -------------------------------------------------
 177  
 178      /**
 179      Height of the visible region of the host node in pixels. If the host node is
 180      the body, this will be the same as `_winHeight`.
 181  
 182      @property {Number} _height
 183      @protected
 184      **/
 185  
 186      /**
 187      Whether or not the host node is the `<body>` element.
 188  
 189      @property {Boolean} _hostIsBody
 190      @protected
 191      **/
 192  
 193      /**
 194      Width of the visible region of the host node in pixels. If the host node is
 195      the body, this will be the same as `_winWidth`.
 196  
 197      @property {Number} _width
 198      @protected
 199      **/
 200  
 201      /**
 202      Height of the viewport in pixels.
 203  
 204      @property {Number} _winHeight
 205      @protected
 206      **/
 207  
 208      /**
 209      Width of the viewport in pixels.
 210  
 211      @property {Number} _winWidth
 212      @protected
 213      **/
 214  
 215      // -- Lifecycle Methods ----------------------------------------------------
 216      initializer: function (config) {
 217          // Cache for quicker lookups in the critical path.
 218          this._host                  = config.host;
 219          this._hostIsBody            = this._host.get('nodeName').toLowerCase() === 'body';
 220          this._scrollDelay           = this.get('scrollDelay');
 221          this._scrollMargin          = this.get('scrollMargin');
 222          this._scrollNode            = this._getScrollNode();
 223  
 224          this.refreshDimensions();
 225  
 226          this._lastScroll = this.getScrollInfo();
 227  
 228          this._bind();
 229      },
 230  
 231      destructor: function () {
 232          new Y.EventHandle(this._events).detach();
 233          this._events = null;
 234      },
 235  
 236      // -- Public Methods -------------------------------------------------------
 237  
 238      /**
 239      Returns a NodeList containing all offscreen nodes inside the host node that
 240      match the given CSS selector. An offscreen node is any node that is entirely
 241      outside the visible (onscreen) region of the host node based on the current
 242      scroll location.
 243  
 244      @method getOffscreenNodes
 245      @param {String} [selector] CSS selector. If omitted, all offscreen nodes
 246          will be returned.
 247      @param {Number} [margin] Additional margin in pixels beyond the actual
 248          onscreen region that should be considered "onscreen" for the purposes of
 249          this query. Defaults to the value of the `scrollMargin` attribute.
 250      @return {NodeList} Offscreen nodes matching _selector_.
 251      @see scrollMargin
 252      **/
 253      getOffscreenNodes: function (selector, margin) {
 254          if (typeof margin === 'undefined') {
 255              margin = this._scrollMargin;
 256          }
 257  
 258          var elements = Y.Selector.query(selector || '*', this._host._node);
 259  
 260          return new Y.NodeList(Y.Array.filter(elements, function (el) {
 261              return !this._isElementOnscreen(el, margin);
 262          }, this));
 263      },
 264  
 265      /**
 266      Returns a NodeList containing all onscreen nodes inside the host node that
 267      match the given CSS selector. An onscreen node is any node that is fully or
 268      partially within the visible (onscreen) region of the host node based on the
 269      current scroll location.
 270  
 271      @method getOnscreenNodes
 272      @param {String} [selector] CSS selector. If omitted, all onscreen nodes will
 273          be returned.
 274      @param {Number} [margin] Additional margin in pixels beyond the actual
 275          onscreen region that should be considered "onscreen" for the purposes of
 276          this query. Defaults to the value of the `scrollMargin` attribute.
 277      @return {NodeList} Onscreen nodes matching _selector_.
 278      @see scrollMargin
 279      **/
 280      getOnscreenNodes: function (selector, margin) {
 281          if (typeof margin === 'undefined') {
 282              margin = this._scrollMargin;
 283          }
 284  
 285          var elements = Y.Selector.query(selector || '*', this._host._node);
 286  
 287          return new Y.NodeList(Y.Array.filter(elements, function (el) {
 288              return this._isElementOnscreen(el, margin);
 289          }, this));
 290      },
 291  
 292      /**
 293      Returns an object hash containing information about the current scroll
 294      position of the host node. This is the same information that's mixed into
 295      the event facade of the `scroll` event and other scroll-related events.
 296  
 297      @method getScrollInfo
 298      @return {Object} Object hash containing information about the current scroll
 299          position. See the `scroll` event for details on what properties this
 300          object contains.
 301      @see scroll
 302      **/
 303      getScrollInfo: function () {
 304          var domNode    = this._scrollNode,
 305              lastScroll = this._lastScroll,
 306              margin     = this._scrollMargin,
 307  
 308              scrollLeft   = domNode.scrollLeft,
 309              scrollHeight = domNode.scrollHeight,
 310              scrollTop    = domNode.scrollTop,
 311              scrollWidth  = domNode.scrollWidth,
 312  
 313              scrollBottom = scrollTop + this._height,
 314              scrollRight  = scrollLeft + this._width;
 315  
 316          return {
 317              atBottom: scrollBottom > (scrollHeight - margin),
 318              atLeft  : scrollLeft < margin,
 319              atRight : scrollRight > (scrollWidth - margin),
 320              atTop   : scrollTop < margin,
 321  
 322              isScrollDown : lastScroll && scrollTop > lastScroll.scrollTop,
 323              isScrollLeft : lastScroll && scrollLeft < lastScroll.scrollLeft,
 324              isScrollRight: lastScroll && scrollLeft > lastScroll.scrollLeft,
 325              isScrollUp   : lastScroll && scrollTop < lastScroll.scrollTop,
 326  
 327              scrollBottom: scrollBottom,
 328              scrollHeight: scrollHeight,
 329              scrollLeft  : scrollLeft,
 330              scrollRight : scrollRight,
 331              scrollTop   : scrollTop,
 332              scrollWidth : scrollWidth
 333          };
 334      },
 335  
 336      /**
 337      Returns `true` if _node_ is at least partially onscreen within the host
 338      node, `false` otherwise.
 339  
 340      @method isNodeOnscreen
 341      @param {HTMLElement|Node|String} node Node or selector to check.
 342      @param {Number} [margin] Additional margin in pixels beyond the actual
 343          onscreen region that should be considered "onscreen" for the purposes of
 344          this query. Defaults to the value of the `scrollMargin` attribute.
 345      @return {Boolean} `true` if _node_ is at least partially onscreen within the
 346          host node, `false` otherwise.
 347      @since 3.11.0
 348      **/
 349      isNodeOnscreen: function (node, margin) {
 350          node = Y.one(node);
 351          return !!(node && this._isElementOnscreen(node._node, margin));
 352      },
 353  
 354      /**
 355      Refreshes cached position, height, and width dimensions for the host node.
 356      If the host node is the body, then the viewport height and width will be
 357      used.
 358  
 359      This info is cached to improve performance during scroll events, since it's
 360      expensive to touch the DOM for these values. Dimensions are automatically
 361      refreshed whenever the browser is resized, but if you change the dimensions
 362      or position of the host node in JS, you may need to call
 363      `refreshDimensions()` manually to cache the new dimensions.
 364  
 365      @method refreshDimensions
 366      **/
 367      refreshDimensions: function () {
 368          var docEl = doc.documentElement;
 369  
 370          // On iOS devices and on Chrome for Android,
 371          // documentElement.clientHeight/Width aren't reliable, but
 372          // window.innerHeight/Width are. The dom-screen module's viewport size
 373          // methods don't account for this, which is why we do it here.
 374          if (Y.UA.ios || (Y.UA.android && Y.UA.chrome)) {
 375              this._winHeight = win.innerHeight;
 376              this._winWidth  = win.innerWidth;
 377          } else {
 378              this._winHeight = docEl.clientHeight;
 379              this._winWidth  = docEl.clientWidth;
 380          }
 381  
 382          if (this._hostIsBody) {
 383              this._height = this._winHeight;
 384              this._width  = this._winWidth;
 385          } else {
 386              this._height = this._scrollNode.clientHeight;
 387              this._width  = this._scrollNode.clientWidth;
 388          }
 389  
 390          this._refreshHostBoundingRect();
 391      },
 392  
 393      // -- Protected Methods ----------------------------------------------------
 394  
 395      /**
 396      Binds event handlers.
 397  
 398      @method _bind
 399      @protected
 400      **/
 401      _bind: function () {
 402          var winNode = Y.one('win');
 403  
 404          this._events = [
 405              this.after({
 406                  scrollDelayChange : this._afterScrollDelayChange,
 407                  scrollMarginChange: this._afterScrollMarginChange
 408              }),
 409  
 410              winNode.on('windowresize', this._afterResize, this)
 411          ];
 412  
 413          // If the host node is the body, listen for the scroll event on the
 414          // window, since <body> doesn't have a scroll event.
 415          if (this._hostIsBody) {
 416              this._events.push(winNode.after('scroll', this._afterHostScroll, this));
 417          } else {
 418              // The host node is not the body, but we still need to listen for
 419              // window scroll events so we can determine whether nodes are
 420              // onscreen.
 421              this._events.push(
 422                  winNode.after('scroll', this._afterWindowScroll, this),
 423                  this._host.after('scroll', this._afterHostScroll, this)
 424              );
 425          }
 426      },
 427  
 428      /**
 429      Returns the DOM node that should be used to lookup scroll coordinates. In
 430      some browsers, the `<body>` element doesn't return scroll coordinates, and
 431      the documentElement must be used instead; this method takes care of
 432      determining which node should be used.
 433  
 434      @method _getScrollNode
 435      @return {HTMLElement} DOM node.
 436      @protected
 437      **/
 438      _getScrollNode: function () {
 439          // WebKit returns scroll coordinates on the body element, but other
 440          // browsers don't, so we have to use the documentElement.
 441          return this._hostIsBody && !Y.UA.webkit ? doc.documentElement :
 442                  Y.Node.getDOMNode(this._host);
 443      },
 444  
 445      /**
 446      Underlying element-based implementation for `isNodeOnscreen()`.
 447  
 448      @method _isElementOnscreen
 449      @param {HTMLElement} el HTML element.
 450      @param {Number} [margin] Additional margin in pixels beyond the actual
 451          onscreen region that should be considered "onscreen" for the purposes of
 452          this query. Defaults to the value of the `scrollMargin` attribute.
 453      @return {Boolean} `true` if _el_ is at least partially onscreen within the
 454          host node, `false` otherwise.
 455      @protected
 456      @since 3.11.0
 457      **/
 458      _isElementOnscreen: function (el, margin) {
 459          var hostRect = this._hostRect,
 460              rect     = el.getBoundingClientRect();
 461  
 462          if (typeof margin === 'undefined') {
 463              margin = this._scrollMargin;
 464          }
 465  
 466          // Determine whether any part of _el_ is within the visible region of
 467          // the host element or the specified margin around the visible region of
 468          // the host element.
 469          return !(rect.top > hostRect.bottom + margin
 470                      || rect.bottom < hostRect.top - margin
 471                      || rect.right < hostRect.left - margin
 472                      || rect.left > hostRect.right + margin);
 473      },
 474  
 475      /**
 476      Caches the bounding rect of the host node.
 477  
 478      If the host node is the body, the bounding rect will be faked to represent
 479      the dimensions of the viewport, since the actual body dimensions may extend
 480      beyond the viewport and we only care about the visible region.
 481  
 482      @method _refreshHostBoundingRect
 483      @protected
 484      **/
 485      _refreshHostBoundingRect: function () {
 486          var winHeight = this._winHeight,
 487              winWidth  = this._winWidth,
 488  
 489              hostRect;
 490  
 491          if (this._hostIsBody) {
 492              hostRect = {
 493                  bottom: winHeight,
 494                  height: winHeight,
 495                  left  : 0,
 496                  right : winWidth,
 497                  top   : 0,
 498                  width : winWidth
 499              };
 500  
 501              this._isHostOnscreen = true;
 502          } else {
 503              hostRect = this._scrollNode.getBoundingClientRect();
 504          }
 505  
 506          this._hostRect = hostRect;
 507      },
 508  
 509      /**
 510      Mixes detailed scroll information into the given DOM `scroll` event facade
 511      and fires appropriate local events.
 512  
 513      @method _triggerScroll
 514      @param {EventFacade} e Event facade from the DOM `scroll` event.
 515      @protected
 516      **/
 517      _triggerScroll: function (e) {
 518          var info       = this.getScrollInfo(),
 519              facade     = Y.merge(e, info),
 520              lastScroll = this._lastScroll;
 521  
 522          this._lastScroll = info;
 523  
 524          this.fire(EVT_SCROLL, facade);
 525  
 526          if (info.isScrollLeft) {
 527              this.fire(EVT_SCROLL_LEFT, facade);
 528          } else if (info.isScrollRight) {
 529              this.fire(EVT_SCROLL_RIGHT, facade);
 530          }
 531  
 532          if (info.isScrollUp) {
 533              this.fire(EVT_SCROLL_UP, facade);
 534          } else if (info.isScrollDown) {
 535              this.fire(EVT_SCROLL_DOWN, facade);
 536          }
 537  
 538          if (info.atBottom && (!lastScroll.atBottom ||
 539                  info.scrollHeight > lastScroll.scrollHeight)) {
 540  
 541              this.fire(EVT_SCROLL_TO_BOTTOM, facade);
 542          }
 543  
 544          if (info.atLeft && !lastScroll.atLeft) {
 545              this.fire(EVT_SCROLL_TO_LEFT, facade);
 546          }
 547  
 548          if (info.atRight && (!lastScroll.atRight ||
 549                  info.scrollWidth > lastScroll.scrollWidth)) {
 550  
 551              this.fire(EVT_SCROLL_TO_RIGHT, facade);
 552          }
 553  
 554          if (info.atTop && !lastScroll.atTop) {
 555              this.fire(EVT_SCROLL_TO_TOP, facade);
 556          }
 557      },
 558  
 559      // -- Protected Event Handlers ---------------------------------------------
 560  
 561      /**
 562      Handles DOM `scroll` events on the host node.
 563  
 564      @method _afterHostScroll
 565      @param {EventFacade} e
 566      @protected
 567      **/
 568      _afterHostScroll: function (e) {
 569          var self = this;
 570  
 571          clearTimeout(this._scrollTimeout);
 572  
 573          this._scrollTimeout = setTimeout(function () {
 574              self._triggerScroll(e);
 575          }, this._scrollDelay);
 576      },
 577  
 578      /**
 579      Handles browser resize events.
 580  
 581      @method _afterResize
 582      @protected
 583      **/
 584      _afterResize: function () {
 585          this.refreshDimensions();
 586      },
 587  
 588      /**
 589      Caches the `scrollDelay` value after that attribute changes to allow
 590      quicker lookups in critical path code.
 591  
 592      @method _afterScrollDelayChange
 593      @param {EventFacade} e
 594      @protected
 595      **/
 596      _afterScrollDelayChange: function (e) {
 597          this._scrollDelay = e.newVal;
 598      },
 599  
 600      /**
 601      Caches the `scrollMargin` value after that attribute changes to allow
 602      quicker lookups in critical path code.
 603  
 604      @method _afterScrollMarginChange
 605      @param {EventFacade} e
 606      @protected
 607      **/
 608      _afterScrollMarginChange: function (e) {
 609          this._scrollMargin = e.newVal;
 610      },
 611  
 612      /**
 613      Handles DOM `scroll` events on the window.
 614  
 615      @method _afterWindowScroll
 616      @param {EventFacade} e
 617      @protected
 618      **/
 619      _afterWindowScroll: function () {
 620          this._refreshHostBoundingRect();
 621      }
 622  }, {
 623      NS: 'scrollInfo',
 624  
 625      ATTRS: {
 626          /**
 627          Number of milliseconds to wait after a native `scroll` event before
 628          firing local scroll events. If another native scroll event occurs during
 629          this time, previous events will be ignored. This ensures that we don't
 630          fire thousands of events when the user is scrolling quickly.
 631  
 632          @attribute scrollDelay
 633          @type Number
 634          @default 50
 635          **/
 636          scrollDelay: {
 637              value: 50
 638          },
 639  
 640          /**
 641          Additional margin in pixels beyond the onscreen region of the host node
 642          that should be considered "onscreen".
 643  
 644          For example, if set to 50, then a `scrollToBottom` event would be fired
 645          when the user scrolls to within 50 pixels of the bottom of the
 646          scrollable region, even if they don't actually scroll completely to the
 647          very bottom pixel.
 648  
 649          This margin also applies to the `getOffscreenNodes()` and
 650          `getOnscreenNodes()` methods by default.
 651  
 652          @attribute scrollMargin
 653          @type Number
 654          @default 50
 655          **/
 656          scrollMargin: {
 657              value: 50
 658          }
 659      }
 660  });
 661  
 662  
 663  }, '3.17.2', {"requires": ["array-extras", "base-build", "event-resize", "node-pluginhost", "plugin", "selector"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1