skip to Main Content

I have a simple text slider, but I’m now solving the following problems there.

  1. I only want to display what fits in 300px when it loads. So in this case it displays "Some longer text, Text, Some text, Some.." but to me it displays part of another element. Is it possible to limit it to show only the complete items?
  2. Moving. If only the visible items are displayed, is it possible to make it move two fully visible items each time? It doesn’t matter which side. And it always ends with the last item at the end of the slider or the first one at the beginning. Is it possible to do this?

Here is my code.

var currentPosition = 0;

  function moveMenu(direction) {
    var menu = document.getElementById('menu');
    var menuWrapperWidth = document.querySelector('.menu-wrapper').offsetWidth;
    var totalMenuWidth = [...menu.children].reduce((acc, el) => acc + el.offsetWidth + 20, 0);
    var maxScroll = totalMenuWidth - menuWrapperWidth;
    var scrollStep = menuWrapperWidth / 2;

    if (direction === 'next' && currentPosition > -maxScroll) {
      currentPosition -= scrollStep;
      currentPosition = Math.max(currentPosition, -maxScroll);
    } else if (direction === 'prev' && currentPosition < 0) {
      currentPosition += scrollStep;
      currentPosition = Math.min(currentPosition, 0);
    }
    menu.style.transform = `translateX(${currentPosition}px)`;
  }

  document.getElementById('prevButton').addEventListener('click', function() {
    moveMenu('prev');
  });
  document.getElementById('nextButton').addEventListener('click', function() {
    moveMenu('next');
  });
.menu-wrapper {
  width: 300px;
  overflow: hidden;
}
.menu {
  display: flex;
  white-space: nowrap;
  transition: transform 0.5s ease;
}
.menu-item {
  margin-right: 20px;
  flex-shrink: 0;
}
<div class="menu-wrapper">
  <button id="prevButton">Prev</button>
  <div id="menu" class="menu">
    <span class="menu-item">Some longer text</span>
    <span class="menu-item">Text</span>
    <span class="menu-item">Some text</span>
    <span class="menu-item">Some text</span>
    <span class="menu-item">Longer text</span>
    <span class="menu-item">Some longer text</span>
    <span class="menu-item">Text</span>
    <span class="menu-item">Some text</span>
  </div>
  <button id="nextButton">Next</button>
</div>

2

Answers


  1. Is this what you want?

    <style>
      .menu-wrapper {
        width: 300px;
        overflow: hidden;
      }
      
      .menu {
        display: flex;
        white-space: nowrap;
        transition: transform .5s ease;
      }
      
      .menu-item {
        margin-right: 20px;
        flex-shrink: 0;
      }
    </style>
    <div class="menu-wrapper">
      <button id="prevButton" onclick="moveMenu('prev')">Prev</button>
      <div id="menu" class="menu">
        <span class="menu-item">Some longer text</span>
        <span class="menu-item">Text</span>
        <span class="menu-item">Some text</span>
        <span class="menu-item">Some text</span>
        <span class="menu-item">Longer text</span>
        <span class="menu-item">Some longer text</span>
        <span class="menu-item">Text</span>
        <span class="menu-item">Some text</span>
      </div>
      <button id="nextButton" onclick="moveMenu('next')">Next</button>
    </div>
    <script>
      var menu = document.getElementById('menu');
      const { children } = menu
      let currentIndex = 0
      const positionMap = [{position: 0}]
      let sum = 0
      let position = 0
      for (const index in children) {
        const menuItem = children[index]
        const {
          offsetWidth
        } = menuItem
        sum -= offsetWidth + 20
        if (sum < -300 || index === children.length - 1) {
          menuItem.style.opacity = 0
          positionMap[positionMap.length - 1].hideIndex = index
          position += sum + offsetWidth + 20
          positionMap.push({
            position: position
          })
          sum = -offsetWidth - 20
        }
      }
      function moveMenu(direction) {
        setOpacity(1)
        if (direction === 'next') {
          if (currentIndex >= (positionMap.length - 1)) return
          currentIndex++
        } else {
          if (currentIndex <= 0) return
          currentIndex--
        }
        setOpacity(0)
        menu.style.transform = `translateX(${positionMap[currentIndex].position}px)`;
      }
      function setOpacity(val) {
        const item = children[positionMap[currentIndex].hideIndex]
        if (item) item.style.opacity = val
      }
    </script>
    Login or Signup to reply.
  2. <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
      .menu-wrapper {
        width: 300px;
        overflow: hidden;
      }
      .menu {
        display: flex;
        white-space: nowrap;
        transition: transform 0.5s ease;
      }
      .menu-item {
        margin-right: 20px;
        flex-shrink: 0;
      }
    </style>
    </head>
    <body>
    
    <div class="menu-wrapper">
      <button id="prevButton">Prev</button>
      <div id="menu" class="menu">
        <span class="menu-item">Some longer text</span>
        <span class="menu-item">Text</span>
        <span class="menu-item">Some text</span>
        <span class="menu-item">Some text</span>
        <span class="menu-item">Longer text</span>
        <span class="menu-item">Some longer text</span>
        <span class="menu-item">Text</span>
        <span class="menu-item">Some text</span>
      </div>
      <button id="nextButton">Next</button>
    </div>
    
    <script>
        var currentPosition = 0;
        var allSpans = [];
        var visibleItemsIndices = [];
        var spansSize = [];
        var menuWrapperWidth = 300;
        var currentWidth = 0;
        var lastVisibleIndex = 0;
        
        function initializeMenu() {
          var menu = document.getElementById('menu');
          allSpans = Array.from(menu.children);
          allSpans.forEach(function(span) {
            spansSize.push(span.offsetWidth+20);
          });
          updateVisibleItems();
          renderVisibleItems();
        }
        
        function updateVisibleItems() {
          currentWidth = 0;
          visibleItemsIndices = [];
          allSpans.forEach((span, index) => {
            var itemWidth = span.offsetWidth + 20;
            if (currentWidth + itemWidth <= menuWrapperWidth) {
              visibleItemsIndices.push(index);
              currentWidth += itemWidth;
              lastVisibleIndex = index;
            }
          });
        }
        
        function renderVisibleItems() {
          var menu = document.getElementById('menu');
          menu.innerHTML = '';
          visibleItemsIndices.forEach(index => {
            menu.appendChild(allSpans[index].cloneNode(true));
          });
        }
        
        function checkAndMoveItems(direction) {
            if (direction === 'next') {
                for (let i = 0; i < 2; i++) {
                    if (lastVisibleIndex + 1 < allSpans.length) {
                        visibleItemsIndices.push(lastVisibleIndex + 1);
                        currentPosition -= spansSize[lastVisibleIndex + 1]-20;
                        currentWidth += spansSize[lastVisibleIndex + 1];
                        lastVisibleIndex++;
                    }
                }
            } else if (direction === 'prev') {
                for (let i = 0; i < 2; i++) {
                    if (visibleItemsIndices[0] > 0) {
                        visibleItemsIndices.unshift(visibleItemsIndices[0] - 1);
                        currentPosition += spansSize[visibleItemsIndices[0]]-20;
                        currentWidth += spansSize[visibleItemsIndices[0]];
                        lastVisibleIndex--;
                    }
                }
            }
        
            renderVisibleItems();
            animateSlide();
        
            setTimeout(() => {
                while (currentWidth > menuWrapperWidth && visibleItemsIndices.length > 0) {
                    if (direction === 'next') {
                        var removedIndex = visibleItemsIndices.shift();
                    } else {
                        var removedIndex = visibleItemsIndices.pop();
                    }
                    currentWidth -= spansSize[removedIndex];
                }
        
                renderVisibleItems();
        
                currentPosition = 0;
                var menu = document.getElementById('menu');
                menu.style.transition = 'none';
                menu.style.transform = `translateX(${currentPosition}px)`;
            }, 500);
        }
    
        
        function animateSlide() {
            var menu = document.getElementById('menu');
            menu.style.transition = 'transform 0.5s ease';
            menu.style.transform = `translateX(${currentPosition}px)`;
        }
        
        document.getElementById('prevButton').addEventListener('click', function() {
          checkAndMoveItems('prev');
        });
        document.getElementById('nextButton').addEventListener('click', function() {
          checkAndMoveItems('next');
        });
        
        window.onload = initializeMenu;
    </script>
    
    </body>
    </html>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search