This website uses cookies to personalise ads and to analyse traffic ok
web design

Page scroll to id with mousewheel and keyboard

Code sample which should scroll between page sections created with Page scroll to id plugin, using the mouse-wheel and keyboard arrows.

In order to scroll between page sections via mouse-wheel and/or keyboard you’d normally need custom javascript code made specifically for your site layout/markup but I’ve created a more generic script that works along Page scroll to id plugin. The script uses a special plugin class (_mPS2id-h) in order to work independently from most layouts. This class is related to plugin’s highlight feature, meaning that it should work in most cases.

The script should be placed after Page scroll to id plugin files and function call. If you’re using the WordPress version of the plugin, you should place it wherever your theme allows you to add custom javascript or directly in the footer.php template inside a script (<script>...</script>) tag after wp_footer();

The script

(function($){
  $(window).on("load",function(){

    if(!$(document).data("mPS2id")){
      console.log("Error: 'Page scroll to id' plugin not present or activated. Please run the code after plugin is loaded.");
      return;
    }

    $(document).data("mPS2idExtend",{
      selector:"._mPS2id-h",
      currentSelector:function(){
        return this.index($(".mPS2id-highlight-first").length ? $(".mPS2id-highlight-first") : $(".mPS2id-highlight"));
      },
      input:{y:null,x:null},
      i:null,
      time:null
    }).on("scrollSection",function(e,dlt,i){
      var d=$(this).data("mPS2idExtend"),
        sel=$(d.selector);
      if(!$("html,body").is(":animated")){
        if(!i) i=d.currentSelector.call(sel);
        if(!(i===0 && dlt>0) && !(i===sel.length-1 && dlt<0)) sel.eq(i-dlt).trigger("click.mPS2id");
      }
    }).on("keydown",function(e){ //keyboard
      var code=e.keyCode ? e.keyCode : e.which,
        keys=$(this).data("mPS2id").layout==="horizontal" ? [37,39] : [38,40];
      if(code===keys[0] || code===keys[1]){
        if($($(this).data("mPS2idExtend").selector).length) e.preventDefault();
        $(this).trigger("scrollSection",(code===keys[0] ? 1 : -1));
      }
    });

    //mousewheel (https://github.com/jquery/jquery/issues/2871)
    var supportsWheel=false;
    function wheel(e){
      if (e.type == "wheel") supportsWheel = true;
        else if (supportsWheel) return;
      if($($(document).data("mPS2idExtend").selector).length) e.preventDefault();
      $(document).trigger("scrollSection",((e.detail<0 || e.wheelDelta>0 || e.deltaY < 0 || e.deltaX < 0) ? 1 : -1));
    };
    document.addEventListener('wheel', wheel, {passive: false}, false);
    document.addEventListener('mousewheel', wheel, {passive: false}, false);
    document.addEventListener('DOMMouseScroll', wheel, {passive: false}, false);

  });
})(jQuery);

You can change the selector value to a more specific one if you need. This selector should be your “Page scroll to id” main navigation links, so instead of using simply selector:"._mPS2id-h" you could use for example selector:".menu-item ._mPS2id-h"

Important: Please note that you do not have to add the _mPS2id-h class to your elements. It is added automatically by the plugin. Also note that if you have additional links handled by the plugin (e.g. in addition to your menu links, you have back-to-top, next/previous section links etc. in your page), you might need to set your main (e.g. menu) links selector as the “Highlight selector(s)” in plugin settings (e.g. set the option value to .menu-item a).

The script works best when your sections have the same height (or width for horizontal layouts) as the viewport, which is something that most page layouts with this kind of functionality have.

Extending the script with touch events (optional)

If you want to have the same functionality in touch-devices, you can extend the script above like this:

