skip to Main Content

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!

2

Answers


  1. Instead of scroll, you can replace the visible cards with next 4 cards. I have set different color to each card so you can see it in action. Basically, I am storing all the cards in an array, and appending 4 cards at a time according to the pressed button.

    const nextbtns = document.querySelectorAll('.next')
    const backbtns = document.querySelectorAll('.back')
    const boo = document.getElementsByClassName('box');
    const cent = document.getElementById('con');
    let tempr = [];
    let a = 0;
    let b = 4;
    
    function nextFour() {
      [...boo].forEach((a) => a.remove());
      tempr.slice(a, b).forEach((a) => {
        if (typeof(a) == 'object') {
          cent.appendChild(a)
        }
      });
    }
    
    
    for (const i in boo) {
      tempr.push(boo[i]);
    }
    
    [...boo].forEach((a) => a.remove());
    tempr.slice(a, b).forEach((a) => cent.appendChild(a));
    
    for (let nxt of nextbtns) {
      nxt.addEventListener("click", () => {
        if (b >= tempr.length) return;
        a = a + 4;
        b = b + 4;
        nextFour()
      })
    }
    
    for (let bck of backbtns) {
      bck.addEventListener("click", () => {
        if (a <= 0) return;
        a = a - 4;
        b = b - 4;
        nextFour();
      })
    }
    .row {
      width: 100%;
      height: 270px;
      position: relative;
      left: 0;
      margin-top: 20px;
      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;
      position: relative;
      left: 20;
      right: 10;
    }
    
    .box {
      margin: 1px;
      width: 24%;
      height: 180px;
      background-color: #171717;
      border-radius: 20px;
      display: inline-block;
      position: relative;
      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;
    }
    
    button:hover {
      background-color: red;
    }
    <html>
    
    <body>
      <link href="Home.css" rel="stylesheet">
      <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" style="background-color: red;">
            </div>
            <div class="box" style="background-color: blue;">
            </div>
            <div class="box" style="background-color: yellow;">
            </div>
            <div class="box" style="background-color: black;">
            </div>
            <div class="box" style="background-color: green;">
            </div>
            <div class="box" style="background-color: lightblue;">
            </div>
            <div class="box" style="background-color: lightgreen;">
            </div>
            <div class="box" style="background-color: gray;">
            </div>
            <div class="box" style="background-color: lightgreen;">
            </div>
    
        </div>
        </center>
      </div>
      </div>
      <script type="text/javascript" src="Home.js"></script>
    </body>
    
    </html>
    Login or Signup to reply.
  2. 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).

    <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" 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);
    };
    
    // the main function we are calling.
    const init = (isResizing, { nextbtns, backbtns, container, boxes }) => {
      // we will use it for pagination and recalculating the scroll left on resize
      const pages = Math.floor(container.childElementCount / BOXES_PER_VIEW);
    
      // calculating the box size.
      let boxSize = container.clientWidth / BOXES_PER_VIEW - SPACE_BETWEEN;
      let skip = boxSize + SPACE_BETWEEN;
    
      for (let box of boxes) {
        box.style.minWidth = `${boxSize}px`;
      }
      // when resizing i want only to recalculate the scroll left
      if (isResizing) {
        container.scrollLeft = container.clientWidth * getCurrentPage(container);
      }
      // if we are resizing the screen i dont want to add more and more event listeners.
      if (!isResizing) {
        for (let nxt of nextbtns) {
          nxt.addEventListener("click", () => {
            if (getCurrentPage(container) < pages) {
              handlePageChange(container, +1);
    
              container.scrollLeft += parseInt(skip * BOXES_PER_VIEW, 10);
            }
          });
        }
    
        for (let bck of backbtns) {
          bck.addEventListener("click", () => {
            if (getCurrentPage(container) > 0) {
              handlePageChange(container, -1);
    
              container.scrollLeft -= parseInt(skip * BOXES_PER_VIEW, 10);
            }
          });
        }
      }
    };
    
    function createRowSlider(row) {
    // getting child elements from the specific row and passing to init
      const nextbtns = row.querySelectorAll(".next");
      const backbtns = row.querySelectorAll(".back");
      const container = row.querySelector(".container");
      const boxes = row.querySelectorAll(".box");
      //calling init with resize false and the child elemnets
      init(false, { nextbtns, backbtns, container, boxes });
    
      // adding listener to the window to check resize event. and calling init with resize true and the child elements.
      window.addEventListener("resize", () =>
        init(true, { nextbtns, backbtns, container, boxes })
      );
    }
    
    // --USING THE LIB--
    
    // getting the 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.


    All together:

    // 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, container, boxes }) => {
      const pages = Math.floor(container.childElementCount / BOXES_PER_VIEW);
    
      let boxSize = container.clientWidth / BOXES_PER_VIEW - SPACE_BETWEEN;
      let skip = boxSize + 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 += parseInt(skip * BOXES_PER_VIEW, 10);
            }
          });
        }
    
        for (let bck of backbtns) {
          bck.addEventListener("click", () => {
            if (getCurrentPage(container) > 0) {
              handlePageChange(container, -1);
    
              container.scrollLeft -= parseInt(skip * BOXES_PER_VIEW, 10);
            }
          });
        }
      }
    };
    
    function createRowSlider(row) {
      const nextbtns = row.querySelectorAll(".next");
      const backbtns = row.querySelectorAll(".back");
      const container = row.querySelector(".container");
      const boxes = row.querySelectorAll(".box");
      init(false, { nextbtns, backbtns, container, boxes });
      window.addEventListener("resize", () =>
        init(true, { nextbtns, backbtns, container, boxes })
      );
    }
    
    // unique rows
    const uniqueRow = document.getElementById("uniqueRowId");
    const uniqueRow2 = document.getElementById("uniqueRowId2");
    // creating the row sliders
    createRowSlider(uniqueRow);
    createRowSlider(uniqueRow2);
    .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;
    }
    <html>
      <head>
        <link rel="stylesheet" href="./styles.css">
      </head>
      <body>
    <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" 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>
    
    <div class="row" id="uniqueRowId2">
      <a class="btns">
        <button type="button" class="back" >&#8249</button>
        <button type="button" class="next" >&#8250</button>
      </a>
      <div class="container" 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>
    <script src="./index.js"></script>
    
      </body>
    </html>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search