Search code examples
javascriptloopsbuttonscrolloverflow

How to scroll every 4 div specifically, with side scrolling buttons?


How to scroll every 4 div specifically, with side scrolling buttons?

On a previous question I asked about how to loop and simplify codes of multiple side scrolling buttons in every overflow-x: How to loop and simplify code of multiple side scrolling buttons that scrolls specific row in javascript?

Now with this answered by Elna Haim, I am asking for help, to anyone knowledgeable in javascript on how is it possible to scroll every 4 div specifically and specifically stops and fits the 4 boxes in it when next and back button is clicked.

It can scroll using data-scroll="1280" but every time the screen size changes the scrolling gets broken too, and you will be able to see the div getting cut in half on the code below.

Also there's a problem in the margin not triggering in the code snippet, I don't know why.

const nextbtns = document.querySelectorAll('.next')
const backbtns = document.querySelectorAll('.back')

for (let nxt of nextbtns) {
    nxt.addEventListener("click", () => {
      const con = nxt.getAttribute("data-con");
      const target = nxt.getAttribute("data-scroll");
      document.querySelector(`#${con}`).scrollLeft += parseInt(target, 10);
    });
}

for (let bck of backbtns) {
    bck.addEventListener("click", () => {
      const con = bck.getAttribute("data-con");
      const target = bck.getAttribute("data-scroll");
      document.querySelector(`#${con}`).scrollLeft -= parseInt(target, 10);
    });
}
.row {
width: 100%;
height: 270px;
overflow-x: hidden;
-ms-overflow-style: none;
scrollbar-width: none;
}
.container {
overflow-x: hidden;
white-space: nowrap;
-ms-overflow-style: none;
scrollbar-width: none;
scroll-behavior: smooth;
transition: scroll-behavior .5s ease-in-out;
}
.box {
width: 24%;
height: 180px;
background-color: #171717;
border-radius: 20px;
display: inline-block;
margin-right: 14;
}

.btns button {
--color: #202020;
background-color: #202020;
padding: 8px 17.5px 12px 17.5px;
border: 3px solid var(--color);
color: grey;
border-radius: 50px;
font-family: 'Arial Black';
position: relative;
bottom: 0px;
}
<html>
  <body>
<div class="row">
  <a class="btns">
    <button type="button" class="back" data-con="con" data-scroll="1321">&#8249</button>
    <button type="button" class="next" data-con="con" data-scroll="1321">&#8250</button>
  </a>
  <div class="container" id="con">
    <center>
      <div class="box">
      </div><div class="box">
      </div><div class="box">
      </div><div class="box">
      </div><div class="box">
      </div><div class="box">
      </div><div class="box">
      </div><div class="box">
      </div>
    </center>
  </div>
</div>
  </body>
</html>

For the jsfiddle: https://jsfiddle.net/c0f9da82/ The margin is still not working I guess it would only work if codes are saved locally.

Thanks Alot to Elna Haim for answering the previous question and is looking to answer this question again! If anyone else can help answer the question it would be Greatly appreciated!

Also I have another question in: Anyone around Good at both Javascript and Youtube api?, I am using lite-youtube js and I am confused in adding an eventlistener for onStateChange Where I am using lite youtube js by paulirish on github, and asked how is it possible to create an event listener to get the onStateChange end parameter. Thank you very much in advance! to everyone looking to answer my questions!


