Home > database >  How to make each current element in loop centered in container
How to make each current element in loop centered in container

Time:02-08

  1. As the container slides to the left, I want each tab to be centered for a brief moment in the container.
  2. Before the animation begins, I want the very first tab to be centered in the container on page load before the animation begins. I know I need to divide each element's offsetWidth by 2 so that it is calculated to be centered but I'm not sure where/how to do that.

Link to JS Fiddle: https://jsfiddle.net/nb5a92x3/

window.onload = function() {
    
    const tabsContainer = document.querySelector('.tabsContainer');
    const tabs = tabsContainer.querySelectorAll('.tab');

    function animate(target) {
        let siblings = [];
        let elm = target;
        let keyframes = [];
        let offset;
    
        // Push the widths of each previous element to an array
        while (elm = elm.previousElementSibling) {
            siblings.push(elm.offsetWidth);
        }
        
        // Add the sum of all previous elements
        offset = siblings.reduce((prev, current) => prev   current, 0);

        // Animate the container to the left
        setTimeout(function() {
            tabsContainer.style.left = `calc(50% - ${offset}px)`;
        }, i * 800);
    }
    
    // Run the code
    for (i = 0; i < tabs.length; i  ) {
        animate(tabs[i]);
    }
  }
body {
    background: #ccc;
}

section {
    text-align: center;
    width: 375px;
    background: white;
    margin: 0 auto;
    padding: 16px;
    position: relative;
    display: flex;
    overflow: hidden;
}

.tabsContainer {
    display: flex;
    position: relative;
    margin: 0;
    padding: 0;
    list-style: none;
    gap: 0 16px;
    transition: left 0.2s ease;

    .tab {
        border-radius: 25px;
        background: gray;
        text-align: center;
        height: 34px;
        display: inline-flex;
        align-items: center;
        padding: 0 16px;
    }
}
<section>
  <ul >
    <li >TAB ONE</li>
    <li >ANOTHER TAB HERE</li>
    <li >A REALLY LONG TAB HERE</li>
    <li >LAST TAB</li>
  </ul>
</section>

CodePudding user response:

If you set in CSS your container to margin-left: 50%
than all you need is x = EL_curr.offsetLeft EL_curr.offsetWidth / 2
and apply that x value to the slider-container CSS translateX (which is far more performant than animating the CSS left property, since transform can be hardware accelerated).

// Utility functions:

const EL  = (sel, par) => (par || document).querySelector(sel);
const ELS = (sel, par) => (par || document).querySelectorAll(sel);

// Tabs animator:

const tabsAnimator = (EL_slider) => {
  const ELS_items = ELS(".tab", EL_slider);
  const tot = ELS_items.length;
  let curr = 0;
  let tOut;

  const anim = () => {
    const EL_curr = ELS_items[curr];
    const x = EL_curr.offsetLeft   EL_curr.offsetWidth / 2;
    EL_slider.style.transform = `translateX(-${x}px)`;
    curr  = 1; // Increment counter
    if (curr > tot - 1) curr = 0; // Reset counter
    tOut = setTimeout(anim, 1500);
  };

  anim(); // Init!
};

ELS(".tabsContainer").forEach(tabsAnimator);
* {
  margin: 0;
  box-sizing: border-box;
}

.tabsWrapper {
  width: 375px;
  margin: 20px auto;
  padding: 16px 0;
  position: relative;
  overflow: hidden;
  background: #ccc;
  border-radius: 3em;
}

.tabsContainer {
  position: relative;
  padding: 0;
  display: inline-flex;
  list-style: none;
  gap: 0 15px;
  transition: transform 0.3s;
  margin-left: 50%;
}

.tabsContainer .tab {
  border-radius: 25px;
  background: gold;
  white-space: nowrap;
  padding: 5px 15px;
}
<section >
  <ul >
    <li >5 OFF FRIDAY</li>
    <li >10 OFFSUMMER</li>
    <li >SAVE MORE</li>
    <li >HALF OFF EVERYTHING</li>
  </ul>
</section>

<section >
  <ul >
    <li >JavaScript</li>
    <li >HTML</li>
    <li >CSS</li>
  </ul>
</section>

  • don't use horizontal paddings on the wrapper or container slider.
  • don't just use section as a selector. It's too common. Use rather a specific class like .tabsWrapper on the wrapper parent.

Additionally, if you'd like to pause the animation on "mouseenter", use clearTimeout(tOut); — and just anim() in the "mouseleave" Event.

  •  Tags:  
  • Related