user2287917
user2287917

Reputation: 91

Scroll Through Div While Locking Body Scroll. Overflow:Hidden?

I am using ScrollStop to create a sort of z-axis timeline. It uses scroll events to cycle through timeline content.

The code is uploaded as a website here http://jcwebandgraphic.com/archive2/

The issue:

I have used mouseenter and mouseleave to fire a CSS change - overflow hidden and overflow visible, to prevent scrolling on the body while scrolling in the timeline div.

This has successfully stopped body scrolling, but it has also seemed to stop the scrollstop function that allows me to use the scroll event to move through the timeline.

Is this because the scroll event relies on coordinate inputs and since body scrolling is locked it isn't getting these? Is it something else? Do you have any hints on how this can work with my project? Is there a better event listener or scroll locking method for this project?

The Code:

    <script>
        // When the Window Object is loaded and a Scroll Event is initiated, The following functions will Be Fired
        $(window).on('scrollstart', function() {

            // Timeline Functions for Touchless Devices

                // Block 1 - Scroll Locking

                    // Locks Body Scrolling While in Timeline
                    $('#timeline').on('mouseenter', function() {
                        $('body').css('overflow', 'hidden');
                    }); // Closes $(#timeline) mouseenter Function

                    // Rerstores Body Scrolling While Out of Timeline
                    $('#timeline').on('mouseleave', function() {
                        $('body').css('overflow', 'visible');
                    }); // Closes $(#timeline) mouseleave Function
                    // Closes Functions Block 1

                // Closes Block 1 - Scroll Locking

                // Block 2 - Transitions

                    // Reassigns jQuery Defined HTML Objects as HTML Elements (For Removal and Re-Insertion to the DOM)
                    var last = $('.scroll li:last-child')[0]; // Convert to HTML Element jQuery Element
                    var parent = last.parentElement;

                    // Animate to 0 Opacity
                    $('.scroll li:last-child').animate({opacity: '.1'});

                        // Animation and Re-Ordering Block

                        // 1 Removes Last Child From the DOM
                        // 2 Forces Opacity Back to 1
                        // 3 Re-Inserts it into the DOM at the First Child Position

                        // 1 Removal
                        parent.removeChild(last);
                        // 2 Opacity
                        last.style.opacity = 1;
                        // 3 Re-insertion
                        parent.insertBefore(last, parent.firstElementChild)

                    // Closes Animation and Re-Ordering Block

                // Closes Block 2 - Transitions

        }); //Closes $(window) Load Functions

        // Required Fixes

            // 1 Size / Positioning Glitch on mouseenter, mouseleave (CSS, jQuery)
            // 2 Restore Scroll Transitions on mouseenter (jQuery)
            // 3 Touch Device Usability for Simulated or Alternate mouseenter and mouseleave Functions
            // 4 Create Function Looping if Scroll Event lasts more than ~250ms
            // 5 Make #timeline Height Responsive

        // Completed Fixes

            // 9 Scroll Only Works on First Refresh, or Directly from File  
            // 8 Functions Break When Removing Unnecessary HTML Content
            // 7 Body Continues to Scroll While Scrolling Through Timeline
            // 6 Collapsing Parent Element on #timeline
            // 5 Last Child Failing to Re-Order
            // 4 Limited Scroll Area Forces Premature Animation Transition Completion
            // 3 Limited Scroll Area Prevents Opacity Completion
            // 2 Uncaught Exception Errors (4)
            // 1 Uncaught Token Errors (2)

    </script>

<body>

    <!-- Timeline  -->
    <section id='timeline'>
        <h2 class='subtitle'>Timeline</h2>
        <div class='scroll'>
            <li><img src='images/pa3.jpg'></li>
            <li><img src='images/pa2.jpg'></li>
            <li><img src='images/pa1.jpg'></li>
        </div>
    </section>
    <!-- Closes Timeline -->
</body>

Upvotes: 1

Views: 4406

Answers (2)

Will Po
Will Po

Reputation: 221

Try using body-scroll-lock (https://www.npmjs.com/package/body-scroll-lock).

Setting overflow: hidden on body doesn't work on iOS. You'll need some JS logic to make it work there. To lock body scroll for all devices, whilst enabling a target element to still be able to scroll, you can do something like this using body-scroll-lock:

// 1. Import the functions
const bodyScrollLock = require('body-scroll-lock');
const disableBodyScroll = bodyScrollLock.disableBodyScroll;
const enableBodyScroll = bodyScrollLock.enableBodyScroll;

// 2. Get a target element (such as a modal/lightbox/flyout/nav). 
const targetElement = document.querySelector("#someElementId");


// 3. ...in some event handler after showing the target element...disable body scroll
disableBodyScroll(targetElement);


// 4. ...in some event handler after hiding the target element...
enableBodyScroll(targetElement);

For more details on the rationale, check out https://medium.com/jsdownunder/locking-body-scroll-for-all-devices-22def9615177.

Hope it helps.

Upvotes: 1

tao
tao

Reputation: 90103

There is a major problem with your code. You're binding a new instance of it on each $(window) scrollstart event. After first window scrollstart the #timeline mousenter code will run once on #timeline mouse events. On second window scrollstart it will run twice each time you enter/exit #timeline, and so on...

You have to replace $(window).on('scrollstart', ... with $(window).on('load', .... so you only bind once.


Regarding the body of your script, I suggest a lighter approach. Most times, CSS is faster and requires less resources than JavaScript. So the better way to go is to:

  1. define a class in CSS:
body.no-scrollbar {overflow:hidden;}
  1. apply/remove the class to body on #timeline:hover.
$(window).on('load', function() {
  $('#timeline').hover(
    function() {
      $('body').addClass('no-scrollbar');
    },
    function() {
      $('body').removeClass('no-scrollbar');
    }
  );
});

I used hover() which doesn't affect mobile devices. This should not affect the other script in any way.

Here's a quick demo:

$(window).on('load', () => $('#timeline').hover(
  () => $('body').addClass('no-scrollbar'),
  () => $('body').removeClass('no-scrollbar')
));
body.no-scrollbar {
  overflow: hidden;
}

/* rest is just styling for SO example. Disregard */
body {
  height: 200vh;
}

#timeline {
  border: 1px solid red;
  width: calc(100vw - 9rem);
  height: 3rem;
  margin: 3rem;
}
legend {padding: 0 .4rem;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<fieldset id="timeline">
  <legend>timeline</legend>
</fieldset>

Upvotes: 0

Related Questions