(function($){
  $(window).on("load",function(){

    if(!$(document).data("mPS2id")){
      console.log("Error: 'Page scroll to id' plugin not present or activated. Please run the code after plugin is loaded.");
      return;
    }

    $(document).data("mPS2idExtend",{
      selector:"._mPS2id-h",
      currentSelector:function(){
        return this.index($(".mPS2id-highlight-first").length ? $(".mPS2id-highlight-first") : $(".mPS2id-highlight"));
      },
      input:{y:null,x:null},
      i:null,
      time:null
    }).on("scrollSection",function(e,dlt,i){
      var d=$(this).data("mPS2idExtend"),
        sel=$(d.selector);
      if(!$("html,body").is(":animated")){
        if(!i) i=d.currentSelector.call(sel);
        if(!(i===0 && dlt>0) && !(i===sel.length-1 && dlt<0)) sel.eq(i-dlt).trigger("click.mPS2id");
      }
    }).on("keydown",function(e){ //keyboard
      var code=e.keyCode ? e.keyCode : e.which,
        keys=$(this).data("mPS2id").layout==="horizontal" ? [37,39] : [38,40];
      if(code===keys[0] || code===keys[1]){
        if($($(this).data("mPS2idExtend").selector).length) e.preventDefault();
        $(this).trigger("scrollSection",(code===keys[0] ? 1 : -1));
      }
    }).on("pointerdown touchstart",function(e){ //touch (optional)
      var o=e.originalEvent,
        d=$(this).data("mPS2idExtend");
      if(o.pointerType==="touch" || e.type==="touchstart"){
        var y=o.screenY || o.changedTouches[0].screenY;
        d.input.y=y;
        if($(this).data("mPS2id").layout==="horizontal"){
          var x=o.screenX || o.changedTouches[0].screenX;
          d.input.x=x;
        }
        d.time=o.timeStamp;
        d.i=d.currentSelector.call($(d.selector));
      }
    }).on("touchmove",function(e){
      if($("html,body").is(":animated")) e.preventDefault();
    }).on("pointerup touchend",function(e){
      var o=e.originalEvent;
      if(o.pointerType==="touch" || e.type==="touchend"){
        var y=o.screenY || o.changedTouches[0].screenY,
          d=$(this).data("mPS2idExtend"),
          diff=d.input.y-y,
          time=o.timeStamp-d.time,
          i=d.currentSelector.call($(d.selector));
        if($(this).data("mPS2id").layout==="horizontal"){
          var x=o.screenX || o.changedTouches[0].screenX,
            diff=d.input.x-x;
        }
        if(Math.abs(diff)<2) return;
        var _switch=function(){
            return time<200 && i===d.i;
          };
        $(this).trigger("scrollSection",[(diff>0 && _switch() ? -1 : diff<0 && _switch() ? 1 : 0),(_switch() ? d.i : i)]);
      }
    });

    //mousewheel (https://github.com/jquery/jquery/issues/2871)
    var supportsWheel=false;
    function wheel(e){
      if (e.type == "wheel") supportsWheel = true;
        else if (supportsWheel) return;
      if($($(document).data("mPS2idExtend").selector).length) e.preventDefault();
      $(document).trigger("scrollSection",((e.detail<0 || e.wheelDelta>0 || e.deltaY < 0 || e.deltaX < 0) ? 1 : -1));
    };
    document.addEventListener('wheel', wheel, {passive: false}, false);
    document.addEventListener('mousewheel', wheel, {passive: false}, false);
    document.addEventListener('DOMMouseScroll', wheel, {passive: false}, false);

  });
})(jQuery);

You should also add the following rule to your stylesheet:

body{ -ms-touch-action: none; touch-action: none; }

Please note that using such functionality with touch devices can be (very) tricky and might not work exactly the same across all devices, browsers etc. A good way would be to keep such behavior for mouse and keyboard and let touch devices scroll the page normally.

Auto-generating aside bullet indicators (optional)

If you see the script demo, in addition to the main navigation menu, you’ll notice few bullets at the right which act as links and visual indicators of the sections. You can create those by hand or you can use a small function like this:

$("body").append("<div id='sections-bullets' />").find($(document).data("mPS2idExtend").selector).each(function(){
  $("#sections-bullets").append("<a href='"+$(this).attr("href")+"' class='section-bullet' rel='m_PageScroll2id'></a>");
});

This should be placed within the window load function:

(function($){
  $(window).on("load",function(){

    //the script above goes here...

    //still inside window load we add the function that auto-generates the bullets 
    $("body").append("<div id='sections-bullets' />").find($(document).data("mPS2idExtend").selector).each(function(){
      $("#sections-bullets").append("<a href='"+$(this).attr("href")+"' class='section-bullet' rel='m_PageScroll2id'></a>");
    });

  });
})(jQuery);

If you need help or the code customized for some specific layout let me know in the comments or contact me.

 


110 Comments

Post a comment

