SitePoint Sponsor

User Tag List

Results 1 to 19 of 19
  1. #1
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    turn off javascript/jquery function after certain scrolling distance

    Hello everybody,

    I'm working on a one-page web site, where each page is as tall as your viewport, and you can scroll down to go to the next pages. I found a nice jQuery plugin called windows.js that automatically snaps your page in place after you've scrolled down, so it always exactly fits your browser window. Works great, but I want this plugin to work on every page except for the last page. I'm not an expert with javascript, so I hope there's an easy solution for this. This is the plugin part in my code:

    Code:
    $(document).ready(function() {
    
    	var $windows = $('.windows');
    
    	$windows.windows({
    		snapping: true,
    		snapSpeed: 400,
    		snapInterval: 1200,
    		onScroll: function(s){},
    		onSnapComplete: function($el){},
    		onWindowEnter: function($el){}
    	});
    });
    What I'm trying to achieve is that this code should run, until you've scrolled past a certain point where the last page hits the top of the window, and from there on you should be able to scroll without the 'snapping'. When you scroll back up to pages above this last one, it should run again.

    Any ideas? Thanks in advance,

    Hugo

  2. #2
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,313
    Mentioned
    178 Post(s)
    Tagged
    8 Thread(s)
    Hi,

    As I understand it, you give elements that should snap into place a specific class name (for example), then initialize the plugin for these elements.

    E.g.

    Code:
    <section class="window"></section>
    <section class="window"></section>
    <section class="window"></section>
    
    $('.window').windows({
            snapping: true,
            snapSpeed: 500,
            snapInterval: 1100,
            onScroll: function(scrollPos){
                // scrollPos:Number
            },
            onSnapComplete: function($el){
                // after window ($el) snaps into place
            },
            onWindowEnter: function($el){
                // when new window ($el) enters viewport
            }
    })
    So, for those elements that shouldn't snap into place, just don't give them that class name.

    Code:
    <section class="window"></section>
    <section class="window"></section>
    <section class="window"></section>
    <section></section> 
    <section></section>
    Or did I miss the point?

  3. #3
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Pullo,

    Thanks, that is the point indeed and that's what I too expected would happen. But it doesn't, when I take away the 'windows' class from the last section and scroll that page into view, it scrolls all the way back to the very top, to the home page again. Here's a link to the site (in progress), that might help and explain it a bit better: http://www.11amtoday.nl/klanten/brandweerboot/v3/

  4. #4
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,313
    Mentioned
    178 Post(s)
    Tagged
    8 Thread(s)
    Quote Originally Posted by hap View Post
    when I take away the 'windows' class from the last section and scroll that page into view, it scrolls all the way back to the very top, to the home page again.
    Oh yeah, that's a bit weird.

    Is their any reason you don't want to apply the same behaviour to the blog section of your site (i.e. give it a class of "window", too)?

  5. #5
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah, each page on the site gets the exact height of your viewport, except for that blog page; that page gets more and more height as more items are being added over time, so that's why I placed that page all the way on the bottom.

    Maybe I can calculate the height of all pages above the blog, and then do 'something' with javascript when you scroll past that height (so when the top of the blog page hits the top of the window), but I'm not too familiar with javascript yet on how to 'disable the windows.js plugin' or something like that when you scroll past that certain point?

  6. #6
    om nom nom nom Stomme poes's Avatar
    Join Date
    Aug 2007
    Location
    Netherlands
    Posts
    10,233
    Mentioned
    47 Post(s)
    Tagged
    1 Thread(s)
    looks like a job for the return statement.


    However I'm not familiar with the plugin, and so I don't know if it actually bothers to measure the distance between currentPos and document top. Or if you'd have to give the blog part another class and write an addition to the plugin, where when .blog top position === window/document top, DoOtherThings. But that would be the detection, based on where the top of Blog is in relation to the document or main window.

  7. #7
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey I did get it to work now, so that's good, however I had to put the whole thing inside the $(window).scroll(function() which is inside the document ready function. I'm not sure if that makes it way too heavy now, as it continuously reads that function while you are scrolling? Or should that be all right? It sort of looks like this now:

    Code:
    $(document).ready(function() {
    
      $(window).scroll(function() {
    
        if (scroll > blogTop) {
          var $windows = '';
        } else if (scroll <= blogTop) {
          var $windows = $('.windows');
        }
    		
        $windows.windows({
          snapping: true,
          snapSpeed: 400,
          snapInterval: 1200,
          onScroll: function(s){},
          onSnapComplete: function($el){},
          onWindowEnter: function($el){}
        });
    
      });
    
    });
    Here's the live link: http://www.11amtoday.nl/klanten/brandweerboot/v3/

  8. #8
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,313
    Mentioned
    178 Post(s)
    Tagged
    8 Thread(s)
    In your position, I would just hack the plugin a little.

    If you look at the plugin source code, you should see this:

    Code JavaScript:
    var _snapWindow = function(){
      // clear timeout if exists
      if(t){clearTimeout(t);}
      // check for when user has stopped scrolling, & do stuff
      if(options.snapping){
        t = setTimeout(function(){
          var $visibleWindow = _getCurrentWindow(), // visible window
              scrollTo = $visibleWindow.offset().top, // top of visible window
              completeCalled = false;
     
          // animate to top of visible window
          $('html:not(:animated),body:not(:animated)').animate({scrollTop: scrollTo }, options.snapSpeed, function(){
            if(!completeCalled){
              if(t){clearTimeout(t);}
              t = null;
              completeCalled = true;
              options.onSnapComplete($visibleWindow);
            }
          });
        }, options.snapInterval);
      }
    };

    Add this line to the top of it:

    Code JavaScript:
    if($(".noSnap").visible( true )){ return false; }

    The .visible() method checks to see if any elements with the class of .noSnap are visible on the screen.
    The plugin does expose its own .isOnScreen() method, but I found this to be unreliable when testing.

    Then we need to add the code for the .visible() method:

    Code JavaScript:
    (function($){
      /**
      * Copyright 2012, Digital Fusion
      * Licensed under the MIT license.
      * [url]http://teamdf.com/jquery-plugins/license/[/url]
      *
      * @author Sam Sehnert
      * @desc A small plugin that checks whether elements are within
      *     the user visible viewport of a web browser.
      *     only accounts for vertical position, not horizontal.
      */
      $.fn.visible = function(partial){
        var $t = $(this),
        $w = $(window),
        viewTop = $w.scrollTop(),
        viewBottom = viewTop + $w.height(),
        _top = $t.offset().top,
        _bottom = _top + $t.height(),
        compareTop  = partial === true ? _bottom : _top,
        compareBottom  = partial === true ? _top : _bottom;
     
        return ((compareBottom <= viewBottom) && (compareTop >= viewTop));
      };
    })(jQuery);

    After that, give the containing section of your blog a class of ".noSnap" and you should be good.

    Here's a demo.
    The final section will not snap into place, whereas the others will.

  9. #9
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi! Super thanks for all your input, that seems like a much better solution than anything I could think of. There's one thing though; it works good if you slowly scroll down, but when you scroll down real fast (either manually or by pressing the blog button in my menu, which auto scrolls down), it automatically scrolls back up again. Same in your demo. Any ideas on that? Besides that it works perfect, thanks very much for helping out.

  10. #10
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,313
    Mentioned
    178 Post(s)
    Tagged
    8 Thread(s)
    Quote Originally Posted by hap View Post
    There's one thing though; it works good if you slowly scroll down, but when you scroll down real fast (either manually or by pressing the blog button in my menu, which auto scrolls down), it automatically scrolls back up again. Same in your demo. Any ideas on that?
    Yeah, that seems to be the timeout event firing.

    Change the beginning of the function to this:

    Code:
    var _snapWindow = function(){
      // clear timeout if exists
      if(t){clearTimeout(t);}
    						
      if($(".noSnap").visible( true )){ return false; }
    I updated my demo to reflect this.

    (Be sure to clear your cache before trying it).

  11. #11
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Sweet! That's it, thank you very much for that. One last small thing I would like to ask you: Right now the function 'visible' returns true as soon as the element with class 'noSnap' enters the screen on the very bottom. Can I easily change that to when the top of that element hits the top of the window instead of enters the bottom?

    Thanks

  12. #12
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,313
    Mentioned
    178 Post(s)
    Tagged
    8 Thread(s)
    Quote Originally Posted by hap View Post
    Can I easily change that to when the top of that element hits the top of the window instead of enters the bottom?
    Sure! You can use the offset function for that. It gives you the element's position relative to the (left,top) of the document.

    Let me know if you get stuck.

  13. #13
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah I'm pretty familiar with the offset function, I'm already using it in my navigation, but I just don't know how or where to put it inside your 'visible' function, to fire it once the top of the blog hits the top of the viewport instead of becoming visible on the bottom of the viewport:

    Code:
    (function($){
    	/**
    	* Copyright 2012, Digital Fusion
    	* Licensed under the MIT license.
    	* http://teamdf.com/jquery-plugins/license/
    	*
    	* @author Sam Sehnert
    	* @desc A small plugin that checks whether elements are within
    	*		 the user visible viewport of a web browser.
    	*		 only accounts for vertical position, not horizontal.
    	*/
    	$.fn.visible = function(partial){
    		var $t			= $(this),
    		$w				= $(window),
    		viewTop			= $w.scrollTop(),
    		viewBottom		= viewTop + $w.height(),
    		_top			= $t.offset().top,
    		_bottom			= _top + $t.height(),
    		compareTop		= partial === true ? _bottom : _top,
    		compareBottom	= partial === true ? _top : _bottom;
    		
    		return ((compareBottom <= viewBottom) && (compareTop >= viewTop));
    	};
    })(jQuery);

  14. #14
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,313
    Mentioned
    178 Post(s)
    Tagged
    8 Thread(s)
    Change this:

    Code JavaScript:
    if($(".noSnap").visible( true )){ return false; }

    To this:

    Code JavaScript:
    if($(".noSnap").visible(true) && ($(document).scrollTop() - $(".noSnap").offset().top) > 0){ 
      return false; 
    }

    Does that work?

  15. #15
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Pullo, that works great. Thank you very much for all your help on this, I really appreciate it! The result so far is online for viewing at http://www.11amtoday.nl/klanten/brandweerboot/v3/

    Thanks!

  16. #16
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,313
    Mentioned
    178 Post(s)
    Tagged
    8 Thread(s)
    No problem

    However, the code is starting to get a bit messy now and we could really do with caching our selectors, so that we are not querying the DOM every time something happens.

    Leave this with me and I'll post an optimized version back here soon.

  17. #17
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wow... that's really nice of you. Thanks! Looking forward to see what that's gonna look like. Cheers!

  18. #18
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,313
    Mentioned
    178 Post(s)
    Tagged
    8 Thread(s)
    Hi,

    I modified the plugin for you a little.
    Now, you should give all of the sections a class of "window" and remove the class of "noSnap".

    HTML Code:
    <section class="window" id="one">Section A</section>
    <section class="window" id="two">Section B</section>
    <section class="window" id="three">Section C</section>
    <section class="window" id="four">Section D</section>
    <section class="window" id="five">Section E</section>        
    <section class="window" id="six">Section F</section>
    When you initialize the plugin, you can now pass in an option of ignoreLast:

    Code JavaScript:
    var $win = $('.window');
    $win.windows({
      snapping: true,
      snapSpeed: 500,
      snapInterval: 1100,
      ignoreLast: true,
      onScroll: function(s){},
      onSnapComplete: function($el){},
      onWindowEnter: function($el){}
    });

    If this is set to true, the final element in the collection that you call the windows() method on, will be allowed to scroll as far as it likes past the top of the viewport, without snapping back into position.

    I made a new demo to reflect this.

    Here's the code:

    Code:
    <!doctype html>
    <html>
      <head>
        <title>windows</title>
        <style>
          html, body{
            height: 100%;
          }
          .window{
            height:100%;
          }
          #one{
            background-color: red;
          }
          #two{
            background-color: blue;
          }
          #three{
            background-color: yellow;
          }
          #four{
            background-color: green;
          }
          #five{
            background-color: pink;
          }
          #six{
            background-color: #639;
            height:3000px;
          }
        </style>
      </head>
      
      <body>
        <section class="window" id="one">Section A</section>
        <section class="window" id="two">Section B</section>
        <section class="window" id="three">Section C</section>
        <section class="window" id="four">Section D</section>
        <section class="window" id="five">Section E</section>        
        <section class="window" id="six">Section F</section> 
              
        <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
        <script>
          /*!
           * windows: a handy, loosely-coupled jQuery plugin for full-screen scrolling windows.
           * Version: 0.0.1
           * Original author: @nick-jonas
           * Website: http://www.workofjonas.com
           * Licensed under the MIT license
           */
          
          ;(function ( $, window, document, undefined ) {
          
          var that = this,
              pluginName = 'windows',
              defaults = {
                  snapping: true,
                  snapSpeed: 500,
                  snapInterval: 1100,
                  ignoreLast: false,
                  onScroll: function(){},
                  onSnapComplete: function(){},
                  onWindowEnter: function(){}
              },
              options = {},
              $w = $(window),
              s = 0, // scroll amount
              t = null, // timeout
              $windows = [];
              
              /**
               * Constructor
               * @param {jQuery Object} element       main jQuery object
               * @param {Object} customOptions        options to override defaults
               */
              function windows( element, customOptions ) {
                  this.element = element;
                  options = options = $.extend( {}, defaults, customOptions) ;
                  this._defaults = defaults;
                  this._name = pluginName;
                  $windows.push(element);
                  var isOnScreen = $(element).isOnScreen();
                  $(element).data('onScreen', isOnScreen);
                  if(isOnScreen) options.onWindowEnter($(element));
              }
          
              /**
               * Get ratio of element's visibility on screen
               * @return {Number} ratio 0-1
               */
              $.fn.ratioVisible = function(){
                  var s = $w.scrollTop();
                  if(!this.isOnScreen()) return 0;
                  var curPos = this.offset();
                  var curTop = curPos.top - s;
                  var screenHeight = $w.height();
                  var ratio = (curTop + screenHeight) / screenHeight;
                  if(ratio > 1) ratio = 1 - (ratio - 1);
                  return ratio;
              };
          
              /**
               * Is section currently on screen?
               * @return {Boolean}
               */
              $.fn.isOnScreen = function(){
                  var s = $w.scrollTop(),
                      screenHeight = $w.height(),
                      curPos = this.offset(),
                      curTop = curPos.top - s;
                  return (curTop >= screenHeight || curTop <= -screenHeight) ? false : true;
              };
          
              /**
               * Get section that is mostly visible on screen
               * @return {jQuery el}
               */
              var _getCurrentWindow = $.fn.getCurrentWindow = function(){
                  var maxPerc = 0,
                      maxElem = $windows[0];
                      
                  $.each($windows, function(i){
                      var perc = $(this).ratioVisible();
                      if(Math.abs(perc) > Math.abs(maxPerc)){
                          maxElem = $(this);
                          maxPerc = perc;
                      }
                  });
                  return $(maxElem);
              };
          
          
              // PRIVATE API ----------------------------------------------------------
          
              /**
               * Window scroll event handler
               * @return null
               */
              var _onScroll = function(){
                  s = $w.scrollTop();
          
                  _snapWindow();
          
                  options.onScroll(s);
          
                  // notify on new window entering
                  $.each($windows, function(i){
                      var $this = $(this),
                          isOnScreen = $this.isOnScreen();
                      if(isOnScreen){
                          if(!$this.data('onScreen')) options.onWindowEnter($this);
                      }
                      $this.data('onScreen', isOnScreen);
                  });
              };
          
              var _onResize = function(){
                  _snapWindow();
              };
          
              var _snapWindow = function(){          
                // clear timeout if exists
                if(t){clearTimeout(t);}
                
                if(options.ignoreLast){
                  if ($(document).scrollTop() - $($windows[$windows.length-1]).offset().top > 0){ 
                    return false;
                  }
                }
                
                // check for when user has stopped scrolling, & do stuff
                if(options.snapping){
                  t = setTimeout(function(){
                    var $visibleWindow = _getCurrentWindow(), // visible window
                        scrollTo = $visibleWindow.offset().top, // top of visible window
                        completeCalled = false;
                        
                    // animate to top of visible window
                    $('html:not(:animated),body:not(:animated)').animate({scrollTop: scrollTo }, options.snapSpeed, function(){
                      if(!completeCalled){
                        if(t){clearTimeout(t);}
                        t = null;
                        completeCalled = true;
                        options.onSnapComplete($visibleWindow);
                      }
                    });
                  }, options.snapInterval);
                }
              };
          
              /**
               * A really lightweight plugin wrapper around the constructor,
                  preventing against multiple instantiations
               * @param  {Object} options
               * @return {jQuery Object}
               */
              $.fn[pluginName] = function ( options ) {
          
                  $w.scroll(_onScroll);
                  $w.resize(_onResize);
          
                  return this.each(function(i) {
                      if (!$.data(this, 'plugin_' + pluginName)) {
                          $.data(this, 'plugin_' + pluginName,
                          new windows( this, options ));
                      }
                  });
              };
          
          })( jQuery, window, document );          
        </script>
      
        <script>
          var $win = $('.window');
          $win.windows({
            snapping: true,
            snapSpeed: 500,
            snapInterval: 1100,
            ignoreLast: true,
            onScroll: function(s){},
            onSnapComplete: function($el){},
            onWindowEnter: function($el){}
          });
        </script>
      </body>
    </html>
    I removed the .visible() method entirely (as it was superfluous) and I amended most of the stuff that was querying the DOM.
    I did keep $(document).scrollTop() however, as that could change if the windows gets resized.

    I hope this helps.

  19. #19
    SitePoint Member
    Join Date
    Jun 2013
    Location
    The Netherlands
    Posts
    15
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey Pullo,

    Thanks again. I updated it and works perfectly. I can see it's much cleaner now and less heavy. Thank you very much!

    Regards,

    Hugo


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •