Home > Software engineering >  Can these slides be looped?
Can these slides be looped?

Time:06-14

Just worked on a simple div slider with arrows. The code works just fine, except, when I navigate to the left or right, it stops at the last item.

Right now, when a user clicks the left arrow to the last div slide, it stops. Also, when a user clicks the right arrow to the last div slide, it stops.

What I am trying to achieve is when I navigate to the left or right, the slider should continue in a loop. That is, without ending at the last item.

For instance, when a user clicks the right arrow after reaching 8, the next number should be 1, click again 2 follows, click again 3 follows, and so on. Likewise, when a user clicks the left arrow after reaching 1, the next number should be 8, click again 7 follows, click again 6 follows, and so on.

let buttonLeft = document.getElementById('slide_left')
let buttonRight = document.getElementById('slide_right')

let container = document.getElementById('slider')

buttonLeft.addEventListener('click', function() {
  container.scrollLeft -= 90
})

buttonRight.addEventListener('click', function() {
  container.scrollLeft  = 90
})
body {
  background-color: #555;
  height: 100vh;
  display: grid;
  align-items: center;
  justify-items: center;
  font-family: 'Helvetica';
}

div#slide_wrapper {
  width: 440px;
  display: flex;
  justify-content: space-between;
  height: fit-content;
}

div#slider {
  width: 350px;
  display: flex;
  height: fit-content;
  flex-wrap: nowrap;
  overflow: hidden;
}

div.thumbnail {
  min-width: 80px;
  min-height: 80px;
  cursor: pointer;
  display: grid;
  place-items: center;
  font-size: 30px;
}

div.thumbnail:not(:last-child) {
  margin-right: 10px;
}

div.thumbnail:nth-child(1) {
  background-color: darkturquoise;
}

div.thumbnail:nth-child(2) {
  background-color: goldenrod;
}

div.thumbnail:nth-child(3) {
  background-color: rebeccapurple;
}

div.thumbnail:nth-child(4) {
  background-color: salmon;
}

div.thumbnail:nth-child(5) {
  background-color: lawngreen;
}

div.thumbnail:nth-child(6) {
  background-color: sienna;
}

div.thumbnail:nth-child(7) {
  background-color: bisque;
}

div.thumbnail:nth-child(8) {
  background-color: navy;
}

div#slide_wrapper>button {
  height: fit-content;
  align-self: center;
  font-size: 24px;
  font-weight: 800;
  border: none;
  outline: none;
}

div#slide_wrapper>button:hover {
  cursor: pointer;
}
<div id="slide_wrapper">
  <button id="slide_left" >&#10094;</button>
  <div id="slider">
    <div >1</div>
    <div >2</div>
    <div >3</div>
    <div >4</div>
    <div >5</div>
    <div >6</div>
    <div >7</div>
    <div >8</div>
  </div>
  <button id="slide_right" >&#10095;</button>
</div>

CodePudding user response:

It depends on exactly what effect you want, but for the simple case illustrated you can just remove the first (or last, depending on which direction you are going) element and put it to the end (or beginning) of the set of thumbnails.

That makes things a bit more responsive too as you don't have to fix the widths in px terms if you don't want to as they don't have to be remembered in order to move the elements.

const buttonLeft = document.getElementById('slide_left')
const buttonRight = document.getElementById('slide_right')

const container = document.getElementById('slider');

buttonLeft.addEventListener('click', function() {
  const last = document.querySelector('#slider > :last-child');
  last.remove();
  container.prepend(last);
})

buttonRight.addEventListener('click', function() {
  const first = document.querySelector('#slider > :first-child');
  first.remove();
  container.append(first);
})
body {
  background-color: #555;
  height: 100vh;
  display: grid;
  align-items: center;
  justify-items: center;
  font-family: 'Helvetica';
}

div#slide_wrapper {
  width: 440px;
  display: flex;
  justify-content: space-between;
  height: fit-content;
}

div#slider {
  width: 350px;
  display: flex;
  height: fit-content;
  flex-wrap: nowrap;
  overflow: hidden;
}

div.thumbnail {
  min-width: 80px;
  min-height: 80px;
  cursor: pointer;
  display: grid;
  place-items: center;
  font-size: 30px;
}

div.thumbnail:not(:last-child) {
  margin-right: 10px;
}

div.thumbnail.c1 {
  background-color: darkturquoise;
}

div.thumbnail.c2 {
  background-color: goldenrod;
}

div.thumbnail.c3 {
  background-color: rebeccapurple;
}

div.thumbnail.c4 {
  background-color: salmon;
}

div.thumbnail.c5 {
  background-color: lawngreen;
}

div.thumbnail.c6 {
  background-color: sienna;
}

div.thumbnail.c7 {
  background-color: bisque;
}

div.thumbnail.c8 {
  background-color: navy;
}

div#slide_wrapper>button {
  height: fit-content;
  align-self: center;
  font-size: 24px;
  font-weight: 800;
  border: none;
  outline: none;
}

div#slide_wrapper>button:hover {
  cursor: pointer;
}
<div id="slide_wrapper">
  <button id="slide_left" >&#10094;</button>
  <div id="slider">
    <div >1</div>
    <div >2</div>
    <div >3</div>
    <div >4</div>
    <div >5</div>
    <div >6</div>
    <div >7</div>
    <div >8</div>
  </div>
  <button id="slide_right" >&#10095;</button>
</div>

CodePudding user response:

I think it's overall a better way to render the data dynamically, only have 4 containers whose values change as you click on the left and right arrows, instead of having a long scrollable div. That becomes more difficult to maintain especially if you're trying to implement wrap-around feature.

So I changed the logic of how you render the data onto the screen. Instead of having as many divs in the container as there are data, hiding them and scrolling through the div as buttons are clicked, I made it so that there are always 4 (or as many as you want) containers and their values are changed as you press the buttons from a variable (and the containers stay in the same position and never move).

A good learning experience would be to change the shape of the data to something like:

const data = [
  {
    value: 1,
    color: goldenrod
  }, {
    value: 2,
    color: rebeccapurple
  }...
]

and get the colors to apply to the divs when you press the buttons inside render function by setting styles of an HTML element.

Hope this helps.

const data = [1, 2, 3, 4, 5, 6, 7, 8]
let curPos = 0;

let buttonLeft = document.getElementById('slide_left')
let buttonRight = document.getElementById('slide_right')

let container = document.getElementById('slider')

const render = (curPos) => {
  for (let i = 0; i < container.children.length; i  ) {
    container.children[i].innerText = data[wrapPos(curPos   i)];
  }
}

const calculatePos = (newPos) => {
  if (newPos >= data.length) {
    curPos = 0;
    return curPos;
  }
  if (newPos < 0) {
    curPos = data.length - 1;
    return curPos;
  }

  curPos = newPos;
  return curPos;
}

const wrapPos = (pos) => {
  if(pos >= data.length) return pos - data.length;
  return pos;
}

buttonLeft.addEventListener('click', function() {
  render(calculatePos(curPos - 1))
})

buttonRight.addEventListener('click', function() {
  render(calculatePos(curPos   1))
})

render(curPos);
body {
  background-color: #555;
  height: 100vh;
  display: grid;
  align-items: center;
  justify-items: center;
  font-family: 'Helvetica';
}

div#slide_wrapper {
  width: 440px;
  display: flex;
  justify-content: space-between;
  height: fit-content;
}

div#slider {
  width: 350px;
  display: flex;
  height: fit-content;
  flex-wrap: nowrap;
  overflow: hidden;
}

div.thumbnail {
  min-width: 80px;
  min-height: 80px;
  cursor: pointer;
  display: grid;
  place-items: center;
  font-size: 30px;
}

div.thumbnail:not(:last-child) {
  margin-right: 10px;
}

div.thumbnail:nth-child(1) {
  background-color: darkturquoise;
}

div.thumbnail:nth-child(2) {
  background-color: goldenrod;
}

div.thumbnail:nth-child(3) {
  background-color: rebeccapurple;
}

div.thumbnail:nth-child(4) {
  background-color: salmon;
}

div.thumbnail:nth-child(5) {
  background-color: lawngreen;
}

div.thumbnail:nth-child(6) {
  background-color: sienna;
}

div.thumbnail:nth-child(7) {
  background-color: bisque;
}

div.thumbnail:nth-child(8) {
  background-color: navy;
}

div#slide_wrapper>button {
  height: fit-content;
  align-self: center;
  font-size: 24px;
  font-weight: 800;
  border: none;
  outline: none;
}

div#slide_wrapper>button:hover {
  cursor: pointer;
}

.active {
  color: red;
}
<div id="slide_wrapper">
  <button id="slide_left" >&#10094;</button>
  <div id="slider">
    <div ></div>
    <div ></div>
    <div ></div>
    <div ></div>
  </div>
  <button id="slide_right" >&#10095;</button>
</div>

CodePudding user response:

You can try this approach with removeChild (remove the first/last elements) and insertAdjacentHTML (insert the last/first elements).

let buttonLeft = document.getElementById('slide_left');
let buttonRight = document.getElementById('slide_right');
let container = document.getElementById('slider');

let containerMaxLeft = container.scrollWidth - container.clientWidth;
buttonLeft.addEventListener('click', function() {
  let nextPos = container.scrollLeft - 90;
  if (nextPos < 0) {
     const lastChild = container.lastElementChild
    container.removeChild(lastChild)
    container.insertAdjacentHTML(
        "afterbegin",
        lastChild.outerHTML
    );
  } else {
    container.scrollLeft = nextPos;
  }
});

buttonRight.addEventListener('click', function() {
  let nextPos = container.scrollLeft   90;
  if (nextPos > containerMaxLeft) {
    const firstChild = container.firstElementChild
    container.removeChild(firstChild)
    container.insertAdjacentHTML(
        "beforeend",
        firstChild.outerHTML
    );
  } else {
    container.scrollLeft = nextPos;
  }
});
body {
  background-color: #555;
  height: 100vh;
  display: grid;
  align-items: center;
  justify-items: center;
  font-family: 'Helvetica';
}

div#slide_wrapper {
  width: 440px;
  display: flex;
  justify-content: space-between;
  height: fit-content;
}

div#slider {
  width: 350px;
  display: flex;
  height: fit-content;
  flex-wrap: nowrap;
  overflow: hidden;
}

div.thumbnail {
  min-width: 80px;
  min-height: 80px;
  cursor: pointer;
  display: grid;
  place-items: center;
  font-size: 30px;
}

div.thumbnail:not(:last-child) {
  margin-right: 10px;
}

div.thumbnail {
  background-color: darkturquoise;
}

div#slide_wrapper>button {
  height: fit-content;
  align-self: center;
  font-size: 24px;
  font-weight: 800;
  border: none;
  outline: none;
}

div#slide_wrapper>button:hover {
  cursor: pointer;
}
<div id="slide_wrapper">
  <button id="slide_left" >&#10094;</button>
  <div id="slider">
    <div >1</div>
    <div >2</div>
    <div >3</div>
    <div >4</div>
    <div >5</div>
    <div >6</div>
    <div >7</div>
    <div >8</div>
  </div>
  <button id="slide_right" >&#10095;</button>
</div>

Note: Colors are not applied correctly because the first element will become the last element, so the colors will be messed up with the changed element orders.

  • Related