Comments pages: 1 2

  1. Kristen Mathiasen
    Posted on May 20, 2019 at 20:19 Permalink

    Hi Manos.
    I have followed your guide on this page but can’t seem to get the scroll-function to work with keyboard-arrows.
    I’m using the WordPress-plugin as well and has put the top JS into the footer.
    I have tried with different selectors and targets but to no avail.
    Do you have any idea where I go woring?
    Thank you in advance.

    Kind regards.

    Kristen

    Reply
    • malihu
      Posted on May 20, 2019 at 23:39 Permalink

      Hello Kristen,

      I checked the source of your page and the js script is not there. Not sure how/where you insert it but it’s not loaded in the front-end.

      Reply
  2. CJ
    Posted on April 5, 2019 at 19:54 Permalink

    Hi, as of the latest chrome update, scrolling events are listed as passive by default. That means event.preventDefault() will be ignored on scroll unless another parameter ({ passive: false }); is passed to the event listener.

    Jquery has an open ticket for this and apparently it is hard to implement passing this extra parameter with jquery.

    Chome feature description:
    https://www.chromestatus.com/feature/6662647093133312

    Github open issue:
    https://github.com/jquery/jquery/issues/2871

    This means that the current suggestion for using this plugin on scroll events results in choppy scrolling (because e.preventDefault() is ignored).

    .on("mousewheel DOMMouseScroll",function(e){ //mousewheel if($($(this).data("mPS2idExtend").selector).length) e.preventDefault(); $(this).trigger("scrollSection",((e.originalEvent.detail<0 || e.originalEvent.wheelDelta>0) ? 1 : -1));

    Do you have any suggestion for how rewrite this part as vanilla js, using addEventListener, so that the “passive” parameter can be passed properly?

    Thanks in advance!

    Reply
    • malihu
      Posted on May 9, 2019 at 04:08 Permalink

      Hello,

      Sorry I think I missed your previous message (I get a lot!)

      At the moment there’s no way to do it with jQuery. The only way is to write a vanilla javascript mouse-wheel function.

      Because of the severity of this issue, I updated the code and replaced the jQuery mouse-wheel function with a pure javascript one. This is needed in order to use the passive: false (see the updated code).

      Let me know

      Reply
    • malihu
      Posted on March 21, 2019 at 20:47 Permalink

      Hi,

      You’ll need to have the section links somewhere in your page (even hidden), because otherwise the script cannot function. You can simply place a div with the links anywhere and hide them via CSS display: none;

      Reply
  3. Tyler K
    Posted on February 3, 2019 at 04:41 Permalink

    Thanks for this plugin, script, and support! I hope you can assist me. I have the plugin installed, but after I put the script into my footer.php, I can no longer scroll with my mouse at all on the site.

    If I use the menu to go to another section on the page, that works fine – but if you scroll down on the mouse, it will bring you always to the top of the page.

    https://testingwith.us/eatit/

    What am I doing wrong to get this working?

    Reply
    • malihu
      Posted on February 3, 2019 at 20:27 Permalink

      Hi,

      Try changing the selector line in the custom script from:

      selector:"._mPS2id-h",

      to:

      selector:".menu-item ._mPS2id-h",

      You could also do this in plugin settings by setting the “Highlight selector(s)” option value to:

      .menu-item a

      Reply
  4. Dambar
    Posted on January 6, 2019 at 06:10 Permalink

    Hi mate

    Scrolling is too fast on mousewheel. It skips the next section if I change scrollSpeed: 400. Is there any options to make it goes to next section not after the next section?

    cheers

    Reply
  5. Balázs Hornyák
    Posted on December 23, 2018 at 23:13 Permalink

    Hi Malihu!

    Everything works fine, except the bullet indicators. I don’t know where I went wrong but I’m using wordpress. And I also don’t know where should I write the: “._mPS2id-h” and wheres these selectors.

    Can I get some help about it?

    Regards!

    Reply
  6. Enrico
    Posted on November 30, 2018 at 12:22 Permalink

    I’m tring to use your plugin on this page https://www.ecopubblicita.it/prova/
    i ‘ve some problem could you help me?
    What’ wrong?
    Thanks
    E

    Reply
    • malihu
      Posted on November 30, 2018 at 19:16 Permalink

      Hi,

      The script should be placed after Page scroll to id plugin files and function call.

      A good place to insert the custom script is in your theme/child theme footer.php template inside a script (<script>...</script>) tag after wp_footer(); function.

      Reply
  7. Yo26
    Posted on October 3, 2018 at 18:29 Permalink

    Hi malihu,

    I am trying to have this scroll directly to sections in my divi one page using your script and the Page scroll to id plugin but I can’t make it work. Could you have a look and let me know if you have an idea why?

    https://goo.gl/rz9tze

    Thanks a lot!

    Reply

Comments pages: 1 2

Post a comment

Cancel reply

Your e-mail is never published nor shared. Required fields are marked *

You may use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
You can write or copy/paste code directly in your comment using the <code> tag:
<code>code here...</code>
You may also use the data-lang attribute to determine the code language like so:
<code data-lang-html>, <code data-lang-css>, <code data-lang-js> and <code data-lang-php>

css.php