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 pletely 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:
Using
for
andforEach
loops
to get theitems
but couldn't connect between them and thetheSlider.style.left
.Tried The jsfiddle example above with some changes (
set to full screen
) and it works but the problem is onwindow.resize
it glitches and need to refresh the page the make it works as expected.Refreshing a specific content in the page and not the page itself (not reloading), by using
JavaScript
orjQuery
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>
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 pletely 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:
Using
for
andforEach
loops
to get theitems
but couldn't connect between them and thetheSlider.style.left
.Tried The jsfiddle example above with some changes (
set to full screen
) and it works but the problem is onwindow.resize
it glitches and need to refresh the page the make it works as expected.Refreshing a specific content in the page and not the page itself (not reloading), by using
JavaScript
orjQuery
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>
Share
Improve this question
edited Aug 26, 2021 at 15:37
001
asked Jan 1, 2021 at 19:39
001001
2,0597 silver badges23 bronze badges
2
- could you please provide more specific details about the effect that you try to achive? – noam gaash Commented Jan 17, 2021 at 17:17
- 2 I would like to help but it is very unclear what you want to improve in the behaviour of the code. You specify that you are aiming for " responsive even on window.resize, always on full screen (items width: 100vw or 100%) and without the infinite mode.". However it already resizes on window resize, always goes 100% of the container width, and does not have infinite scroll. I do not see what is missing. Separately, I do not understand what you mean by refreshing specific content in part of the screen. How do you do that? – ProfDFrancis Commented Jan 17, 2021 at 22:27
3 Answers
Reset to default 1I 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>
Have you considered using CSS' scroll-snapping? This already natively does what you want (without any need for JS). The only thing you need to add yourself is the ability to drag, but only if you want to enable dragging on non-touch devices.
I've whipped up a quick demo, including dragging support (via JS), below:
//JS for dragging support - wrote this quickly, you could probably fine tune this.
//Only handles X movement (could be modified to do Y as well).
const ele = document.getElementById('container');
let startPos = 0;
const mouseMoveHandler = function(e) {
// How far the mouse has been moved
const dx = e.clientX - startPos;
// Scroll the element
ele.scrollLeft = ele.scrollLeft - dx;
// Store mousepos for next scroll
startPos = e.clientX;
};
//as soon as user interaction begins, start handling the movement
document.addEventListener('mousedown', (e) => {
//store the current cursor position as the starting position
startPos = e.clientX;
//add the dragging class which temporarily disables the snapping, or else the dragging (scrolling) doesn't work
ele.classList.add('dragging');
ele.addEventListener('mousemove', mouseMoveHandler);
});
//when interaction stops, stop handling the movement
document.addEventListener('mouseup', (e) => {
ele.classList.remove('dragging');
ele.removeEventListener('mousemove', mouseMoveHandler);
});
/* CSS based on codepen example from https://blog.logrocket./how-to-use-css-scroll-snap/ */
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: 'Helvetica', sans-serif;
}
.container {
cursor: grab;
width: 60vw;
height: 70vh;
margin: 15vh auto;
overflow-x: scroll;
scroll-snap-type: x mandatory;
color: white;
background-color: oldlace;
display: flex;
align-items: center;
}
.container.dragging {
scroll-snap-type: none;
user-select: none;
/* prevents accidental text selection */
}
.child {
margin-left: 0.5rem;
height: 90%;
scroll-snap-align: start;
padding: 1rem;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
.child:nth-child(1n) {
background-color: darkorchid;
flex: 0 0 80%;
}
.child:nth-child(2n) {
background-color: indigo;
flex: 0 0 60%;
}
.child:nth-child(3n) {
background-color: navy;
flex: 0 0 40%;
}
.child:nth-child(4n) {
background-color: palegreen;
flex: 0 0 50%;
}
.child:nth-child(5n) {
background-color: yellow;
flex: 0 0 80%;
}
.child:nth-child(6n) {
background-color: orange;
flex: 0 0 60%;
}
.child:nth-child(7n) {
background-color: tomato;
flex: 0 0 80%;
}
<main class="container" id="container">
<div class="child">
<h2>Image 1</h2>
</div>
<div class="child">
<h2>Image 2</h2>
</div>
<div class="child">
<h2>Image 3</h2>
</div>
<div class="child">
<h2>Image 4</h2>
</div>
<div class="child">
<h2>Image 5</h2>
</div>
<div class="child">
<h2>Image 6</h2>
</div>
<div class="child">
<h2>Image 7</h2>
</div>
</main>
I believe we can remove the glitch as well as achieving the div's(slider-item) of equal width as you have already used full screen = width 100%) without reloading.
By simply using ResizeObserver : I believe all we have to do is to call "closeDragElement" function on window resize as this is the only function that calculates the size of the sliders.
CDN for ResizeObserver.
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;
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() {
// 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";
}
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
function dragElement(theSlider) {
var pos1 = 0, pos3 = 0;
theSlider.onmousedown = dragMouseDown;
theSlider.addEventListener('resize', closeDragElement);
}
dragElement(theSlider);
/* global ResizeObserver */
const ro = new ResizeObserver(entries => {
for (let entry of entries) {
closeDragElement();
}
});
ro.observe(theSlider); //<-- NOTICE HERE
*,
*: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>
Before: https://www.dropbox./s/y0oy4rwnlj5la4g/before.gif?dl=0
After: https://www.dropbox./s/jsqba0ef61ibef1/after.gif?dl=0