Scrollable horizontal row - Can I scroll it back with JS?

I have an NSPanel Pro with a simple dashboard, on which there is a paper-buttons-row that is sidescrollable after adding overflow-x: scroll to the styles section.

The most used buttons are on the far left, and it is not reasonable that users scroll it back after accessing buttons out of sight.

So is it possible to have something listen to if it is scrolled, wait a set amount of time, and then scroll it back? ChatGPT was extremely confident but none of that stuff worked. Here is the basic card, and then just buttons repeated…

type: custom:paper-buttons-row
styles:
  justify-content: start
  align-items: center
  flex-direction: row
  gap: 12px
  margin-top: 32px
  overflow-x: scroll
  scroll-snap-type: x mandatory
buttons:
  - name: ' '
    icon: false
    entity: switch.esphome_web_a4db24_esp_dyrabjalla
    ripple: none
    styles:
      button:
        background-image: url('/local/png/key.png')
        background-size: 100px
        background-position: center
        background-repeat: no-repeat
        display: flex
        min-width: 120px
        height: 120px
        border-radius: 34px
        background-color: rgba(0,98,98,1)

I was able to do this by adding a custom javascript resource with this script, it looks for the paper-buttons-row, and the flex-box within it, can probably be altered to work for other scrollable containers.

Video, 10 second delay before scrolling back

console.log("Scroll Back Script Loaded");

function findElementInShadowRoot(element, selector) {
    return element.shadowRoot ? element.shadowRoot.querySelector(selector) : null;
}

function onDocumentReady(callback) {
    document.readyState === 'complete' || document.readyState === 'interactive' ? callback() : document.addEventListener('DOMContentLoaded', callback);
}

onDocumentReady(() => {
    function findElementDeep(selector) {
        function findDeep(element) {
            if (!element) return null;
            let found = element.querySelector(selector);
            if (found) return found;
            if (element.shadowRoot) {
                found = element.shadowRoot.querySelector(selector);
                if (found) return found;
                for (const child of element.shadowRoot.children) {
                    found = findDeep(child);
                    if (found) return found;
                }
            }
            for (const child of element.children) {
                found = findDeep(child);
                if (found) return found;
            }
            return null;
        }
        return findDeep(document.body);
    }

    const intervalId = setInterval(() => {
        const paperButtonsRow = findElementDeep("paper-buttons-row");
        if (paperButtonsRow) {
            const flexBox = findElementInShadowRoot(paperButtonsRow, ".flex-box");
            if (flexBox) {
                clearInterval(intervalId);
                addScrollBackFunctionality(flexBox);
            } else {
                console.log("Looking for flex-box in shadow DOM...");
            }
        } else {
            console.log("Looking for paper-buttons-row...");
        }
    }, 1000);
});

function addScrollBackFunctionality(scrollContainer) {
    let scrollBackTimeout;

    const resetScrollBackTimeout = () => {
        clearTimeout(scrollBackTimeout);
        scrollBackTimeout = setTimeout(() => {
            console.log("Scrolling back to start");
            scrollContainer.scrollTo({ left: 0, behavior: 'smooth' });
        }, 10000); // 10 seconds of inactivity
    };

    scrollContainer.addEventListener('scroll', () => {
        if (scrollContainer.scrollLeft !== 0) {
            resetScrollBackTimeout();
        }
    });

    resetScrollBackTimeout(); // Initialize the timeout when the script runs
}
1 Like