Solution

  • One way to achieve it, is to convert your code to work as a mini library. and to make sure you are handling each row separtly (common way to work with sliders / swipers).

    in order to achieve it we need to modify the code a little:

    HTML: each row will be a "stand alone component", and now it will look like this: Very important each row must have a unique id and each container must have the default page attribute (this will define the page we show by default). *notice we need unique id in the row and in the container

    <div class="row" id="uniqueRowId">
      <a class="btns">
        <button type="button" class="back" >&#8249</button>
        <button type="button" class="next" >&#8250</button>
      </a>
      <div class="container" id="container1" page="0">
          <div class="box">1</div>
          <div class="box">2 </div>
          <div class="box">3 </div>
          <div class="box">4 </div>
          <div class="box">5 </div>
          <div class="box">6 </div>
          <div class="box">7 </div>
          <div class="box">8 </div>
          <div class="box">9 </div>
          <div class="box">10 </div>
          <div class="box">11 </div>
          <div class="box">12 </div>
          <div class="box">13 </div>
          <div class="box">14 </div>
          <div class="box">15 </div>
          <div class="box">16 </div>
      </div>
    </div>
    

    the css also need to be improved, we will use display flex, and gap in the container. and on the box we will apply min-width:

    .row {
      width: 100%;
      height: 270px;
      overflow-x: hidden;
      -ms-overflow-style: none;
      scrollbar-width: none;
    }
    .container {
      overflow-x: hidden;
      white-space: nowrap;
      -ms-overflow-style: none;
      scrollbar-width: none;
      scroll-behavior: smooth;
      transition: scroll-behavior 0.5s ease-in-out;
      width: 100%;
      display: flex;
      gap: 14px;
    }
    .box {
      min-width: calc(25% - 14px);
      height: 180px;
      background-color: #171717;
      border-radius: 20px;
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
    }
    
    .btns button {
      --color: #202020;
      background-color: #202020;
      padding: 8px 17.5px 12px 17.5px;
      border: 3px solid var(--color);
      color: grey;
      border-radius: 50px;
      font-family: "Arial Black";
      position: relative;
      bottom: 0px;
      cursor: pointer;
    }
    
    

    And now the js. its a bit different from what you have currently, but i built it on top of the exist code.

    // defaults.
    const BOXES_PER_VIEW = 4;
    const SPACE_BETWEEN = 14;
    
    // handler to change page (action will represnt +1 or -1).
    const handlePageChange = (container, action) => {
      const page = parseInt(container.getAttribute("page"), 10);
      container.setAttribute("page", page + action);
    
      return page + action;
    };
    
    // getting the current page from the container.
    const getCurrentPage = (container) => {
      const page = container.getAttribute("page");
      return parseInt(page, 10);
    };
    
    const init = (isResizing, { nextbtns, backbtns, containerInit, boxes }) => {
      const container = document.getElementById(containerInit.id);
      const pages = Math.floor(container.childElementCount / BOXES_PER_VIEW);
    
      let boxSize = container.clientWidth / BOXES_PER_VIEW - SPACE_BETWEEN;
    
      for (let box of boxes) {
        box.style.minWidth = `${boxSize}px`;
      }
    
      if (isResizing) {
        container.scrollLeft = container.clientWidth * getCurrentPage(container);
      }
      if (!isResizing) {
        for (let nxt of nextbtns) {
          nxt.addEventListener("click", () => {
            if (getCurrentPage(container) < pages) {
              handlePageChange(container, +1);
    
              container.scrollLeft += container.clientWidth;
            }
          });
        }
    
        for (let bck of backbtns) {
          bck.addEventListener("click", () => {
            if (getCurrentPage(container) > 0) {
              handlePageChange(container, -1);
    
              container.scrollLeft -= container.clientWidth;
            }
          });
        }
      }
    };
    
    function createRowSlider(row) {
      const nextbtns = row.querySelectorAll(".next");
      const backbtns = row.querySelectorAll(".back");
      const containerInit = row.querySelector(".container");
      const boxes = row.querySelectorAll(".box");
      init(false, { nextbtns, backbtns, containerInit, boxes });
      window.addEventListener("resize", () =>
        init(true, { nextbtns, backbtns, containerInit, boxes })
      );
    }
    
    // unique rows
    const uniqueRow = document.getElementById("uniqueRowId");
    const uniqueRow2 = document.getElementById("uniqueRowId2");
    // creating the row sliders
    createRowSlider(uniqueRow);
    createRowSlider(uniqueRow2);
    
    

    here is a working codesandbox: https://codesandbox.io/p/sandbox/gallant-payne-kmlzvm

    **NOTE: we dont implement any lazy load here. means the browser will load all the rows and all the content at once. might be bad for performace. you need to take it into concideration.

    This is a basic implementation of slider library, you can develop it more for your needs.