Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript - Draggable Gallery Slider

I'm using a function from W3Schools as a starter point to make a draggable gallery slider (.slider-all-items in my code). Few lines have been removed from the original, to prevent dragging vertically.

I'm trying to achieve something very similar to this jsfiddle example, a draggable slideshow, and:

  • always on full screen (.item { width: 100vw || 100%})
  • responsive even on window.resize
  • and without the infinite mode (check the jsfiddle above).

Added 3 slides, each slide is taking 100% of the window (responsive).

<div data-item="slider-full" class="slider-container">
    <div class="slider-all-items">
        <div class="slider-item slider-item1"></div>
        <div class="slider-item slider-item2"></div>
        <div class="slider-item slider-item3"></div>
    </div>
</div>

Now, I don't need the infinite mode, but the animation that toggles between the slides is a part of the Draggable Slider.
If you tried dragging the slides (the direction doesn't matter) in the jsfiddle example just a few px it won't get the (next or prev) slide and center it, in short the function is prevented.

To animate the left and right .items, I did something like this:

/* If the user is dragging to -1 (no slide), it'll set the slider `left` to *0* */

if (sliderLeft >= 0) {theSlider.style.left = "0px";}

The same for the last .item (slide):

/* If dragging greater than the last one, set to the last with animation */
if (sliderRight <= sliderWidth) {theSlider.style.left = on the last item}

Animation - Transition:
The property transition and it's value 0.5s ease is stored in a class named .shifting.

If any of both (the above conditions) is true, .shifting class will be added to the Slider, it'll transition: 0.5s (as mentioned above). And at the same time, a setTimeout() function, delay 0.5s will be executed, which will add and remove it. (The delay is to balance the time it takes to the transition to finish completely before removing the class). Important to mention that the class .shifting should be removed to keep the drag fast and smooth. In short: It'll have the class only in action (when the mouse is free).

.shifting{
    transition: all 0.5s ease;
} 

What I'm trying to achieve:

Make the slides draggable (full screen - responsive - without the infinite mode), and:

if the drag is only X(small number)px prevent it (as in jsfiddle - using .shifting with my code).

if more than X(greater than the x before)px, get the asked slide and center it.

Live (Similar) Example:

EDIT: You have to sign in to see it, sorry didn't notice and couldn't find another example

  • Fiverr - scroll down and check the banner (slider). Ignore the infinite mode and the navigation dots.

Things I tried:

  1. Using for and forEach loops to get the items but couldn't connect between them and the theSlider.style.left.

  2. Tried The jsfiddle example above with some changes (set to full screen) and it works but the problem is on window.resize it glitches and need to refresh the page the make it works as expected.

  3. Refreshing a specific content in the page and not the page itself (not reloading), by using JavaScript or jQuery didn't work.

When refreshing a content in the page didn't work, I had an idea using the jsfiddle mentioned above changed the width and height to 100% and 100vh, and on window.resize` get the current slide index, remove the slider, and append it again with the stored index, but it may glitch sometimes so decided to stick with my code, and the questions is:

How can I connect between the slides to make the slider works as required?

My Code:

// get the slider
var theSlider = document.querySelector(".slider-all-items");
// get the items in the slider
var sliderItem = document.querySelectorAll('.slider-item');

// variables saved for later
var sliderWidth;
var sliderRight;

// run the function
dragElement(theSlider);


function dragElement(theSlider) {
    var pos1 = 0, pos3 = 0;
    theSlider.onmousedown = dragMouseDown;

    function dragMouseDown(e) {
        e = e || window.event;
        e.preventDefault();
        // get the mouse cursor position at startup:
        pos3 = e.clientX;
        document.onmouseup = closeDragElement;
        // call a function whenever the cursor moves:
        document.onmousemove = elementDrag;
    }

    function elementDrag(e) {
        e = e || window.event;
        e.preventDefault();
        // calculate the new cursor position:
        pos1 = pos3 - e.clientX;
        pos3 = e.clientX;

        // set the element's new position:
        theSlider.style.left = (theSlider.offsetLeft - pos1) + "px";
    }
    
    function closeDragElement() {
        // add the class .shifting to the slider for every css change (transition)
        theSlider.classList.add("shifting");
        
        // get each item width
        sliderWidth = theSlider.getBoundingClientRect().width  / sliderItem.length;
        // get the right side position of the slider
        sliderRight = theSlider.getBoundingClientRect().right;
        // get the left side position of the slider
        sliderLeft = theSlider.getBoundingClientRect().left;

        if(sliderLeft >= 0){
            theSlider.style.left = "0px";
        }

        if(sliderRight <= sliderWidth){            
            theSlider.style.left =  -Math.abs((sliderWidth * sliderItem.length) - sliderWidth) + "px";
        }
        
        // delay 0.5s, then remove the class .shifting when finished checking and styling
        // .shifting {transition: all 0.5s ease;}
        setTimeout(() => {
            theSlider.classList.remove("shifting");
        }, 500);

        // stop moving when mouse button is released:
        document.onmouseup = null;
        document.onmousemove = null;
    }
}
*, *:before, *:after {
    margin: 0;
    padding: 0;
    -webkit-box-sizing: border-box; 
    box-sizing: border-box;
}

.slider-container {
    position: relative;
    height: 80vh;
    overflow-x: scroll;
    overflow-y: hidden;
}

.slider-container::-webkit-scrollbar {
    display: none !important;
}

.slider-all-items {
    position: absolute;
    display: inline-flex;    
}

.slider-item {
    width: calc(100vw - 0px);
    height: 80vh;
    cursor: grab;
    display: block;
}

.slider-item1 {
    background: red;
}

.slider-item2 {
    background-color: blue;
}

.slider-item3 {
    background-color: yellow;
}

.shifting{
    transition: all 0.5s ease;
}
<div data-item="slider-full" class="slider-container">
    <div class="slider-all-items">
        <div class="slider-item slider-item1"></div>
        <div class="slider-item slider-item2"></div>
        <div class="slider-item slider-item3"></div>
    </div>
</div>
like image 728
001 Avatar asked Nov 17 '25 13:11

001


1 Answers

I am not sure if i understand everything correctly, but maybe it helps you. Here is my code example for a full screen slider with your wished "shifting" class.

const slider = document.querySelector('.slider-container')
const items = document.querySelectorAll('.slider-item')

// active item index
let selectedIndex = 0

// move item positions depend on selected index
const applyItemPositions = () => {
  items.forEach((item, index) => {
    // add shifting class
    item.classList.add('shifting')
    item.style.left = `${ (index - selectedIndex) * 100 }%`
  })
  
  // remove shifting class after 500ms
  setTimeout(() => {
    items.forEach((item, index) => {
      item.classList.remove('shifting')
    })
  }, 500)
}

// initial positions
applyItemPositions()

// helpers
const decreaseSelectedIndex = () => {
  if (selectedIndex > 0) {
    selectedIndex -= 1
    applyItemPositions()
  }
}

const increaseSelectedIndex = () => {
  if (selectedIndex < items.length - 1) {
    selectedIndex += 1
    applyItemPositions()
  }
}

// swipe events

startPos = [0, 0]

// swipestart
slider.addEventListener('pointerdown', (e) => {
  startPos = [e.clientX, e.clientY]
})

// swipeend
slider.addEventListener('pointerup', (e) => {
  const endPos = [e.clientX, e.clientY]
  
  // threshold (min 20px swipe distance)
  if (Math.abs(endPos[0] - startPos[0]) < 20) {
    return
  }
  
  if (endPos[0] < startPos[0]) {
    increaseSelectedIndex()
  }
  
  if (endPos[0] > startPos[0]) {
    decreaseSelectedIndex()
  }
})
html, body {
  width: 100vw;
  height: 100vh;
  margin: 0;
  padding: 0;
}

.slider-container {
  position: relative;
  width: 100%;
  height: 100%;
  background: #eee;
  overflow: hidden;
}
  
.slider-item {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.slider-item1 {
  background: red;
}

.slider-item2 {
  background: blue;
}

.slider-item3 {
  background: yellow;
}

.shifting{
  transition: all 0.5s ease;
}
<div data-item="slider-full" class="slider-container">
  <div class="slider-item slider-item1"></div>
  <div class="slider-item slider-item2"></div>
  <div class="slider-item slider-item3"></div>
</div>
like image 63
Asobitu Avatar answered Nov 20 '25 04:11

Asobitu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!