Search code examples
javascriptscrollkeyboardoverlayarrow-keys

Prevent webpage scrolling with keyboard only if an element is displayed


On a webpage, certain keyboard keys will control the scrolling of the webpage such like spacebar, arrowup, arrowdown.

I intend to prevent the user from scrolling with these keys when an overlay element is displayed. This overlay element has a default of display: none;.

I understand the sentiment of "do not alter browser's behavior," but scrolling will still be allowed by scroll wheel and/or touchpad (and of course, the scrollbar).
When the overlay is displayed, these keys are only intended for controlling the overlay contents, and not the page scrolling.
Respectively, when the overlay is not displayed, the keys should resume their default behavior of scrolling the page.

Due to this, I'm thinking of using an EventListener, and I found one here:

window.addEventListener("keydown", function(e) {
    if([" ","ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].indexOf(e.code) > -1) {
        e.preventDefault();
    }
}, false);

Snippets of example code:

function launchOverlay() {
    overlay.style.display = "flex";

    window.addEventListener("keydown", function(e) {
        if([" ","ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].indexOf(e.code) > -1) {
            e.preventDefault();
        }
    }, false);
}

function closeOverlay() {
    overlay.style.display = "none";

    window.removeEventListener("keydown", function(e) {
        if([" ","ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].indexOf(e.code) > -1) {
            e.preventDefault();
        }
    }, false);
}

document.addEventListener('keydown', function(event) {
    if (overlay.style.display == "flex") {
        if (event.code === "Space") {
            space();
        } else if (event.code === "ArrowUp") {
            aUp();
        } else if (event.code === "ArrowDown") {
            aDown();
        } else if (event.code === "ArrowLeft") {
            aLeft();
        } else if (event.code === "ArrowRight") {
            aRight();
        } else if (event.code === "Escape") {
            closeOverlay();
        }
    }
});

I'm wondering how I can assign the EventListener only if the overlay element is showing. At the current, my arrow keys remain "hijacked" and will not function, after the overlay is closed. If I open the overlay again, it functions as I have assigned it.


Solution

  • Your problem is that you aren't actually removing the EventListener for the scroll prevention.

    To do that, you can do:

    function launchOverlay() {
        overlay.style.display = "flex";
        window.addEventListener("keydown", scroll);
    }
    
    function closeOverlay() {
        overlay.style.display = "none";
        window.removeEventListener("keydown", scroll);
    }
    
    function scroll(e) {
        if([" ","ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].indexOf(e.code) > -1) {
            e.preventDefault();
        }
    }
    

    But since you are reassigning these arrow keys while preventing the scrolling, you should have this block of code within one function, so you can add/remove it as an EventListener.

    When you turn on the overlay, include the EventListener to prevent the scrolling via addEventListener.
    Likewise, remove the EventListener when you turn off the overlay, removeEventListener.


    In this code, I have opted for a switch instead of the if-else you had.
    I also used key instead of code since e.code does not account for the user's keyboard layout and may return an unexpected value. Read more

    function launchOverlay() {
        overlay.style.display = "flex";
        window.addEventListener("keydown", keyboardNav);
    }
    
    function closeOverlay() {
        overlay.style.display = "none";
        window.removeEventListener("keydown", keyboardNav);
    }
    
    
    // Define EventListener toggle
    function keyboardNav(e) {
        // Prevent scrolling
        if ([" ", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].indexOf(e.key) > -1) {
            e.preventDefault();
        }
    
        // When overlay is on, what do the keys do?
        if (overlay.style.display == "flex") {
            switch (e.key) {
                case " ":
                    space();
                    break;
                case "ArrowUp":
                    aUp();
                    break;
                case "ArrowDown":
                    aDown();
                    break;
                case "ArrowLeft":
                    aLeft();
                    break;
                case "ArrowRight":
                    aRight();
                    break;
                case "Escape":
                    closeOverlay();
                    break;
                default:
                    break;
            }
        }
    }
    

    With this, everything in the keyboardNav() would only be used after running the launchOverlay(), and be "ignored" after closeOverlay().