Note: Smooth scrolling to hash links is now built into Oxygen editor at Manage > Settings > Global Styles > Scripts > Smooth Scroll to Hash Links. You may follow this tutorial for the “Back to Top” part.

A common question I see being asked in the Oxygen community is:

How do I set up smooth scrolling for same-page jump links to sections and divs on my pages?

In this tutorial, I show how smooth scrolling (courtsey, css-tricks) to target sections can be set up in Oxygen along with a smooth scroll to top functionality (courtsey, codyhouse).

Step 1

At Appearance > Menus, create/edit your menu which appears on the frontend (via the Menu element).

Add URLs in this format: #section-9-224 where section-9-224 is the ID of the section or any other element to which that link should take the user to.

This ID can be obtained by inspecting the element using your browser inspector or in Oxygen itself.

Enter your Navigation Labels.

Step 2

Edit your main sitewide Oxygen template.

Add a Code Block element (screenshot) having this HTML:

<a href="#0" class="cd-top js-cd-top">Top</a>

and this CSS:

/* Back to Top */

.cd-top {
  display: inline-block;
  height: 40px;
  width: 40px;
  position: fixed;
  bottom: 40px;
  right: 10px;
  -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
          box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
  /* image replacement properties */
  overflow: hidden;
  text-indent: 100%;
  white-space: nowrap;
  background: rgba(101, 190, 194, 0.8) url( no-repeat center 50%;
  visibility: hidden;
  opacity: 0;
  -webkit-transition: opacity .3s 0s, visibility 0s .3s, background-color .3s 0s;
  transition: opacity .3s 0s, visibility 0s .3s, background-color .3s 0s;
.cd-top:hover {
  -webkit-transition: opacity .3s 0s, visibility 0s 0s, background-color .3s 0s;
  transition: opacity .3s 0s, visibility 0s 0s, background-color .3s 0s;
} {
  /* the button becomes visible */
  visibility: visible;
  opacity: 1;
} {
  /* if the user keeps scrolling down, the button is out of focus and becomes less visible */
  opacity: .5;

.cd-top:hover {
  background-color: #65bec2;
  opacity: 1;

@media only screen and (min-width: 768px) {
  .cd-top {
    right: 20px;
    bottom: 20px;

@media only screen and (min-width: 1024px) {
  .cd-top {
    height: 60px;
    width: 60px;
    right: 30px;
    bottom: 30px;

In the above replace



Step 3

Install my custom functionality plugin.

Connect to your server using a FTP client and create a file named say, smooth-scrolling.js inside the plugin’s assets/js directory having the following code:

(function ($) {

    // Select all links with hashes
        // Remove links that don't actually link to anything
        .click(function (event) {
            // On-page links
            if (
                location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
                location.hostname == this.hostname
            ) {
                // Figure out element to scroll to
                var target = $(this.hash);
                target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
                // Does a scroll target exist?
                if (target.length) {
                    // Only prevent default if animation is actually gonna happen
                    $('html, body').animate({
                        scrollTop: target.offset().top
                    }, 1000, function () {
                        // Callback after animation
                        // Must change focus!
                        var $target = $(target);
                        if ($":focus")) { // Checking if the target was focused
                            return false;
                        } else {
                            $target.attr('tabindex', '-1'); // Adding tabindex for elements not focusable
                            $target.focus(); // Set focus again

    // Back to Top - by
    var backTop = document.getElementsByClassName('js-cd-top')[0],
        // browser window scroll (in pixels) after which the "back to top" link is shown
        offset = 300,
        //browser window scroll (in pixels) after which the "back to top" link opacity is reduced
        offsetOpacity = 1200,
        scrollDuration = 700
    scrolling = false;
    if (backTop) {
        //update back to top visibility on scrolling
        window.addEventListener("scroll", function (event) {
            if (!scrolling) {
                scrolling = true;
                (!window.requestAnimationFrame) ? setTimeout(checkBackToTop, 250) : window.requestAnimationFrame(checkBackToTop);
        //smooth scroll to top
        backTop.addEventListener('click', function (event) {
            (!window.requestAnimationFrame) ? window.scrollTo(0, 0) : scrollTop(scrollDuration);

    function checkBackToTop() {
        var windowTop = window.scrollY || document.documentElement.scrollTop;
        (windowTop > offset) ? addClass(backTop, 'cd-top--show') : removeClass(backTop, 'cd-top--show', 'cd-top--fade-out');
        (windowTop > offsetOpacity) && addClass(backTop, 'cd-top--fade-out');
        scrolling = false;

    function scrollTop(duration) {
        var start = window.scrollY || document.documentElement.scrollTop,
            currentTime = null;

        var animateScroll = function (timestamp) {
            if (!currentTime) currentTime = timestamp;
            var progress = timestamp - currentTime;
            var val = Math.max(Math.easeInOutQuad(progress, start, -start, duration), 0);
            window.scrollTo(0, val);
            if (progress < duration) {


    Math.easeInOutQuad = function (t, b, c, d) {
        t /= d / 2;
        if (t < 1) return c / 2 * t * t + b;
        return -c / 2 * (t * (t - 2) - 1) + b;

    //class manipulations - needed if classList is not supported
    function hasClass(el, className) {
        if (el.classList) return el.classList.contains(className);
        else return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
    function addClass(el, className) {
        var classList = className.split(' ');
        if (el.classList) el.classList.add(classList[0]);
        else if (!hasClass(el, classList[0])) el.className += " " + classList[0];
        if (classList.length > 1) addClass(el, classList.slice(1).join(' '));
    function removeClass(el, className) {
        var classList = className.split(' ');
        if (el.classList) el.classList.remove(classList[0]);
        else if (hasClass(el, classList[0])) {
            var reg = new RegExp('(\\s|^)' + classList[0] + '(\\s|$)');
            el.className = el.className.replace(reg, ' ');
        if (classList.length > 1) removeClass(el, classList.slice(1).join(' '));


Edit the plugin’s php file and add

    plugin_dir_url( __FILE__ ) . 'assets/js/smooth-scrolling.js',
    array( 'jquery' ),

inside the custom_enqueue_files() function.

Step 4

Upload cd-top-arrow.svg file (source) to the plugin’s assets/images directory.


Need help implementing a tutorial in your site or want to hire me for custom work?


Find the article helpful and wish to donate?



For adding code blocks wrap the code in three backticks. Markdown should work.
Provide a URL of your site/webpage if something is not working.

31 comments on “Smooth scrolling to hash links and Back to Top in Oxygen”

  1. Hi Thank you for the tutorial..I was able to get it to work..But the only thing is, that I cannot see the arrow in the svg (it’s just a on color icon). What could I have done wrong? Thank you

    1. @Lukas You need to connect to your server using a FTP client and navigate to wp-content/plugins/my-custom-functionality.

      plugin.php will be inside that directory.

  2. When activating this, I get

    “Uncaught ReferenceError: addComment is not defined at HTMLAnchorElement.onclick”

    and can no longer reply to other comments.

    Do you have any suggestions on how to fix it?

    1. In the plugin’s php file, if you comment out

          plugin_dir_url( __FILE__ ) . 'assets/js/smooth-scrolling.js',
          array( 'jquery' ),

      does the problem go away?

  3. Hi Sridhar, this is great and exactly what I’ve been looking for. I followed the tutorial exactly, but it doesn’t work for me at all. Can you help troubleshoot?

  4. Hi Sridhar, Thanks for the great tutorial, it is working fine for me except for a couple of issues with the menu items. On my single page website I have a home page with 3 sections (Services/About/Contact), my WordPress menu has 4 menu items 1. home, 2/3/4. Services/About/Contact (custom links created as explained in this tutorial).

    On the initial page load the “home” menu item reflecting the active state properties, but I am having the following issues with the other menu item states…

    When I click on any menu item other than the “home”, the scrolling to respective section is working fine, but the “home” menu item still remains in active state and respective menu item which was clicked remains in normal state, this happens on desktops.
    On mobile devices (iPad and iPhone) when I click on any menu item other than the “home”, the “home” menu item still remains in active state and the respective menu item which was clicked remains in hover state.

    I am not sure whether I am doing something wrong or it is a bug, one thing I figured out is, as it is a single page website, technically the “home” page always remains as the active page and therefore the “home” menu item remains in the active state all the time.

    Please help if you have any solution to this issue. Thank You.

  5. Thanks for great tut Sridhar. I’m wondering how you account for the height of a fixed header in regards to hash-links? I have a fixed secondary quicklinks menu for shuttling between sections. However, the scrollTo positions are off due to the height of the fixed header. Can you help please?


  6. Hi Guys, hello Sridhar Katakam

    I did all the steps and it worked. The only problem I’ve had was with the SVG file.

    The path is not good.

    In the css the path has: “…my-custom-functionality…”

    But the folder downloaded from Github has “…my-custom-functionality-master…”

    Just add master in the css file and it will work.

    WJ Malcom – how did you do the sticky header when scrolling down?

    1. Just add master in the css file and it will work.

      Done. Ty.

      how did you do the sticky header when scrolling down?

      This is built into Oxygen. Select the Header Builder element and you’ll find it.

  7. Ty aQziany i din’t spot that you solve my problem ๐Ÿ™‚ for the sticky header its in header builder -> sticky . but if you talk about the animation effect that make it down scalle is just css :

    .oxy-header.oxy-sticky-header-active {
    background-color:rgba(215, 45, 70, 0.9);
    .oxy-header.oxy-sticky-header-active img {
    .oxy-header img {
    -webkit-transition:0.5s ease all;
    transition:0.5s ease all
    .oxy-header.oxy-sticky-header-active #_header_row-3-14 {
    #_header_row-3-14 {
    -webkit-transition:0.5s ease all;
    transition:0.5s ease all

  8. Your tutorial worked great but for some reason I cannot get the SVG to display. Not sure where I went wrong. I uploaded the SVG to the assets/images folder as instructed. I checked the path of both Inspect Element and the code block and they seem correct so they recognize the file being there but won’t display it. Any advice?

    1. Emily here. Turns out the SVG file won’t display on my localhost but once I transferred my site to a staging server, it worked fine. Weird quirk but it is what it is. Thanks again for your great tutorials. ๐Ÿ™‚

  9. Hello Sridhar, thank you for this tutorial, it works perfectly for me.
    I notice that when I click on a button that renames me to a #link, it is framed as if it were selected by the browser.
    How to make the element not selected?
    Thank you for all your sharing.

    1. That is the browser adding an outline to the element to be helpful for accessibility reasons.

      If you want to remove it, add the following CSS:

      :focus {
      outline: none;

  10. Just want to say thanks for the excellent tutorial. You are a great teacher and I’m so happy I decided to become a “Premium Member”. Well worth the investment and I look forward to you expanding the list of free and premium tutorials. Many sincere thanks! Oh and by the way, it worked first time for me.

linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram
%d bloggers like this: