JSFiddle - React, Tailwind, and code Playground

by Paul Leech

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="menu-wrapper">
  <ul class="menu">
    <li class="item">1</li>
    <li class="item">2</li>
    <li class="item">3</li>
    <li class="item">4</li>
    <li class="item">5</li>
    <li class="item">6</li>
    <li class="item">7</li>
    <li class="item">8</li>
    <li class="item">9</li>
    <li class="item">10</li>
    <li class="item">11</li>
    <li class="item">12</li>
    <li class="item">13</li>
  </ul>

  <div class="paddles">
    <button class="left-paddle paddle hidden"/>      
        <button class="right-paddle paddle">
          >
        </button>
  </div>

</div>

<div class="print" id="print-wrapper-size"><span></span> Wrapper / visible menu size</div>
<div class="print" id="print-menu-size"><span></span> Total menu size</div>
<div class="print" id="print-menu-invisible-size"><span></span> Invisible menu size</div>
<div class="print" id="print-menu-position"><span></span> Scroll position</div>

CSS

body {
    margin: 0;
}

* {
    padding: 0;
    margin: 0;
}

.menu-wrapper {
    position: relative;
    max-width: 100vw;
    width: 100vw;
    height: 100px;
    margin: 1em auto;
    border: 1px solid black;
    overflow-x: hidden;
    overflow-y: hidden;
}

.menu   {
    height: 120px;
    background: #f3f3f3;
    box-sizing: border-box;
    
    white-space: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    }
    
    .item {
        display: inline-block;
        width: 200px;
        height: 100%;
        outline: 1px dotted gray;
        padding: 1em;
        box-sizing: border-box;
    }
}

.paddles {
}
.paddle {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 2rem;
    opacity:0;
    transition:1.5s;
    cursor:pointer;
    background: rgba(240,240,240,0.2);
}
.paddle:hover {
  transition:0.5s;
  background: rgba(240,240,240,0.8);
}

.left-paddle {
    left: 0;
}
.right-paddle {
    right: 0;
}
.hidden {
    display: none;
}

.print {
    margin: auto;
    max-width: 500px;
  }
    
    span {
        display: inline-block;
        width: 100px;
    }
}

JavaScript

// duration of scroll animation
var scrollDuration = 300;
// paddles
var leftPaddle = document.getElementsByClassName('left-paddle');
var rightPaddle = document.getElementsByClassName('right-paddle');
// get items dimensions
var itemsLength = $('.item').length;
var itemSize = $('.item').outerWidth(true);
// get some relevant size for the paddle triggering point
var paddleMargin = 20;

// get wrapper width
var getMenuWrapperSize = function() {
  return $('.menu-wrapper').outerWidth();
}
var menuWrapperSize = getMenuWrapperSize();
// the wrapper is responsive
$(window).on('resize', function() {
  menuWrapperSize = getMenuWrapperSize();
});
// size of the visible part of the menu is equal as the wrapper size 
var menuVisibleSize = menuWrapperSize;

// get total width of all menu items
var getMenuSize = function() {
  return itemsLength * itemSize;
};
var menuSize = getMenuSize();
// get how much of menu is invisible
var menuInvisibleSize = menuSize - menuWrapperSize;

// get how much have we scrolled to the left
var getMenuPosition = function() {
  return $('.menu').scrollLeft();
};

// finally, what happens when we are actually scrolling the menu
$('.menu').on('scroll', function() {

  // get how much of menu is invisible
  menuInvisibleSize = menuSize - menuWrapperSize;
  // get how much have we scrolled so far
  var menuPosition = getMenuPosition();

  var menuEndOffset = menuInvisibleSize - paddleMargin;

  // show & hide the paddles 
  // depending on scroll position
  if (menuPosition <= paddleMargin) {
    $(leftPaddle).addClass('hidden');
    $(rightPaddle).removeClass('hidden');
  } else if (menuPosition < menuEndOffset) {
    // show both paddles in the middle
    $(leftPaddle).removeClass('hidden');
    $(rightPaddle).removeClass('hidden');
  } else if (menuPosition >= menuEndOffset) {
    $(leftPaddle).removeClass('hidden');
    $(rightPaddle).addClass('hidden');
  }

  // print important values
  $('#print-wrapper-size span').text(menuWrapperSize);
 ...