I am writing my own Drag'n'drop functionality for one of my projects and I am running into an issue. All my "draggable" elements are inside a container with display:flex. On mousedown event on one of this elements I set the position to absolute so I would be able to set the left and top properties of the element when I am dragging. Here is what I am doing:
let container = document.querySelector("#big-container")
var dragging = false;
var draggedObject;
let shiftX=0;
let shiftY=0;
document.querySelectorAll(".draggable").forEach((draggable,index) => {
draggable.style.order = index;
draggable.draggable =false;
draggable.ondragstart = ()=>{return false}
draggable.addEventListener("mousedown",ev =>{
draggedObject = draggable;
shiftX = ev.offsetX+5;
shiftY = ev.offsetY+5;
draggable.style.position = "absolute";
draggable.style.left = (ev.clientX - shiftX) + 'px';
draggable.style.top = (ev.clientY - shiftY) + 'px';
dragging = true;
let placeholder = document.createElement("div");
placeholder.id = "placeholder";
placeholder.style.order = draggable.style.order;
container.appendChild(placeholder);
})
})
document.addEventListener("mousemove", ev =>{
if(dragging){
draggedObject.style.left = ev.clientX - shiftX + 'px';
draggedObject.style.top = ev.clientY - shiftY + 'px';
}
})
document.addEventListener("mouseup",ev =>{
if(dragging){
draggedObject.style.position = 'static'
let placeholder = document.querySelector("#placeholder");
container.removeChild(placeholder);
dragging = false
}
})
/* the :not(:last-of-type(div)) is there so the console doesn't get affected */
*{
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: black;
}
.draggable {
width: 90px;
height: 120px;
margin: 5px;
}
#placeholder {
width: 90px;
height: 120px;
margin: 5px;
background-color: rgba(0, 0, 0, 0.3);
border: dashed grey 5px;
}
<body draggable="false" ondragstart="return false;">
<div id = "big-container" style ="display: flex; background-color: rgb(76, 104, 95); width: 500px; height: 500px;">
<div style="background-color: rgb(204, 125, 111);" class="draggable"></div>
<div style="background-color: rgb(170, 214, 120);" class="draggable"></div>
<div style="background-color: rgb(129, 212, 167);" class="draggable"></div>
<div style="background-color: rgb(162, 137, 196);" class="draggable"></div>
</div>
</body>
What I am trying to achieve is that on mousedown the element should stay where it was and after that when I move my mouse to move the element as well.(the anchor point should be where I clicked the element)
I am doing shiftX = ev.offsetX+5; because I need to account for the element's margin.
The issue is when I click on a element(and don't move my mouse at all), you can see a little shift in the element's position. It is very minor(maybe 1 or 2px) and is not happening in all places(some zones in the element do not introduce this position shift)
Do you guys have any idea what might be causing it?
You can use getBoundingClientRect() to get the actual position.
let container = document.querySelector("#big-container");
var dragging = false;
var draggedObject;
let shiftX = 0;
let shiftY = 0;
document.querySelectorAll(".draggable").forEach((draggable, index) => {
draggable.style.order = index;
draggable.draggable = false;
draggable.ondragstart = () => {
return false;
};
draggable.addEventListener("mousedown", (ev) => {
draggedObject = draggable;
var x = draggable.getBoundingClientRect().top - 5;
var y = draggable.getBoundingClientRect().left - 5;
shiftX = ev.offsetX + 5;
shiftY = ev.offsetY + 5;
draggable.style.position = "absolute";
draggable.style.left = y + "px";
draggable.style.top = x + "px";
dragging = true;
let placeholder = document.createElement("div");
placeholder.id = "placeholder";
placeholder.style.order = draggable.style.order;
container.appendChild(placeholder);
});
});
document.addEventListener("mousemove", (ev) => {
if (dragging) {
draggedObject.style.left = ev.clientX - shiftX + "px";
draggedObject.style.top = ev.clientY - shiftY + "px";
}
});
document.addEventListener("mouseup", (ev) => {
if (dragging) {
draggedObject.style.position = "static";
let placeholder = document.querySelector("#placeholder");
container.removeChild(placeholder);
dragging = false;
}
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: black;
}
#big-container {
width: 500px;
height: 500px;
}
.draggable {
width: 90px;
height: 120px;
margin: 5px;
}
#placeholder {
width: 90px;
height: 120px;
margin: 5px;
background-color: rgba(0, 0, 0, 0.3);
border: dashed grey 5px;
}
<body draggable="false" ondragstart="return false;">
<div
id="big-container"
style="display: flex; background-color: rgb(76, 104, 95);"
>
<div
style="background-color: rgb(204, 125, 111);"
class="draggable"
></div>
<div
style="background-color: rgb(170, 214, 120);"
class="draggable"
></div>
<div
style="background-color: rgb(129, 212, 167);"
class="draggable"
></div>
<div
style="background-color: rgb(162, 137, 196);"
class="draggable"
></div>
</div>
</body>
I need to make a carousel/slideshow in plain JavaScript mixed with CSS that slides through the images one by one loop seamlessly.
I can't seem to get any code working. I've tried several approaches but can't. It's got to be with z-index, and not making use of flex.
This is my third attempt at coding this. Can't seem to get the logic. It has to have navigation buttons to switch between images. Can someone help me out?
const getHeader = document.querySelector('.wp-custom-header');
const getImages = document.querySelectorAll('.wp-custom-header img');
const computeImages = function () {
getImages.forEach((img, index) => {
if (index > 0) img.classList.add('out');
});
};
computeImages();
let counter = 0;
let reinit = false;
// getHeader.classList.add('transformSlide');
const slideShowTimer = setInterval(() => {
if (counter > 0 && counter < getImages.length - 1) {
getImages[counter + 1].classList.add('transform-slide');
getImages[counter + 1].classList.add('onqueue-current');
getImages[counter + 1].classList.remove('out');
} else if (counter === 0 && reinit === false) {
getImages[counter + 1].classList.add('transform-slide');
getImages[counter + 1].classList.add('onqueue-current');
getImages[counter + 1].classList.remove('out');
} else if (counter === 0 && reinit === true) {
getImages[counter].classList.add('transform-slide');
getImages[counter].classList.add('onqueue-current');
getImages[counter].classList.remove('out');
getImages[getImages.length - 1].classList.add('out');
getImages[getImages.length - 1].classList.remove('transform-slide');
getImages[getImages.length - 1].classList.remove('onqueue-current');
}
counter++;
}, 2000);
getHeader.addEventListener('transitionend', () => {
if (counter >= 1) {
if (!reinit) {
getImages[counter - 1].classList.remove('transform-slide');
getImages[counter - 1].classList.remove('onqueue-current');
getImages[counter - 1].classList.add('out');
} else {
getImages[counter].classList.remove('transform-slide');
getImages[counter].classList.remove('onqueue-current');
getImages[counter].classList.add('out');
}
}
if (counter >= getImages.length - 1) {
console.log(counter);
counter = 0;
reinit = true;
}
});
This is the HTML
<div id="wp-custom-header" class="wp-custom-header">
<img alt="" src="./image01.svg" />
<img alt="" src="./image02.svg" />
<img alt="" src="./image03.svg" />
<img alt="" src="./image04.svg" />
<img alt="" src="./image05.svg" />
<img alt="" src="./image06.svg" />
</div>
The CSS
.wp-custom-header {
position: relative;
display: block;
width: 100%;
height: var(--header-size);
}
.wp-custom-header img {
position: absolute;
display: block;
min-width: 100%;
-o-object-fit: cover;
object-fit: cover;
transition: var(--slide-transform) ease-in-out;
}
.wp-custom-header img.out {
/* left: -450px; */
z-index: 0;
transform: translateX(100%);
}
.wp-custom-header img.onqueue-next {
z-index: 0;
left: 0px;
}
.wp-custom-header img.onqueue-current {
z-index: 1;
transform: translateX(0px);
}
.transform-slide {
transition: var(--slide-transform) ease-in-out;
}
I took the liberty to tinker with your code. I've reworked your code into smaller functions and added a looping mechanic. Now you have buttons that will loop infinitely no matter how many slides there are in your carousel.
I've added a previous and a next button. Hovering over the images will stop the autoslide functionality from running so that you can control going to the next and previous slide. Whenever you stop hovering the carousel continues.
Hope that this is what you were looking for.
const header = document.querySelector('.wp-custom-header');
const images = document.querySelectorAll('.wp-custom-header img');
const buttons = document.querySelectorAll('.wp-custom-header button');
let activeSlideIndex = 0;
let interval = null;
const updateCarousel = () => {
images.forEach((image, index) => {
if (index === activeSlideIndex) {
image.classList.add('active');
} else if (image.classList.contains('active')) {
image.classList.remove('active');
}
});
};
const nextSlide = () => {
if (activeSlideIndex + 1 < images.length) {
activeSlideIndex++;
} else {
activeSlideIndex = 0;
}
};
const prevSlide = () => {
if (activeSlideIndex - 1 >= 0) {
activeSlideIndex--;
} else {
activeSlideIndex = images.length - 1;
}
};
const startInterval = () => setInterval(() => {
nextSlide();
updateCarousel();
}, 2000);
const stopInterval = () => {
clearInterval(interval);
interval = null;
};
interval = startInterval();
const controls = {
'prev': prevSlide,
'next': nextSlide
};
header.addEventListener('mouseenter', () => {
if (interval !== null) {
stopInterval();
}
});
header.addEventListener('mouseleave', () => {
interval = startInterval();
});
buttons.forEach(button => {
button.addEventListener('click', event => {
const value = event.target.value;
const action = controls[value];
action();
updateCarousel();
});
});
.wp-custom-header {
position: relative;
}
.wp-custom-header-images {
display: block;
width: 100%;
height: 250px;
}
.wp-custom-header-images img {
position: absolute;
display: block;
width: 100%;
height: 100%;
-o-object-fit: cover;
object-fit: cover;
opacity: 0;
will-change: opacity;
transition: opacity 250ms ease-in-out;
z-index: 0;
}
.wp-custom-header-images img.active {
z-index: 1;
opacity: 1;
}
.wp-custom-header-button {
position: absolute;
top: 50%;
border: 1px solid #d0d0d0;
background-color: #f0f0f0;
border-radius: 50%;
width: 50px;
height: 50px;
cursor: pointer;
transform: translate(0, -50%);
z-index: 2;
}
.wp-custom-header-button[value="prev"] {
left: 15px;
}
.wp-custom-header-button[value="next"] {
right: 15px;
}
<div id="wp-custom-header" class="wp-custom-header">
<button class="wp-custom-header-button" value="prev">Prev</button>
<div class="wp-custom-header-images">
<img alt="" src="https://picsum.photos/seed/a/640/360" class="active" />
<img alt="" src="https://picsum.photos/seed/b/640/360" />
<img alt="" src="https://picsum.photos/seed/c/640/360" />
<img alt="" src="https://picsum.photos/seed/d/640/360" />
<img alt="" src="https://picsum.photos/seed/e/640/360" />
<img alt="" src="https://picsum.photos/seed/f/640/360" />
</div>
<button class="wp-custom-header-button" value="next">Next</button>
</div>
I want to show a picture, and some text. When user hovers on text, I want picture to show some other picture. I have other pictures and when user hovers on text I want the changed picture to be the currently visible picture on the viewport.
I have a div on the page, and I want another div to hover on top of the first. I got it working, but it breaks on scroll. I want the position: absolute style to stay and want to transform using translate. The reason I need to compute the bounds and translate, is the .onpage div changes so where I have to show it changes.
let $onpage = document.getElementById('onpage');
let $onhover = document.getElementById('onhover');
let $triggers = document.querySelectorAll('.trigger');
function fTranslate(pos) {
return el => {
el.style.transform = `translate(${pos[0]}px,${pos[1]}px)`;
};
}
function show() {
let bounds = $onpage.getBoundingClientRect();
let pos = [bounds.left, bounds.top];
fTranslate(pos)($onhover);
}
$triggers.forEach(_ => {
_.addEventListener('mouseover', _ => {
show();
});
});
.on-hover {
position: absolute;
top: 0;
left: 0;
}
.on-page {
width: 50vmin;
height: 50vmin;
background: red;
}
.on-hover {
width: 40vmin;
height: 40vmin;
background: #ccc;
}
.content {
height: 10000px;
}
#trigger1 {
margin-top: 10px;
}
#trigger2 {
margin-top: 100px;
}
<div class="wrap">
<div id='onpage' class="on-page"></div>
<span id="trigger1" class="trigger">Hover 1</span>
<p>Some content
<span id="trigger2" class="trigger">Hover 2</span>
</p>
<div id='onhover' class="on-hover"></div>
<div class="content"></div>
</div>
here you go you just had to calculate the scroll offset of the window
let $onpage = document.getElementById('onpage');
let $onhover = document.getElementById('onhover');
function fTranslate(pos) {
return el => {
el.style.transform = `translate(${pos[0]}px,${pos[1]}px)`;
};
}
let bounds = $onpage.getBoundingClientRect();
let pos = [bounds.left, bounds.top];
fTranslate(pos)($onhover);
document.addEventListener('scroll', () => {
let bounds = $onpage.getBoundingClientRect();
const scrollLeft =
window.pageXOffset || document.documentElement.scrollLeft;
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const offsetLeft = bounds.left + scrollLeft;
const offsetTop = bounds.top + scrollTop;
let pos = [offsetLeft, offsetTop];
fTranslate(pos)($onhover);
});
.on-hover {
position: absolute;
top: 0;
left: 0;
}
.on-page {
width: 50vmin;
height: 50vmin;
background: red;
}
.on-hover {
width: 40vmin;
height: 40vmin;
background: #ccc;
}
.content {
height: 10000px;
}
<div class="wrap">
<div id='onpage' class="on-page"></div>
<div id='onhover' class="on-hover"></div>
<div class="content"></div>
</div>
Please let me know if I misunderstood. If you are looking to get the grey square to remain fixed, you can change the values in your translate to be negative to give you a positive offset.
let $onpage = document.getElementById('onpage');
let $onhover = document.getElementById('onhover');
function fTranslate(pos) {
return el => {
el.style.transform = `translate(${-pos[0]}px,${-pos[1]}px)`;
};
}
let bounds = $onpage.getBoundingClientRect();
let pos = [bounds.left, bounds.top];
fTranslate(pos)($onhover);
document.addEventListener('scroll', () => {
let bounds = $onpage.getBoundingClientRect();
let pos = [bounds.left, bounds.top];
fTranslate(pos)($onhover);
});
.on-hover {
position: absolute;
top: 0;
left: 0;
}
.on-page {
width: 50vmin;
height: 50vmin;
background: red;
}
.on-hover {
width: 40vmin;
height: 40vmin;
background: #ccc;
}
.content {
height: 10000px;
}
<div class="wrap">
<div id='onpage' class="on-page"></div>
<div id='onhover' class="on-hover"></div>
<div class="content"></div>
</div>
On mobile, it's a common UI pattern to have a scrollable element inside a draggable element. When you reach the end of the scrollable element, you start dragging the outer element. E.g. in this GIF (https://media.giphy.com/media/9MJgBkoZfqA7jRdQop/giphy.gif), after scrolling to the top, if you continuing scrolling, it'll drag the subreddits menu.
I want to implement a similar pattern using JS/CSS. To do this, I need to detect if users continue scrolling after reaching the end. Is this possible? If so, is it possible to determine how much they scroll after reaching the end?
window.onscroll = function(element) {
if ((window.innerHeight + window.pageYOffset) >= document.body.offsetHeight) {
alert("you're at the bottom of the page");
}
};
Using element parameter to know the current exact x y where mouse is now at to calculate more and some how much was scrolled
Javascript: How to detect if browser window is scrolled to bottom?
If You need to keep track of the user activity after the bottom (or the top) of the page has been reached, beside the scroll event, You need to track the the wheel event. Moreover, on mobile, You need to track also touchstart and touchmove events.
Not all these events are normalized across browsers, so I did my own normalization function, which is more or less something like this:
var compulsivity = Math.log2(Math.max(scrollAmount, 0.01) * wheelAmount);
Below is a complete playground. You can test it in Chrome using the Mobile View of the Developer Tools, or in other browsers using the TouchEmulator.
function Tracker(page) {
this.page = page;
this.moveUp = 0;
this.moveDown = 0;
this.startTouches = {};
this.moveTouches = {};
this.lastScrollY = 0;
this.monitor = {};
this.startThreshold = 160;
this.moveThreshold = 10;
this.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
this.pullToRefresh = window.chrome || navigator.userAgent.match('CriOS');
this.amplitude = 16 / Math.log(2);
this.page.ownerDocument.addEventListener( 'onwheel' in document ? 'wheel' : 'onmousewheel' in document ? 'mousewheel' : 'DOMMouseScroll', this, { passive: true } );
/* The basic scroll event cannot be canceled, so it does not need to be set passive.*/
this.page.ownerDocument.addEventListener('scroll', this);
this.page.addEventListener('touchstart', this, { passive: true });
/* Maybe we need to cancel pullToRefresh */
this.page.addEventListener('touchmove', this, { passive: false });
return this;
}
Tracker.prototype.handleEvent = function (e) { /* handleEvent is built-in */
var winHeight = (this.iOS ? document.documentElement.clientHeight : window.innerHeight) | 0,
currScrollY = window.pageYOffset | 0,
amountScrollY = (this.lastScrollY - currScrollY) | 0,
elHeight = this.page.offsetHeight | 0,
elTop = -currScrollY, elBottom = winHeight - elHeight + currScrollY,
isTop = elTop >= 0, isBottom = elBottom >= 0;
switch (e.type) {
case 'wheel':
case 'onmousewheel':
case 'mousewheel':
case 'DOMMouseScroll':
var wheelDelta = e.wheelDelta ? e.wheelDelta : e.deltaY ? -e.deltaY : -e.detail,
wheelDir = (wheelDelta > 0) - (wheelDelta < 0),
wheelUp = wheelDir < 0, wheelDown = wheelDir > 0,
wheelAmount = 100 * wheelDir;
if (isTop && wheelDown) {
this.moveUp++;
this.moveDown = 0;
} else if (isBottom && wheelUp) {
this.moveUp = 0;
this.moveDown++;
} else {
this.moveUp = 0;
this.moveDown = 0;
}
var compulsivity = this.amplitude * Math.log(Math.max(this.moveUp, this.moveDown, 0.01) * wheelAmount* wheelDir);
this.monitor[e.type].track(wheelAmount, compulsivity);
break;
case 'scroll':
/* end of scroll event for iOS, start/end of scroll event for other browsers */
this.lastScrollY = currScrollY;
this.monitor[e.type].track(amountScrollY, 0);
break;
case 'touchstart':
var touches = [].slice.call(e.touches), i = touches.length;
while (i--) {
var touch = touches[i], id = touch.identifier;
this.startTouches[id] = touch;
this.moveTouches[id] = touch;
}
break;
case 'touchmove':
var touches = [].slice.call(e.touches), i = touches.length,
currTouches = {},
swipeUp = false, swipeDown = false,
currMoveY = 0, totalMoveY = 0;
while (i--) {
var touch = touches[i], id = touch.identifier;
currTouches[id] = touch;
if (id in this.moveTouches) {
currMoveY = this.moveTouches[id].screenY - touch.screenY;
}
if (id in this.startTouches) {
totalMoveY = this.startTouches[id].screenY - touch.screenY;
}
swipeUp = currMoveY > 0 || totalMoveY > 0;
swipeDown = currMoveY < 0 || totalMoveY < 0;
if (this.pullToRefresh && isTop && swipeDown && e.cancelable) {
e.preventDefault();
console.log('Reload prevented');
}
}
this.moveTouches = currTouches;
var moveDir = (totalMoveY > 0) - (totalMoveY < 0),
longSwipe = moveDir * totalMoveY > this.startThreshold,
shortSwipe = moveDir * totalMoveY > this.moveThreshold,
realSwipe = longSwipe || shortSwipe;
if (isTop && swipeDown) {
if (realSwipe) this.moveUp++;
this.moveDown = 0;
} else if (isBottom && swipeUp) {
this.moveUp = 0;
if (realSwipe) this.moveDown++;
} else {
this.moveUp = 0;
this.moveDown = 0;
}
var compulsivity = this.amplitude * Math.log(Math.max(this.moveUp, this.moveDown, 0.01) * moveDir * totalMoveY);
this.monitor[e.type].track(currMoveY, compulsivity);
break;
}
};
function Monitor(events) {
this.ctx = null;
this.cont = null;
this.events = events;
this.values = [];
this.average = 0;
this.lastDrawTime = 0;
this.inertiaDuration = 200;
return this;
}
Monitor.prototype.showOn = function (container) {
var cv = document.createElement('canvas');
this.ctx = cv.getContext('2d');
this.cont = document.getElementById(container);
cv.width = this.cont.offsetWidth;
cv.height = this.cont.offsetHeight;
cv.style.top = 0;
cv.style.left = 0;
cv.style.zIndex = -1;
cv.style.position = 'absolute';
cv.style.backgroundColor = '#000';
this.cont.appendChild(cv);
var self = this;
window.addEventListener('resize', function () {
var cv = self.ctx.canvas, cont = self.cont;
cv.width = cont.offsetWidth;
cv.height = cont.offsetHeight;
});
return this;
};
Monitor.prototype.track = function (value, average) {
this.average = average;
if (this.values.push(value) > this.ctx.canvas.width) this.values.shift();
if (value) this.lastDrawTime = new Date().getTime();
};
Monitor.prototype.draw = function () {
if (this.ctx) {
var cv = this.ctx.canvas, w = cv.width, h = cv.height;
var i = this.values.length, x = w | 0, y = (0.5 * h) | 0;
cv.style.backgroundColor = 'rgb(' + this.average + ', 0, 0)';
this.ctx.clearRect(0, 0, w, h);
this.ctx.strokeStyle = '#00ffff';
this.ctx.lineWidth = 1;
this.ctx.beginPath();
while (i--) {
x -= 4;
if (x < 0) break;
this.ctx.moveTo(x, y);
this.ctx.lineTo(x + 1, y);
this.ctx.lineTo(x + 1, y - this.values[i]);
}
this.ctx.stroke();
var elapsed = new Date().getTime() - this.lastDrawTime;
/* cool down */
this.average = this.average > 0 ? (this.average * 0.9) | 0 : 0;
if (elapsed > this.inertiaDuration) {
this.track(0, this.average);
}
}
var self = this;
setTimeout(function () {
self.draw();
}, 100);
};
Monitor.prototype.connectTo = function (tracker) {
var events = this.events.split(' '), i = events.length;
while (i--) {
tracker.monitor[events[i]] = this;
}
this.draw();
return this;
};
function loadSomeData(target) {
$.ajax({
url: 'https://jsonplaceholder.typicode.com/users',
method: 'GET',
crossDomain: true,
dataType: 'json',
success: function (users) {
var html = '', $ul = $(target).find('ul');
$.each(users, function (i, user) {
var item = '<li><a class="ui-alt-icon ui-nodisc-icon">';
item += '<h2>' + user.name + '</h2>';
item += '<p><strong>' + user.company.name + '</strong></p>';
item += '<p>' + user.address.zipcode + ', ' + user.address.city + '</p>';
item += '<p>' + user.phone + '</p>';
item += '<p>' + user.email + '</p>';
item += '<p class="ui-body-inherit ui-li-aside ui-li-count"><strong>' + user.id + '</strong></p>';
item += '</a></li>';
html += item;
});
$ul.append(html).listview('refresh');
},
});
}
$(document)
.on('pagecreate', '#page-list', function (e) {
$("[data-role='header'], [data-role='footer']").toolbar({ theme: 'a', position: 'fixed', tapToggle: false });
loadSomeData(e.target);
})
.on('pageshow', '#page-list', function (e, ui) {
var tracker = $.data(this, 'mobile-page', new Tracker(this));
new Monitor('touchstart touchmove').connectTo(tracker).showOn('header');
new Monitor('scroll wheel mousewheel DOMMouseScroll').connectTo(tracker).showOn('footer');
});
.ui-page {
touch-action: none;
}
h1, h2, h3, h4, h5, h6, p {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* JQM no frills */
.ui-btn,
.ui-title,
.ui-btn:hover,
.ui-btn:focus,
.ui-btn:active,
.ui-btn:visited {
text-shadow: none !important;
}
* {
-webkit-box-shadow: none !important;
-moz-box-shadow: none !important;
box-shadow: none !important;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Compulsivity</title>
<meta name="description" content="Compulsivity" />
<meta name="HandheldFriendly" content="True" />
<meta name="MobileOptimized" content="320" />
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, width=device-width, minimal-ui shrink-to-fit=no" />
<meta http-equiv="cleartype" content="on" />
<!-- Add to homescreen for Chrome on Android -->
<meta name="mobile-web-app-capable" content="yes" />
<!-- For iOS web apps. Delete if not needed. -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="Compulsivity" />
<link rel="stylesheet" href="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" />
<!--
<script type="application/javascript" src="lib/touch-emulator.js"></script>
<script> TouchEmulator(); </script>
-->
<script type="application/javascript" src="https://cdn.jsdelivr.net/npm/jquery#2.2.4/dist/jquery.min.js"></script>
<script type="application/javascript" src="https://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
<div id="header" data-role="header"><h4 style="color: #fff">Compulsivity</h4></div>
<div id="page-list" data-role="page">
<div data-role="content" role="main">
<ul data-role="listview" data-filter="true" data-inset="true"></ul>
</div>
</div>
<div id="footer" data-role="footer"><h4 style="color: #fff">Scroll</h4></div>
</body>
</html>
Among others, You need to be aware also of the pull-to-refresh and inertia (or momentum) of the smooth scroll behaviors.
Please, try to scroll or to swipe and look how the events are tracked: either the top bar or bottom bar will change color to display the user activity after reaching the bottom or the top respectively of the page.
JavaScript:
// get the button
var theBtn = document.getElementById('theBtn');
// get the box
var theBox = document.getElementById('theBox');
// add event to the button on click show/hide(toggle) the box
theBtn.addEventListener('click', () => {
theBox.classList.toggle('active');
});
// when scrolling on the box
theBox.onscroll = function(){
// get the top of the div
var theBoxTop = theBox.scrollTop;
if(theBoxTop <= 0){
// when it reaches 0 or less, hide the box. It'll toggle the class, since it's "show" will "hide"
theBox.classList.toggle('active');
}
};
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-size: 10px;
font-family: 'Arial', sans-serif;
height: 1500px;
}
html {
scroll-behavior: smooth;
}
ul {
list-style-type: none;
}
#theBox ul li {
border: 1px solid;
height: 100px;
}
#navbar-bottom {
height: 100px;
width: 100%;
background: rgb(90, 111, 143);
position: fixed;
bottom: 0;
left: 0;
right: 0;
box-shadow: 0 0 2px 2px rgba(90, 111, 143, 0.562);
display: flex;
justify-content: space-around;
align-items: center;
}
#theBox {
background-color: red;
height: 350px;
width: 100%;
position: fixed;
bottom: 0;
transform: translateY(100%);
transition: all 0.3s;
overflow-y: scroll;
}
#theBox.active{
transform: translateY(0);
}
.myBtns {
width: 50px;
height: 50px;
border-radius: 50%;
border: none;
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
cursor: pointer;
}
.myBtns span {
height: 3px;
width: 30px;
background-color: black;
margin: 3px 0;
}
<main role="main">
<div id="theBox">
<ul>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
</ul>
</div>
<div id="navbar-bottom">
<button class="myBtns"></button>
<button class="myBtns" id="theBtn">
<span></span>
<span></span>
<span></span>
</button>
<button class="myBtns"></button>
</div>
</main>
jQuery:
// add event to the button on click show/hide(toggle) the box
$('#theBtn').click(function(){
$('#theBox').toggleClass('active');
});
// when scrolling on the box
$('#theBox').scroll(function () {
// get the top of the div
var theBoxTop = $('#theBox').scrollTop();
// when it reaches 0 or less, hide the box. It'll toggle the class, since it's "show" will "hide"
if(theBoxTop <= 0){
$('#theBox').toggleClass('active');
}
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-size: 10px;
font-family: 'Arial', sans-serif;
height: 1500px;
}
html {
scroll-behavior: smooth;
}
ul {
list-style-type: none;
}
#theBox ul li {
border: 1px solid;
height: 100px;
}
#navbar-bottom {
height: 100px;
width: 100%;
background: rgb(90, 111, 143);
position: fixed;
bottom: 0;
left: 0;
right: 0;
box-shadow: 0 0 2px 2px rgba(90, 111, 143, 0.562);
display: flex;
justify-content: space-around;
align-items: center;
}
#theBox {
background-color: red;
height: 350px;
width: 100%;
position: fixed;
bottom: 0;
transform: translateY(100%);
transition: all 0.3s;
overflow-y: scroll;
}
#theBox.active{
transform: translateY(0);
}
.myBtns {
width: 50px;
height: 50px;
border-radius: 50%;
border: none;
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
cursor: pointer;
}
.myBtns span {
height: 3px;
width: 30px;
background-color: black;
margin: 3px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<main role="main">
<div id="theBox">
<ul>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
<li><p>Text</p></li>
</ul>
</div>
<div id="navbar-bottom">
<button class="myBtns"></button>
<button class="myBtns" id="theBtn">
<span></span>
<span></span>
<span></span>
</button>
<button class="myBtns"></button>
</div>
</main>
window.onscroll = function(ev) {
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
alert("you are at the bottom of the page");
}
};
Link to demo: http://jsfiddle.net/5xpoe4yg/
There are two solutions for this. One is for touch devices and second for devices using mouse.
Using Wheel event
If target is a mouse device, then we will use following method:
document.onwheel = event => ScrollAction(event);
For more info on wheel event, please visit this link.
Touch Devices
If target is a touch device then following method will be useful:
document.ontouchcancel = event => TouchInterrupt(event);
document.ontouchend = event => FingerRemoved(event);
document.ontouchmove = event => FingerDragged(event);
document.ontouchstart = event => FingerPlaced(event);
For more info on touch events, please visit this link.
I think your problem fully is solved by this solution.
Your specific question is solveable by listening to the wheel event, although the result is not terribly precise. The wheel event often fires before the scroll event so this example will sometimes log negative scroll value on the first scroll up from the bottom of the page:
const content = document.querySelector('.content');
for (let i = 0; i < 50; i++) {
const p = document.createElement('p');
p.textContent = 'Content';
content.append(p);
};
content.addEventListener('wheel', e => {
const atBottom = content.scrollHeight - content.scrollTop === content.clientHeight;
if (atBottom) console.log(e.deltaY);
});
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
height: 100vh;
width: 100%;
}
.content {
overflow-y: scroll;
height: 100%;
}
<div class="content"></div>
As others have suggested, a better approach for your use case might instead be to have an overlay which you can trigger on click/touch and then scroll into view. One issue you might run into is that deeply nested scroll on web browsers can get real ugly real fast, without resorting to pure JS solutions which also have their own performance issues.
This is a popup that, when clicked on, opens and enables you to scroll. When it gets to the top of the page, it's header sticks.
var navbar = document.querySelector('.navbar'),
navheader = document.querySelector('.navheader');
// Toggle navbar
navheader.addEventListener('click', e => {
navbar.classList.toggle('open');
if (!navbar.classList.contains('open')) {
navbar.style.overflow = 'hidden';
document.body.style.overflow = '';
navbar.scrollTop = 0;
stickTop = false;
navbar.classList.remove('sticky');
navbar.style.top = '';
navbar.style.transition = '.2s';
setTimeout(() => {
navbar.style.transition = '';
}, 200);
}
else {
navbar.style.overflow = 'overlay';
navbar.style.transition = '.2s';
setTimeout(() => {
navbar.style.transition = '';
}, 200);
}
})
var prevtop = 0;
var stickTop = false;
// Add scroll listener
navbar.addEventListener('scroll', e => {
// If navbar is open
if (navbar.classList.contains('open')) {
if (!stickTop) {
navbar.style.top = navbar.getBoundingClientRect().top - navbar.scrollTop + 'px';
}
if ((window.innerHeight - navbar.getBoundingClientRect().bottom) >= 0) {
document.body.style.overflow = 'hidden';
navbar.style.overflow = 'auto';
navbar.style.top = 0;
navbar.classList.add('sticky');
stickTop = true;
}
if (navbar.scrollTop == 0) {
navbar.classList.remove('open');
navbar.style.overflow = 'hidden';
document.body.style.overflow = '';
stickTop = false;
navbar.classList.remove('sticky');
navbar.style.top = '';
navbar.style.transition = '.2s';
setTimeout(() => {
navbar.style.transition = '';
}, 200);
}
}
})
body {
font-family: sans-serif;
}
.navbar {
position: fixed;
top: calc(100vh - 50px);
height: 100vh;
left: 0;
width: 100%;
overflow: hidden;
}
.navbar.open {
top: 50vh;
}
.navcontent {
background: black;
width: 100%;
color: white;
}
.navcontent p {
margin: 0;
}
.navheader {
height: 50px;
width: 100%;
background: lightblue;
cursor: pointer;
top: 0;
position: sticky;
display: flex;
justify-content: center;
z-index: 1;
}
.navheader::before {
width: 50px;
height: 3px;
margin-top: 10px;
background: white;
border-radius: 3px;
content: '';
}
<div class="navbar">
<div class="navheader"></div>
<div class="navcontent"><p>S</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>E</p></div>
</div>
<div class="content">
<p>S</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>E</p>
</div>
I'm using Responsive File Manager for a web file manager in my project.
It is working fine.
Problem
My problem is when I open the image, It shows the preview in lightbox modal.
How can I make it Image as an image gallery like navigating between Image?
<a class="tip-right preview" title="<?php echo trans('Preview')?>" data-url="<?php echo $src;?>" data-toggle="lightbox" href="#previewLightbox">
<i class=" icon-eye-open"></i>
</a>
JS
Here a piece code JS code which is in include.js file
r.on("click", ".preview", function() {
var e = jQuery(this);
return 0 == e.hasClass("disabled") && jQuery("#full-img").attr("src", decodeURIComponent(e.attr("data-url")))
HTML
<div id="previewLightbox" class="lightbox hide fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class='lightbox-content'>
<img id="full-img" src="">
</div>
</div>
Fiddle Here
From my experience there isn't much you can do in terms of editing their manager.
However, it is quite easy to make your own with some CSS and Javascript.
Fiddle
Learning Tutorial
HTML:
<div id="slider" class="slider">
<div class="wrapper">
<div id="items" class="items">
<span class="slide"><img src="image1"/></span>
<span class="slide"><img src="image2"/></span>
<span class="slide"><img src="image3"/></span>
</div>
</div>
<a id="prev" class="control prev"></a>
<a id="next" class="control next"></a>
</div>
CSS:
.slider {
width: 300px;
height: 200px;
}
.wrapper {
overflow: hidden;
position: relative;
background: #222;
z-index: 1;
}
#items {
width: 10000px;
position: relative;
top: 0;
left: -300px;
}
#items.shifting {
transition: left .2s ease-out;
}
.slide {
width: 300px;
height: 200px;
cursor: pointer;
float: left;
display: flex;
flex-direction: column;
justify-content: center;
transition: all 1s;
position: relative;
}
.control {
position: absolute;
top: 50%;
width: 40px;
height: 40px;
background: #fff;
border-radius: 20px;
margin-top: -20px;
box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.3);
z-index: 2;
}
.prev,
.next {
background-size: 22px;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
}
.prev {
background-image: url(ChevronLeft.png);
left: -20px;
}
.next {
background-image: url(ChevronRight-512.png);
right: -20px;
}
.prev:active,
.next:active {
transform: scale(0.8);
}
Javascript:
var slider = document.getElementById('slider'),
sliderItems = document.getElementById('items'),
prev = document.getElementById('prev'),
next = document.getElementById('next');
slide(slider, sliderItems, prev, next);
function slide(wrapper, items, prev, next) {
var posX1 = 0,
posX2 = 0,
posInitial,
posFinal,
threshold = 100,
slides = items.getElementsByClassName('slide'),
slidesLength = slides.length,
slideSize = items.getElementsByClassName('slide')[0].offsetWidth,
firstSlide = slides[0],
lastSlide = slides[slidesLength - 1],
cloneFirst = firstSlide.cloneNode(true),
cloneLast = lastSlide.cloneNode(true),
index = 0,
allowShift = true;
// Clone first and last slide
items.appendChild(cloneFirst);
items.insertBefore(cloneLast, firstSlide);
wrapper.classList.add('loaded');
// Mouse and Touch events
items.onmousedown = dragStart;
// Touch events
items.addEventListener('touchstart', dragStart);
items.addEventListener('touchend', dragEnd);
items.addEventListener('touchmove', dragAction);
// Click events
prev.addEventListener('click', function () { shiftSlide(-1) });
next.addEventListener('click', function () { shiftSlide(1) });
// Transition events
items.addEventListener('transitionend', checkIndex);
function dragStart (e) {
e = e || window.event;
e.preventDefault();
posInitial = items.offsetLeft;
if (e.type == 'touchstart') {
posX1 = e.touches[0].clientX;
} else {
posX1 = e.clientX;
document.onmouseup = dragEnd;
document.onmousemove = dragAction;
}
}
function dragAction (e) {
e = e || window.event;
if (e.type == 'touchmove') {
posX2 = posX1 - e.touches[0].clientX;
posX1 = e.touches[0].clientX;
} else {
posX2 = posX1 - e.clientX;
posX1 = e.clientX;
}
items.style.left = (items.offsetLeft - posX2) + "px";
}
function dragEnd (e) {
posFinal = items.offsetLeft;
if (posFinal - posInitial < -threshold) {
shiftSlide(1, 'drag');
} else if (posFinal - posInitial > threshold) {
shiftSlide(-1, 'drag');
} else {
items.style.left = (posInitial) + "px";
}
document.onmouseup = null;
document.onmousemove = null;
}
function shiftSlide(dir, action) {
items.classList.add('shifting');
if (allowShift) {
if (!action) { posInitial = items.offsetLeft; }
if (dir == 1) {
items.style.left = (posInitial - slideSize) + "px";
index++;
} else if (dir == -1) {
items.style.left = (posInitial + slideSize) + "px";
index--;
}
};
allowShift = false;
}
function checkIndex (){
items.classList.remove('shifting');
if (index == -1) {
items.style.left = -(slidesLength * slideSize) + "px";
index = slidesLength - 1;
}
if (index == slidesLength) {
items.style.left = -(1 * slideSize) + "px";
index = 0;
}
allowShift = true;
}
}