Safari 15.4 scrollBy bug

Demo of a Safari Bug

by dsnvs

HTML

<main>
        <h1>Issue: element.scrollBy is broken when using behavior: 'smooth'</h1>
        <p>It seems like the problem only happens when a <code>element.ScrollBy</code> with
            <code>behavior: 'smooth'</code> is used on an element with CSS attribute <code>overflow</code> (or
            <code>overflow-x</code>) set to <code>hidden</code></p>

        <h3>Confirmed on: </h3>
        <ul>
            <li>Safari for macOS Version 15.4 (17613.1.17.1.13)</li>
        </ul>

        <section class="broken">
            <h2>scrollBy when using the <code>behavior: 'smooth'</code> and <code>overflow: hidden</code></h2>
            <div class="scroller" id="smooth">
                <div class="scroll-item item-1"></div>
                <div class="scroll-item item-2"></div>
                <div class="scroll-item item-3"></div>
            </div>
            <div class="scroller-controls">
                <button data-action="prev" data-scroller="smooth" data-behavior="smooth">Prev</button>
                <button data-action="next" data-scroller="smooth" data-behavior="smooth">Next</button>
            </div>
        </section>

        <section>
            <h2>scrollBy when using the <code>behavior: 'smooth'</code> and <code>overflow: scroll</code></h2>
            <div class="scroller scroller-with-scrollbar" id="smooth-2">
                <div class="scroll-item item-1"></div>
                <div class="scroll-item item-2"></div>
                <div class="scroll-item item-3"></div>
            </div>
            <div class="scroller-controls">
                <button data-action="prev" data-scroller="smooth-2" data-behavior="smooth">Prev</button>
                <button data-action="next" data-scroller="smooth-2" data-behavior="smooth">Next</button>
            </div>
        </section>
        <section>
            <h2>scrollBy when using the <code>behavior: 'instant'</code> and <code>overflow: hidden</code></h2>
            <div class="scroller"...

CSS

body {
    font-family: 'Helvetica', 'Arial', sans-serif;
}


main {
    max-width: 800px;
    margin: 0px auto;
}

section {
    border-radius: 8px;
    padding-top: 5px;
}

section h2 {
    margin: 15px;
    text-align: center;
}

button {
    padding: 10px;
    border-radius: 5px;
}

footer {
    max-width: 800px;
    margin: 30px auto 50px auto;
    padding: 20px 10px;
    border-radius: 8px;
    background-color: rgb(239, 239, 239);
}

a, a:active, a:visited, a:hover {
    color: blue;
}

.broken {
    background-color: rgb(239, 239, 239);
}

.scroller {
    max-width: 780px;
    margin-left: auto;
    margin-right: auto;
    white-space: nowrap;
    overflow-x: hidden;
}

.scroller-with-scrollbar {
    overflow-x: scroll;
}

.scroll-item {
    display: inline-block;
    width: 500px;
    height: 300px;
    border-radius: 10px;
}

.item-1 {
    background-color: rgb(225, 225, 225);
}

.item-2 {
    background-color: rgb(110, 110, 110)
}

.item-3 {
    background-color: rgb(66, 66, 66)
}

.scroller-controls {
    text-align: center;
    margin-top: 10px;
}

JavaScript

let buttons = document.querySelectorAll('button');

buttons.forEach((button) => {
    button.addEventListener('click', (e) => {
        let scroller = document.getElementById(e.currentTarget.attributes['data-scroller'].value);
        let direction = e.currentTarget.attributes['data-action'].value == "next" ? 1 : -1;
        let behavior = e.currentTarget.attributes['data-behavior'].value;

       scrollElement(scroller, direction, behavior);
    })
})

function scrollElement (scroller, direction, behavior) {
    scroller.scrollBy({left: 800 * (direction), behavior: behavior})
}