JSでbodyのスクロール(tochmove)を止めてコンテンツ内のみスクロールできるようにする

ヘッダーメニューを開いている時に、 bodyのスクロールを無効化して、 ヘッダーメニュー内だけスクロールを有効化するときのメモ 僕はこの方法で無駄なbodyのスクロールも影響せず実装できているコードです。

const scrollLock = (scrollabletarget) => {
    const scrollControl = (event) => {
        if (
            scrollabletarget &&
            (event.target === scrollabletarget || scrollabletarget.contains(event.target))
        ) {
            event.stopPropagation();
        } else {
            event.preventDefault();
        }
    };

    const lockScroll = () => {
        document.body.style.overflow = 'hidden';
        document.addEventListener('touchmove', scrollControl, { passive: false });
    };

    const unlockScroll = () => {
        document.body.style.overflow = '';
        document.removeEventListener('touchmove', scrollControl, { passive: false });
    };

    return { lock: lockScroll, unlock: unlockScroll };
};

使い方

// 例ではheaderメニューを想定
const HeaderMenuEvent = () => {
    const HeaderMenuBtn = document.getElementById('header_nav_btn');
    const HeaderMenuBody = document.getElementById('header_nav_body');
    const HeaderMenuScrollableContent = HeaderMenuBody.querySelector('nav');
    let resizeTimer;

    const ScrollLockEvent = scrollLock(HeaderMenuScrollableContent);

    if (!HeaderMenuBtn || !HeaderMenuBody) { return false; }

    function handleTouchMove(event) {
        if (event.target === HeaderMenuBody) {
            event.stopPropagation()
        } else {
            event.preventDefault();
        }
    }

    HeaderMenuBtn.addEventListener('click', e => {
        e.preventDefault();
        HeaderMenuBtn.classList.toggle('active');
        HeaderMenuBody.classList.toggle('active');
        document.body.classList.toggle('header_nav_active');

        if (HeaderMenuBtn.classList.contains('active')) {
            ScrollLockEvent.lock();
        } else {
            ScrollLockEvent.unlock();
        }
    });

    window.addEventListener('resize', () => {
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(() => {
            if (window.innerWidth >= 1025) {
                HeaderMenuBtn.classList.remove('active');
                HeaderMenuBody.classList.remove('active');
            }
        }, 300);
    });
};

使い方解説

あらかじめ、開閉の動作をするJS内で

const ScrollLockEvent = scrollLock();
// または
const ScrollLockEvent = scrollLock(HeaderMenuScrollableContent);

を指定する。

この時の‘HeaderMenuScrollableContent‘は、ヘッダー内のスクロール可能なターゲットを指定しています。

指定をしていない場合はすべてのスクロール(touchmove)自体をロックするようにしています。

そして、開閉に合わせてそれぞれ

if (HeaderMenuBtn.classList.contains('active')) {
    ScrollLockEvent.lock();
} else {
    ScrollLockEvent.unlock();
}

と指定することでスクロールをロック・アンロックするようにしています。