Button for smooth scrolling - javascript

I am doing a button for smooth scrolling to the bottom of the page. The button is working, but without the smooth animation. Does someone have an idea, what I am doing wrong? The same kind of code for button to top is working fine. I need to do this without CSS "smooth".
document.getElementById("buttondown").onclick = function() {
scrollTo(5555550, 2000);
}
// Element or Position to move + Time in ms (milliseconds)
function scrollTo(element, duration) {
var rootelement = document.documentElement;
if (rootelement.scrollTop === 0) {
var up = rootelement.scrollTop;
++rootelement.scrollTop;
rootelement = up + 1 === rootelement.scrollTop-- ? rootelement : document.body;
}
scrollToC(rootelement, rootelement.scrollTop, element, duration);
}
// Element to move, element or px from, element or px to, time in ms to animate
function scrollToC(element, from, to, duration) {
if (duration <= 0) return;
if (typeof from === "object") from = from.offsetTop;
if (typeof to === "object") to = to.offsetTop;
scrollToX(element, from, to, 0, 1 / duration, 20, easeOutCuaic);
}
function scrollToX(element, xFrom, xTo, t01, speed, step, motion) {
if (t01 < 0 || t01 > 1 || speed <= 0) {
element.scrollTop = xTo;
return;
}
element.scrollTop = xFrom - (xFrom - xTo) * motion(t01);
t01 += speed * step;
debugger;
setTimeout(function() {
scrollToX(element, xFrom, xTo, t01, speed, step, motion);
}, step);
}
function easeOutCuaic(t) {
t--;
return t * t * t + 1;
}

You can add smooth behaviour to the function:
document.getElementById("buttondown").onclick = function() {
window.scrollTo({
top: 100,
left: 100,
behavior: 'smooth'
});
}

Related

Image Carousel Codepen Images not loading

I have been trying to implement this codepen [https://codepen.io/LokeshNagarajan/pen/apRmZe?editors=1111] on my website but like in the codepen the images do not load even if I assign an https src and i believe the issue stems from the javascript but not sure exactly what to do to fix it.
(function ($) {
'use strict';
$.fn.waterwheelCarousel = function (startingOptions) {
// Adds support for intializing multiple carousels from the same selector group
if (this.length > 1) {
this.each(function () {
$(this).waterwheelCarousel(startingOptions);
});
return this; // allow chaining
}
var carousel = this;
var options = {};
var data = {};
function initializeCarouselData() {
data = {
itemsContainer: $(carousel),
totalItems: $(carousel).children().length,
containerWidth: $(carousel).width(),
containerHeight: $(carousel).height(),
currentCenterItem: null,
previousCenterItem: null,
items: [],
calculations: [],
carouselRotationsLeft: 0,
currentlyMoving: false,
itemsAnimating: 0,
currentSpeed: options.speed,
intervalTimer: null,
currentDirection: 'forward',
leftItemsCount: 0,
rightItemsCount: 0,
performingSetup: true
};
data.itemsContainer.children().removeClass(options.activeClassName);
}
/**
* This function will set the autoplay for the carousel to
* automatically rotate it given the time in the options
* Can clear the autoplay by passing in true
*/
function autoPlay(stop) {
// clear timer
clearTimeout(data.autoPlayTimer);
// as long as no stop command, and autoplay isn't zeroed...
if (!stop && options.autoPlay !== 0) {
// set timer...
data.autoPlayTimer = setTimeout(function () {
// to move the carousl in either direction...
if (options.autoPlay > 0) {
moveOnce('forward');
} else {
moveOnce('backward');
}
}, Math.abs(options.autoPlay));
}
}
/**
* This function will preload all the images in the carousel before
* calling the passed in callback function. This is only used so we can
* properly determine the width and height of the items. This is not needed
* if a user instead manually specifies that information.
*/
function preload(callback) {
var $imageElements = data.itemsContainer.find('img'),
totalImages = $imageElements.length,
loadedImages = 0;
if (options.preloadImages === false || $imageElements.length === 0) {
callback();
return;
}
$imageElements.each(function () {
$(this).bind('load', function () {
// Add to number of images loaded and see if they are all done yet
loadedImages += 1;
if (loadedImages === totalImages) {
// All done, perform callback
callback();
return;
}
});
// May need to manually reset the src to get the load event to fire
// http://stackoverflow.com/questions/7137737/ie9-problems-with-jquery-load-event-not-firing
$(this).attr('src', $(this).attr('src'));
// If browser has cached the images, it may not call trigger a load. Detect this and do it ourselves
if (this.complete) {
$(this).trigger('load');
}
});
}
/**
* Makes a record of the original width and height of all the items in the carousel.
* If we re-intialize the carousel, these values can be used to re-establish their
* original dimensions.
*/
function setOriginalItemDimensions() {
data.itemsContainer.children().each(function () {
if ($(this).data('original_width') == undefined || options.forcedImageWidth > 0) {
$(this).data('original_width', $(this).width());
}
if ($(this).data('original_height') == undefined || options.forcedImageHeight > 0) {
$(this).data('original_height', $(this).height());
}
});
}
/**
* Users can pass in a specific width and height that should be applied to every image.
* While this option can be used in conjunction with the image preloader, the intended
* use case is for when the preloader is turned off and the images don't have defined
* dimensions in CSS. The carousel needs dimensions one way or another to work properly.
*/
function forceImageDimensionsIfEnabled() {
if (options.forcedImageWidth && options.forcedImageHeight) {
data.itemsContainer.children().each(function () {
$(this).width(options.forcedImageWidth);
$(this).height(options.forcedImageHeight);
});
}
}
/**
* For each "visible" item slot (# of flanking items plus the middle),
* we pre-calculate all of the properties that the item should possess while
* occupying that slot. This saves us some time during the actual animation.
*/
function preCalculatePositionProperties() {
// The 0 index is the center item in the carousel
var $firstItem = data.itemsContainer.children().eq(0);
data.calculations[0] = {
distance: 0,
offset: 0,
opacity: 1
}
// Then, for each number of flanking items (plus one more, see below), we
// perform the calcations based on our user options
var horizonOffset = options.horizonOffset;
var separation = options.separation;
for (var i = 1; i <= options.flankingItems + 2; i++) {
if (i > 1) {
horizonOffset *= options.horizonOffsetMultiplier;
separation *= options.separationMultiplier;
}
data.calculations[i] = {
distance: data.calculations[i - 1].distance + separation,
offset: data.calculations[i - 1].offset + horizonOffset,
opacity: data.calculations[i - 1].opacity * options.opacityMultiplier
}
}
// We performed 1 extra set of calculations above so that the items that
// are moving out of sight (based on # of flanking items) gracefully animate there
// However, we need them to animate to hidden, so we set the opacity to 0 for
// that last item
if (options.edgeFadeEnabled) {
data.calculations[options.flankingItems + 1].opacity = 0;
} else {
data.calculations[options.flankingItems + 1] = {
distance: 0,
offset: 0,
opacity: 0
}
}
}
/**
* Here we prep the carousel and its items, like setting default CSS
* attributes. All items start in the middle position by default
* and will "fan out" from there during the first animation
*/
function setupCarousel() {
// Fill in a data array with jQuery objects of all the images
data.items = data.itemsContainer.children();
for (var i = 0; i < data.totalItems; i++) {
data.items[i] = $(data.items[i]);
}
// May need to set the horizon if it was set to auto
if (options.horizon === 0) {
if (options.orientation === 'horizontal') {
options.horizon = data.containerHeight / 2;
} else {
options.horizon = data.containerWidth / 2;
}
}
// Default all the items to the center position
data.itemsContainer
.css('position', 'relative')
.children()
.each(function () {
// Figure out where the top and left positions for center should be
var centerPosLeft, centerPosTop;
if (options.orientation === 'horizontal') {
centerPosLeft = (data.containerWidth / 2) - ($(this).data('original_width') / 2);
centerPosTop = options.horizon - ($(this).data('original_height') / 2);
} else {
centerPosLeft = options.horizon - ($(this).data('original_width') / 2);
centerPosTop = (data.containerHeight / 2) - ($(this).data('original_height') / 2);
}
$(this)
// Apply positioning and layering to the images
.css({
'left': centerPosLeft,
'top': centerPosTop,
'visibility': 'visible',
'position': 'absolute',
'z-index': 0,
'opacity': 0
})
// Give each image a data object so it remembers specific data about
// it's original form
.data({
top: centerPosTop,
left: centerPosLeft,
oldPosition: 0,
currentPosition: 0,
depth: 0,
opacity: 0
})
// The image has been setup... Now we can show it
.show();
});
}
/**
* All the items to the left and right of the center item need to be
* animated to their starting positions. This function will
* figure out what items go where and will animate them there
*/
function setupStarterRotation() {
options.startingItem = (options.startingItem === 0) ? Math.round(data.totalItems / 2) : options.startingItem;
data.rightItemsCount = Math.ceil((data.totalItems - 1) / 2);
data.leftItemsCount = Math.floor((data.totalItems - 1) / 2);
// We are in effect rotating the carousel, so we need to set that
data.carouselRotationsLeft = 1;
// Center item
moveItem(data.items[options.startingItem - 1], 0);
data.items[options.startingItem - 1].css('opacity', 1);
// All the items to the right of center
var itemIndex = options.startingItem - 1;
for (var pos = 1; pos <= data.rightItemsCount; pos++) {
(itemIndex < data.totalItems - 1) ? itemIndex += 1 : itemIndex = 0;
data.items[itemIndex].css('opacity', 1);
moveItem(data.items[itemIndex], pos);
}
// All items to left of center
var itemIndex = options.startingItem - 1;
for (var pos = -1; pos >= data.leftItemsCount * -1; pos--) {
(itemIndex > 0) ? itemIndex -= 1 : itemIndex = data.totalItems - 1;
data.items[itemIndex].css('opacity', 1);
moveItem(data.items[itemIndex], pos);
}
}
/**
* Given the item and position, this function will calculate the new data
* for the item. One the calculations are done, it will store that data in
* the items data object
*/
function performCalculations($item, newPosition) {
var newDistanceFromCenter = Math.abs(newPosition);
// Distance to the center
if (newDistanceFromCenter < options.flankingItems + 1) {
var calculations = data.calculations[newDistanceFromCenter];
} else {
var calculations = data.calculations[options.flankingItems + 1];
}
var distanceFactor = Math.pow(options.sizeMultiplier, newDistanceFromCenter)
var newWidth = distanceFactor * $item.data('original_width');
var newHeight = distanceFactor * $item.data('original_height');
var widthDifference = Math.abs($item.width() - newWidth);
var heightDifference = Math.abs($item.height() - newHeight);
var newOffset = calculations.offset
var newDistance = calculations.distance;
if (newPosition < 0) {
newDistance *= -1;
}
if (options.orientation == 'horizontal') {
var center = data.containerWidth / 2;
var newLeft = center + newDistance - (newWidth / 2);
var newTop = options.horizon - newOffset - (newHeight / 2);
} else {
var center = data.containerHeight / 2;
var newLeft = options.horizon - newOffset - (newWidth / 2);
var newTop = center + newDistance - (newHeight / 2);
}
newTop -= heightDifference / 2;
newLeft -= widthDifference / 2;
var newOpacity;
if (newPosition === 0) {
newOpacity = 1;
} else {
newOpacity = calculations.opacity;
}
// Depth will be reverse distance from center
var newDepth = options.flankingItems + 2 - newDistanceFromCenter;
$item.data('width', newWidth);
$item.data('height', newHeight);
$item.data('top', newTop);
$item.data('left', newLeft);
$item.data('oldPosition', $item.data('currentPosition'));
$item.data('depth', newDepth);
$item.data('opacity', newOpacity);
$item.data('distanceFactor', distanceFactor);
}
function moveItem($item, newPosition) {
// Only want to physically move the item if it is within the boundaries
// or in the first position just outside either boundary
if (Math.abs(newPosition) <= options.flankingItems + 1) {
performCalculations($item, newPosition);
data.itemsAnimating++;
$item
.css('z-index', $item.data().depth)
// Animate the items to their new position values
.animate({
'left': $item.data().left,
'top': $item.data().top,
'opacity': $item.data().opacity
}, data.currentSpeed, options.animationEasing, function () {
// Animation for the item has completed, call method
itemAnimationComplete($item, newPosition);
});
TweenMax.to($item, data.currentSpeed / 1000, {
scaleX: $item.data('distanceFactor'),
scaleY: $item.data('distanceFactor'),
ease: options.animationEasing
});
} else {
$item.data('currentPosition', newPosition)
// Move the item to the 'hidden' position if hasn't been moved yet
// This is for the intitial setup
if ($item.data('oldPosition') === 0) {
$item
.css({
'left': $item.data().left,
'top': $item.data().top,
'opacity': $item.data().opacity,
'z-index': $item.data().depth
});
TweenMax.to($item, 0, {
scaleX: $item.data('distanceFactor'),
scaleY: $item.data('distanceFactor')
});
}
}
}
/**
* This function is called once an item has finished animating to its
* given position. Several different statements are executed here, such as
* dealing with the animation queue
*/
function itemAnimationComplete($item, newPosition) {
data.itemsAnimating--;
$item.data('currentPosition', newPosition);
// Keep track of what items came and left the center position,
// so we can fire callbacks when all the rotations are completed
if (newPosition === 0) {
data.currentCenterItem = $item;
}
// all items have finished their rotation, lets clean up
if (data.itemsAnimating === 0) {
data.carouselRotationsLeft -= 1;
data.currentlyMoving = false;
// If there are still rotations left in the queue, rotate the carousel again
// we pass in zero because we don't want to add any additional rotations
if (data.carouselRotationsLeft > 0) {
rotateCarousel(0);
// Otherwise there are no more rotations and...
} else {
// Reset the speed of the carousel to original
data.currentSpeed = options.speed;
data.currentCenterItem.addClass(options.activeClassName);
if (data.performingSetup === false) {
options.movedToCenter(data.currentCenterItem);
options.movedFromCenter(data.previousCenterItem);
}
data.performingSetup = false;
// reset & initate the autoPlay
autoPlay();
}
}
}
/**
* Function called to rotate the carousel the given number of rotations
* in the given direciton. Will check to make sure the carousel should
* be able to move, and then adjust speed and move items
*/
function rotateCarousel(rotations) {
// Check to see that a rotation is allowed
if (data.currentlyMoving === false) {
// Remove active class from the center item while we rotate
data.currentCenterItem.removeClass(options.activeClassName);
data.currentlyMoving = true;
data.itemsAnimating = 0;
data.carouselRotationsLeft += rotations;
if (options.quickerForFurther === true) {
// Figure out how fast the carousel should rotate
if (rotations > 1) {
data.currentSpeed = options.speed / rotations;
}
// Assure the speed is above the minimum to avoid weird results
data.currentSpeed = (data.currentSpeed < 100) ? 100 : data.currentSpeed;
}
// Iterate thru each item and move it
for (var i = 0; i < data.totalItems; i++) {
var $item = $(data.items[i]);
var currentPosition = $item.data('currentPosition');
var newPosition;
if (data.currentDirection == 'forward') {
newPosition = currentPosition - 1;
} else {
newPosition = currentPosition + 1;
}
// We keep both sides as even as possible to allow circular rotation to work.
// We will "wrap" the item arround to the other side by negating its current position
var flankingAllowance = (newPosition > 0) ? data.rightItemsCount : data.leftItemsCount;
if (Math.abs(newPosition) > flankingAllowance) {
newPosition = currentPosition * -1;
// If there's an uneven number of "flanking" items, we need to compenstate for that
// when we have an item switch sides. The right side will always have 1 more in that case
if (data.totalItems % 2 == 0) {
newPosition += 1;
}
}
moveItem($item, newPosition);
}
}
}
/**
* The event handler when an image within the carousel is clicked
* This function will rotate the carousel the correct number of rotations
* to get the clicked item to the center, or will fire the custom event
* the user passed in if the center item is clicked
*/
$(this).children().bind("click", function () {
var itemPosition = $(this).data().currentPosition;
if (options.imageNav == false) {
return;
}
// Don't allow hidden items to be clicked
if (Math.abs(itemPosition) >= options.flankingItems + 1) {
return;
}
// Do nothing if the carousel is already moving
if (data.currentlyMoving) {
return;
}
data.previousCenterItem = data.currentCenterItem;
// Remove autoplay
autoPlay(true);
options.autoPlay = 0;
var rotations = Math.abs(itemPosition);
if (itemPosition == 0) {
options.clickedCenter($(this));
} else {
// Fire the 'moving' callbacks
options.movingFromCenter(data.currentCenterItem);
options.movingToCenter($(this));
if (itemPosition < 0) {
data.currentDirection = 'backward';
rotateCarousel(rotations);
} else if (itemPosition > 0) {
data.currentDirection = 'forward';
rotateCarousel(rotations);
}
}
});
function nextItemFromCenter() {
var $next = data.currentCenterItem.next();
if ($next.length <= 0) {
$next = data.currentCenterItem.parent().children().first();
}
return $next;
}
function prevItemFromCenter() {
var $prev = data.currentCenterItem.prev();
if ($prev.length <= 0) {
$prev = data.currentCenterItem.parent().children().last();
}
return $prev;
}
/**
* Intiate a move of the carousel in either direction. Takes care of firing
* the 'moving' callbacks
*/
function moveOnce(direction) {
if (data.currentlyMoving === false) {
data.previousCenterItem = data.currentCenterItem;
options.movingFromCenter(data.currentCenterItem);
if (direction == 'backward') {
options.movingToCenter(prevItemFromCenter());
data.currentDirection = 'backward';
} else if (direction == 'forward') {
options.movingToCenter(nextItemFromCenter());
data.currentDirection = 'forward';
}
}
rotateCarousel(1);
}
/**
* Navigation with arrow keys
*/
$(document).keydown(function (e) {
if (options.keyboardNav) {
// arrow left or up
if ((e.which === 37 && options.orientation == 'horizontal') || (e.which === 38 && options.orientation == 'vertical')) {
autoPlay(true);
options.autoPlay = 0;
moveOnce('backward');
// arrow right or down
} else if ((e.which === 39 && options.orientation == 'horizontal') || (e.which === 40 && options.orientation == 'vertical')) {
autoPlay(true);
options.autoPlay = 0;
moveOnce('forward');
}
// should we override the normal functionality for the arrow keys?
if (options.keyboardNavOverride && (
(options.orientation == 'horizontal' && (e.which === 37 || e.which === 39)) ||
(options.orientation == 'vertical' && (e.which === 38 || e.which === 40))
)) {
e.preventDefault();
return false;
}
}
});
/**
* Public API methods
*/
this.reload = function (newOptions) {
if (typeof newOptions === "object") {
var combineDefaultWith = newOptions;
} else {
var combineDefaultWith = {};
}
options = $.extend({}, $.fn.waterwheelCarousel.defaults, newOptions);
initializeCarouselData();
data.itemsContainer.children().hide();
forceImageDimensionsIfEnabled();
preload(function () {
setOriginalItemDimensions();
preCalculatePositionProperties();
setupCarousel();
setupStarterRotation();
});
}
this.next = function () {
autoPlay(true);
options.autoPlay = 0;
moveOnce('forward');
}
this.prev = function () {
autoPlay(true);
options.autoPlay = 0;
moveOnce('backward');
}
this.reload(startingOptions);
return this;
};
$.fn.waterwheelCarousel.defaults = {
// number tweeks to change apperance
startingItem: 1, // item to place in the center of the carousel. Set to 0 for auto
separation: 175, // distance between items in carousel
separationMultiplier: 0.6, // multipled by separation distance to increase/decrease distance for each additional item
horizonOffset: 0, // offset each item from the "horizon" by this amount (causes arching)
horizonOffsetMultiplier: 1, // multipled by horizon offset to increase/decrease offset for each additional item
sizeMultiplier: 0.7, // determines how drastically the size of each item changes
opacityMultiplier: 0.8, // determines how drastically the opacity of each item changes
horizon: 0, // how "far in" the horizontal/vertical horizon should be set from the container wall. 0 for auto
flankingItems: 3, // the number of items visible on either side of the center
// animation
speed: 300, // speed in milliseconds it will take to rotate from one to the next
animationEasing: 'linear', // the easing effect to use when animating
quickerForFurther: true, // set to true to make animations faster when clicking an item that is far away from the center
edgeFadeEnabled: false, // when true, items fade off into nothingness when reaching the edge. false to have them move behind the center image
// misc
linkHandling: 2, // 1 to disable all (used for facebox), 2 to disable all but center (to link images out)
autoPlay: 0, // indicate the speed in milliseconds to wait before autorotating. 0 to turn off. Can be negative
orientation: 'horizontal', // indicate if the carousel should be 'horizontal' or 'vertical'
activeClassName: 'carousel-center', // the name of the class given to the current item in the center
keyboardNav: false, // set to true to move the carousel with the arrow keys
keyboardNavOverride: true, // set to true to override the normal functionality of the arrow keys (prevents scrolling)
imageNav: true, // clicking a non-center image will rotate that image to the center
// preloader
preloadImages: true, // disable/enable the image preloader.
forcedImageWidth: 0, // specify width of all images; otherwise the carousel tries to calculate it
forcedImageHeight: 0, // specify height of all images; otherwise the carousel tries to calculate it
// callback functions
movingToCenter: $.noop, // fired when an item is about to move to the center position
movedToCenter: $.noop, // fired when an item has finished moving to the center
clickedCenter: $.noop, // fired when the center item has been clicked
movingFromCenter: $.noop, // fired when an item is about to leave the center position
movedFromCenter: $.noop // fired when an item has finished moving from the center
};
})(jQuery);
/*--water_wheel_carousel--*/
var carousel = $("#carousel").waterwheelCarousel({
flankingItems: 2,
separation:200,
sizeMultiplier:0.8,
separationMultiplier:0.9,
speed:250,
});
$("#carousel").swipe( {
swipeStatus:function(event, phase, direction, distance)
{
if (phase=="end"){
if(direction == "right") {
carousel.prev();
}
else if(direction =="left") {
carousel.next();
}else { return false;}
}
},
triggerOnTouchEnd:false,
threshold:100
});
$('#prev').on('click', function (){
carousel.prev();
return false;
});
$('#next').on('click', function (){
carousel.next();
return false;
});
/*--water_wheel carousel_script_end--*/
Kindly help me fix the javascript so as to apply the solution to my code. Thanks
Your images are loading, but there are DIVs with a white background on top of them that are visually blocking the images. If you remove the background-color CSS declaration on those DIVs, then your images will show up. Just update this:
#carousel .caption {
position: absolute;
background-color:#FFF;
top: 0px;
left: 0px;
height:100%;
width:100%;
}
to this:
#carousel .caption {
position: absolute;
top: 0px;
left: 0px;
height:100%;
width:100%;
}

Slider navigation dots not getting highlighted

My slider is partly made of dots indicating which slide is active. So when a slide gets activated, its associated dot should become blue.
But I could not get the last (third) dot to be highlighted when its slide is comes in.
Can anybody help me sort things out?
Thank you.
My code so far:
var prevDot = 0;
function init() {
var elem = document.getElementById('mySwipe');
window.mySwipe = Swipe(elem, {
// startSlide: 4,
// auto: 3000,
// continuous: true,
// disableScroll: true,
// stopPropagation: true,
// callback: function(index, element) {},
transitionEnd: function(index, element) {
$(".swipeDot").find("li").eq(prevDot).removeClass("dot_select");
prevDot = (index == 0 || index == 2) ? 0 : 1;
$(".swipeDot").find("li").eq(prevDot).addClass("dot_select");
console.log(index);
}
});
//$(".swipe-wrap").find("div").eq(2).find("span").css({backgroundImage:"url('../images/p006_img_2.png')"})
$("#prevBtn").click(function() {
window.mySwipe.prev();
});
$("#nextBtn").click(function() {
window.mySwipe.next();
});
$(".swipeDot li").each(function(index) {
$(this).attr("slideIndex", index);
});
$(".swipeDot li").click(function() {
var slideIndex = $(this).attr("slideIndex");
window.mySwipe.slide(slideIndex);
});
}
$(document).ready(init);
function Swipe(container, options) {
console.log(container);
"use strict";
// utilities
var noop = function() {}; // simple no operation function
var offloadFn = function(fn) {
setTimeout(fn || noop, 0)
}; // offload a functions execution
// check browser capabilities
var browser = {
addEventListener: !!window.addEventListener,
touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
transitions: (function(temp) {
var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];
for (var i in props)
if (temp.style[props[i]] !== undefined) return true;
return false;
})(document.createElement('swipe'))
};
// quit if no root element
if (!container) return;
var element = container.children[0];
var slides, slidePos, width, length;
options = options || {};
var index = parseInt(options.startSlide, 10) || 0;
var speed = options.speed || 300;
options.continuous = options.continuous !== undefined ? options.continuous : true;
function setup() {
// cache slides
slides = element.children;
length = slides.length;
// set continuous to false if only one slide
if (slides.length < 2) options.continuous = false;
//special case if two slides
if (browser.transitions && options.continuous && slides.length < 3) {
element.appendChild(slides[0].cloneNode(true));
element.appendChild(element.children[1].cloneNode(true));
slides = element.children;
}
// create an array to store current positions of each slide
slidePos = new Array(slides.length);
// determine width of each slide
width = container.offsetWidth;
element.style.width = (slides.length * width) + 'px';
// stack elements
var pos = slides.length;
while (pos--) {
var slide = slides[pos];
slide.style.width = width + 'px';
slide.setAttribute('data-index', pos);
if (browser.transitions) {
slide.style.left = (pos * -width) + 'px';
move(pos, index > pos ? -width : (index < pos ? width : 0), 0);
}
}
// reposition elements before and after index
if (options.continuous && browser.transitions) {
move(circle(index - 1), -width, 0);
move(circle(index + 1), width, 0);
}
if (!browser.transitions) element.style.left = (index * -width) + 'px';
container.style.visibility = 'visible';
}
function prev() {
if (options.continuous) slide(index - 1);
else if (index) slide(index - 1);
}
function next() {
if (options.continuous) slide(index + 1);
else if (index < slides.length - 1) slide(index + 1);
}
function circle(index) {
// a simple positive modulo using slides.length
return (slides.length + (index % slides.length)) % slides.length;
}
function slide(to, slideSpeed) {
// do nothing if already on requested slide
if (index == to) return;
if (browser.transitions) {
var direction = Math.abs(index - to) / (index - to); // 1: backward, -1: forward
// get the actual position of the slide
if (options.continuous) {
var natural_direction = direction;
direction = -slidePos[circle(to)] / width;
// if going forward but to < index, use to = slides.length + to
// if going backward but to > index, use to = -slides.length + to
if (direction !== natural_direction) to = -direction * slides.length + to;
}
var diff = Math.abs(index - to) - 1;
// move all the slides between index and to in the right direction
while (diff--) move(circle((to > index ? to : index) - diff - 1), width * direction, 0);
to = circle(to);
move(index, width * direction, slideSpeed || speed);
move(to, 0, slideSpeed || speed);
if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place
} else {
to = circle(to);
animate(index * -width, to * -width, slideSpeed || speed);
//no fallback for a circular continuous if the browser does not accept transitions
}
index = to;
offloadFn(options.callback && options.callback(index, slides[index]));
}
function move(index, dist, speed) {
translate(index, dist, speed);
slidePos[index] = dist;
}
function translate(index, dist, speed) {
var slide = slides[index];
var style = slide && slide.style;
if (!style) return;
style.webkitTransitionDuration =
style.MozTransitionDuration =
style.msTransitionDuration =
style.OTransitionDuration =
style.transitionDuration = speed + 'ms';
style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';
style.msTransform =
style.MozTransform =
style.OTransform = 'translateX(' + dist + 'px)';
}
function animate(from, to, speed) {
// if not an animation, just reposition
if (!speed) {
element.style.left = to + 'px';
return;
}
var start = +new Date;
var timer = setInterval(function() {
var timeElap = +new Date - start;
if (timeElap > speed) {
element.style.left = to + 'px';
if (delay) begin();
options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);
clearInterval(timer);
return;
}
element.style.left = (((to - from) * (Math.floor((timeElap / speed) * 100) / 100)) + from) + 'px';
}, 4);
}
// setup auto slideshow
var delay = options.auto || 0;
var interval;
function begin() {
interval = setTimeout(next, delay);
}
function stop() {
delay = 0;
clearTimeout(interval);
}
// setup initial vars
var start = {};
var delta = {};
var isScrolling;
// setup event capturing
var events = {
handleEvent: function(event) {
switch (event.type) {
case 'touchstart':
this.start(event);
break;
case 'touchmove':
this.move(event);
break;
case 'touchend':
offloadFn(this.end(event));
break;
case 'webkitTransitionEnd':
case 'msTransitionEnd':
case 'oTransitionEnd':
case 'otransitionend':
case 'transitionend':
offloadFn(this.transitionEnd(event));
break;
case 'resize':
offloadFn(setup);
break;
}
if (options.stopPropagation) event.stopPropagation();
},
start: function(event) {
var touches = event.touches[0];
// measure start values
start = {
// get initial touch coords
x: touches.pageX,
y: touches.pageY,
// store time to determine touch duration
time: +new Date
};
// used for testing first move event
isScrolling = undefined;
// reset delta and end measurements
delta = {};
// attach touchmove and touchend listeners
element.addEventListener('touchmove', this, false);
element.addEventListener('touchend', this, false);
},
move: function(event) {
// ensure swiping with one touch and not pinching
if (event.touches.length > 1 || event.scale && event.scale !== 1) return
if (options.disableScroll) event.preventDefault();
var touches = event.touches[0];
// measure change in x and y
delta = {
x: touches.pageX - start.x,
y: touches.pageY - start.y
}
// determine if scrolling test has run - one time test
if (typeof isScrolling == 'undefined') {
isScrolling = !!(isScrolling || Math.abs(delta.x) < Math.abs(delta.y));
}
// if user is not trying to scroll vertically
if (!isScrolling) {
// prevent native scrolling
event.preventDefault();
// stop slideshow
stop();
// increase resistance if first or last slide
if (options.continuous) { // we don't add resistance at the end
translate(circle(index - 1), delta.x + slidePos[circle(index - 1)], 0);
translate(index, delta.x + slidePos[index], 0);
translate(circle(index + 1), delta.x + slidePos[circle(index + 1)], 0);
} else {
delta.x =
delta.x /
((!index && delta.x > 0 // if first slide and sliding left
||
index == slides.length - 1 // or if last slide and sliding right
&&
delta.x < 0 // and if sliding at all
) ?
(Math.abs(delta.x) / width + 1) // determine resistance level
:
1); // no resistance if false
// translate 1:1
translate(index - 1, delta.x + slidePos[index - 1], 0);
translate(index, delta.x + slidePos[index], 0);
translate(index + 1, delta.x + slidePos[index + 1], 0);
}
}
},
end: function(event) {
// measure duration
var duration = +new Date - start.time;
// determine if slide attempt triggers next/prev slide
var isValidSlide =
Number(duration) < 250 // if slide duration is less than 250ms
&&
Math.abs(delta.x) > 20 // and if slide amt is greater than 20px
||
Math.abs(delta.x) > width / 2; // or if slide amt is greater than half the width
// determine if slide attempt is past start and end
var isPastBounds = !index && delta.x > 0 // if first slide and slide amt is greater than 0
||
index == slides.length - 1 && delta.x < 0; // or if last slide and slide amt is less than 0
if (options.continuous) isPastBounds = false;
// determine direction of swipe (true:right, false:left)
var direction = delta.x < 0;
// if not scrolling vertically
if (!isScrolling) {
if (isValidSlide && !isPastBounds) {
if (direction) {
if (options.continuous) { // we need to get the next in this direction in place
move(circle(index - 1), -width, 0);
move(circle(index + 2), width, 0);
} else {
move(index - 1, -width, 0);
}
move(index, slidePos[index] - width, speed);
move(circle(index + 1), slidePos[circle(index + 1)] - width, speed);
index = circle(index + 1);
} else {
if (options.continuous) { // we need to get the next in this direction in place
move(circle(index + 1), width, 0);
move(circle(index - 2), -width, 0);
} else {
move(index + 1, width, 0);
}
move(index, slidePos[index] + width, speed);
move(circle(index - 1), slidePos[circle(index - 1)] + width, speed);
index = circle(index - 1);
}
options.callback && options.callback(index, slides[index]);
} else {
if (options.continuous) {
move(circle(index - 1), -width, speed);
move(index, 0, speed);
move(circle(index + 1), width, speed);
} else {
move(index - 1, -width, speed);
move(index, 0, speed);
move(index + 1, width, speed);
}
}
}
// kill touchmove and touchend event listeners until touchstart called again
element.removeEventListener('touchmove', events, false)
element.removeEventListener('touchend', events, false)
},
transitionEnd: function(event) {
if (parseInt(event.target.getAttribute('data-index'), 10) == index) {
if (delay) begin();
options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);
}
}
}
// trigger setup
setup();
// start auto slideshow if applicable
if (delay) begin();
// add event listeners
if (browser.addEventListener) {
// set touchstart event on element
if (browser.touch) element.addEventListener('touchstart', events, false);
if (browser.transitions) {
element.addEventListener('webkitTransitionEnd', events, false);
element.addEventListener('msTransitionEnd', events, false);
element.addEventListener('oTransitionEnd', events, false);
element.addEventListener('otransitionend', events, false);
element.addEventListener('transitionend', events, false);
}
// set resize event on window
window.addEventListener('resize', events, false);
} else {
window.onresize = function() {
setup()
}; // to play nice with old IE
}
// expose the Swipe API
return {
setup: function() {
setup();
},
slide: function(to, speed) {
// cancel slideshow
stop();
slide(to, speed);
},
prev: function() {
// cancel slideshow
stop();
prev();
},
next: function() {
// cancel slideshow
stop();
next();
},
stop: function() {
// cancel slideshow
stop();
},
getPos: function() {
// return current index position
return index;
},
getNumSlides: function() {
// return total number of slides
return length;
},
kill: function() {
// cancel slideshow
stop();
// reset element
element.style.width = '';
element.style.left = '';
// reset slides
var pos = slides.length;
while (pos--) {
var slide = slides[pos];
slide.style.width = '';
slide.style.left = '';
if (browser.transitions) translate(pos, 0, 0);
}
// removed event listeners
if (browser.addEventListener) {
// remove current event listeners
element.removeEventListener('touchstart', events, false);
element.removeEventListener('webkitTransitionEnd', events, false);
element.removeEventListener('msTransitionEnd', events, false);
element.removeEventListener('oTransitionEnd', events, false);
element.removeEventListener('otransitionend', events, false);
element.removeEventListener('transitionend', events, false);
window.removeEventListener('resize', events, false);
} else {
window.onresize = null;
}
}
}
}
if (window.jQuery || window.Zepto) {
(function($) {
$.fn.Swipe = function(params) {
return this.each(function() {
$(this).data('Swipe', new Swipe($(this)[0], params));
});
}
})(window.jQuery || window.Zepto)
}
ul, li{display: inline-block; list-style:none}
li, .swipeBtn{cursor: pointer}
li+li{margin-left:10px}
li::before {content:'●'; color:grey}
.dot_select::before{color: blue}
#mySwipe{overflow: hidden}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="swipeBtn hotspot hspot cElement" id="prevBtn">❰</div>
<div id='mySwipe' style='width:600px;margin:0 auto' class='swipe hotspot hspot cElement'>
<div class='swipe-wrap'>
<div><span id="slide1">---- SLIDE1 ----</span></div>
<div><span id="slide2">---- SLIDE2 ----</span></div>
<div><span id="slide3">---- SLIDE3 ----</span></div>
</div>
</div>
<div class="swipeBtn hotspot hspot cElement" id="nextBtn">❱</div>
<ul class="swipeDot">
<li class="dot_select"></li>
<li ></li>
<li></li>
</ul>
Added the next lines
prevDot=index;
$(".swipeDot").find("li").eq(index).addClass("dot_select");
This will save the value of the active dot. The second line is yours but i just change the eq(prevdot) to eq(index)
Fior the problem stated in the comments. I debbuged the code and found the error on line 174 of the js.
U had
if (direction !== natural_direction) to= -direction * slides.length + to;
This line changed the value of to before it ended the operation and returned rubbish. So i just changed the to var for newTo and works fine. This is a debbuge of a long script. There might be a better solution but this is the one i got.
Hope this is what you were looking for. Happy to explain or help in a better solution if needed.
var prevDot = 0;
function init() {
var elem = document.getElementById('mySwipe');
window.mySwipe = Swipe(elem, {
// startSlide: 4,
// auto: 3000,
// continuous: true,
// disableScroll: true,
// stopPropagation: true,
// callback: function(index, element) {},
transitionEnd: function(index, element) {
$(".swipeDot").find("li").eq(prevDot).removeClass("dot_select");
prevDot = (index == 0 || index == 2) ? 0 : 1;
prevDot=index; $(".swipeDot").find("li").eq(index).addClass("dot_select");
console.log(index);
}
});
//$(".swipe-wrap").find("div").eq(2).find("span").css({backgroundImage:"url('../images/p006_img_2.png')"})
$("#prevBtn").click(function() {
window.mySwipe.prev();
});
$("#nextBtn").click(function() {
window.mySwipe.next();
});
$(".swipeDot li").each(function(index) {
$(this).attr("slideIndex", index);
});
$(".swipeDot li").click(function() {
var slideIndex = $(this).attr("slideIndex");
window.mySwipe.slide(slideIndex);
});
}
$(document).ready(init);
function Swipe(container, options) {
console.log(container);
"use strict";
// utilities
var noop = function() {}; // simple no operation function
var offloadFn = function(fn) {
setTimeout(fn || noop, 0)
}; // offload a functions execution
// check browser capabilities
var browser = {
addEventListener: !!window.addEventListener,
touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
transitions: (function(temp) {
var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];
for (var i in props)
if (temp.style[props[i]] !== undefined) return true;
return false;
})(document.createElement('swipe'))
};
// quit if no root element
if (!container) return;
var element = container.children[0];
var slides, slidePos, width, length;
options = options || {};
var index = parseInt(options.startSlide, 10) || 0;
var speed = options.speed || 300;
options.continuous = options.continuous !== undefined ? options.continuous : true;
function setup() {
// cache slides
slides = element.children;
length = slides.length;
// set continuous to false if only one slide
if (slides.length < 2) options.continuous = false;
//special case if two slides
if (browser.transitions && options.continuous && slides.length < 3) {
element.appendChild(slides[0].cloneNode(true));
element.appendChild(element.children[1].cloneNode(true));
slides = element.children;
}
// create an array to store current positions of each slide
slidePos = new Array(slides.length);
// determine width of each slide
width = container.offsetWidth;
element.style.width = (slides.length * width) + 'px';
// stack elements
var pos = slides.length;
while (pos--) {
var slide = slides[pos];
slide.style.width = width + 'px';
slide.setAttribute('data-index', pos);
if (browser.transitions) {
slide.style.left = (pos * -width) + 'px';
move(pos, index > pos ? -width : (index < pos ? width : 0), 0);
}
}
// reposition elements before and after index
if (options.continuous && browser.transitions) {
move(circle(index - 1), -width, 0);
move(circle(index + 1), width, 0);
}
if (!browser.transitions) element.style.left = (index * -width) + 'px';
container.style.visibility = 'visible';
}
function prev() {
if (options.continuous) slide(index - 1);
else if (index) slide(index - 1);
}
function next() {
if (options.continuous) slide(index + 1);
else if (index < slides.length - 1) slide(index + 1);
}
function circle(index) {
// a simple positive modulo using slides.length
return (slides.length + (index % slides.length)) % slides.length;
}
function slide(to, slideSpeed) {
// do nothing if already on requested slide
if (index == to) return;
if (browser.transitions) {
var direction = Math.abs(index - to) / (index - to); // 1: backward, -1: forward
// get the actual position of the slide
if (options.continuous) {
var natural_direction = direction;
direction = -slidePos[circle(to)] / width;
// if going forward but to < index, use to = slides.length + to
// if going backward but to > index, use to = -slides.length + to
if (direction !== natural_direction) newTo = -direction * slides.length + to;
}
var diff = Math.abs(index - to) - 1;
// move all the slides between index and to in the right direction
while (diff--) move(circle((to > index ? to : index) - diff - 1), width * direction, 0);
to = circle(to);
move(index, width * direction, slideSpeed || speed);
move(to, 0, slideSpeed || speed);
if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place
} else {
to = circle(to);
animate(index * -width, to * -width, slideSpeed || speed);
//no fallback for a circular continuous if the browser does not accept transitions
}
index = to;
offloadFn(options.callback && options.callback(index, slides[index]));
}
function move(index, dist, speed) {
translate(index, dist, speed);
slidePos[index] = dist;
}
function translate(index, dist, speed) {
var slide = slides[index];
var style = slide && slide.style;
if (!style) return;
style.webkitTransitionDuration =
style.MozTransitionDuration =
style.msTransitionDuration =
style.OTransitionDuration =
style.transitionDuration = speed + 'ms';
style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';
style.msTransform =
style.MozTransform =
style.OTransform = 'translateX(' + dist + 'px)';
}
function animate(from, to, speed) {
// if not an animation, just reposition
if (!speed) {
element.style.left = to + 'px';
return;
}
var start = +new Date;
var timer = setInterval(function() {
var timeElap = +new Date - start;
if (timeElap > speed) {
element.style.left = to + 'px';
if (delay) begin();
options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);
clearInterval(timer);
return;
}
element.style.left = (((to - from) * (Math.floor((timeElap / speed) * 100) / 100)) + from) + 'px';
}, 4);
}
// setup auto slideshow
var delay = options.auto || 0;
var interval;
function begin() {
interval = setTimeout(next, delay);
}
function stop() {
delay = 0;
clearTimeout(interval);
}
// setup initial vars
var start = {};
var delta = {};
var isScrolling;
// setup event capturing
var events = {
handleEvent: function(event) {
switch (event.type) {
case 'touchstart':
this.start(event);
break;
case 'touchmove':
this.move(event);
break;
case 'touchend':
offloadFn(this.end(event));
break;
case 'webkitTransitionEnd':
case 'msTransitionEnd':
case 'oTransitionEnd':
case 'otransitionend':
case 'transitionend':
offloadFn(this.transitionEnd(event));
break;
case 'resize':
offloadFn(setup);
break;
}
if (options.stopPropagation) event.stopPropagation();
},
start: function(event) {
var touches = event.touches[0];
// measure start values
start = {
// get initial touch coords
x: touches.pageX,
y: touches.pageY,
// store time to determine touch duration
time: +new Date
};
// used for testing first move event
isScrolling = undefined;
// reset delta and end measurements
delta = {};
// attach touchmove and touchend listeners
element.addEventListener('touchmove', this, false);
element.addEventListener('touchend', this, false);
},
move: function(event) {
// ensure swiping with one touch and not pinching
if (event.touches.length > 1 || event.scale && event.scale !== 1) return
if (options.disableScroll) event.preventDefault();
var touches = event.touches[0];
// measure change in x and y
delta = {
x: touches.pageX - start.x,
y: touches.pageY - start.y
}
// determine if scrolling test has run - one time test
if (typeof isScrolling == 'undefined') {
isScrolling = !!(isScrolling || Math.abs(delta.x) < Math.abs(delta.y));
}
// if user is not trying to scroll vertically
if (!isScrolling) {
// prevent native scrolling
event.preventDefault();
// stop slideshow
stop();
// increase resistance if first or last slide
if (options.continuous) { // we don't add resistance at the end
translate(circle(index - 1), delta.x + slidePos[circle(index - 1)], 0);
translate(index, delta.x + slidePos[index], 0);
translate(circle(index + 1), delta.x + slidePos[circle(index + 1)], 0);
} else {
delta.x =
delta.x /
((!index && delta.x > 0 // if first slide and sliding left
||
index == slides.length - 1 // or if last slide and sliding right
&&
delta.x < 0 // and if sliding at all
) ?
(Math.abs(delta.x) / width + 1) // determine resistance level
:
1); // no resistance if false
// translate 1:1
translate(index - 1, delta.x + slidePos[index - 1], 0);
translate(index, delta.x + slidePos[index], 0);
translate(index + 1, delta.x + slidePos[index + 1], 0);
}
}
},
end: function(event) {
// measure duration
var duration = +new Date - start.time;
// determine if slide attempt triggers next/prev slide
var isValidSlide =
Number(duration) < 250 // if slide duration is less than 250ms
&&
Math.abs(delta.x) > 20 // and if slide amt is greater than 20px
||
Math.abs(delta.x) > width / 2; // or if slide amt is greater than half the width
// determine if slide attempt is past start and end
var isPastBounds = !index && delta.x > 0 // if first slide and slide amt is greater than 0
||
index == slides.length - 1 && delta.x < 0; // or if last slide and slide amt is less than 0
if (options.continuous) isPastBounds = false;
// determine direction of swipe (true:right, false:left)
var direction = delta.x < 0;
// if not scrolling vertically
if (!isScrolling) {
if (isValidSlide && !isPastBounds) {
if (direction) {
if (options.continuous) { // we need to get the next in this direction in place
move(circle(index - 1), -width, 0);
move(circle(index + 2), width, 0);
} else {
move(index - 1, -width, 0);
}
move(index, slidePos[index] - width, speed);
move(circle(index + 1), slidePos[circle(index + 1)] - width, speed);
index = circle(index + 1);
} else {
if (options.continuous) { // we need to get the next in this direction in place
move(circle(index + 1), width, 0);
move(circle(index - 2), -width, 0);
} else {
move(index + 1, width, 0);
}
move(index, slidePos[index] + width, speed);
move(circle(index - 1), slidePos[circle(index - 1)] + width, speed);
index = circle(index - 1);
}
options.callback && options.callback(index, slides[index]);
} else {
if (options.continuous) {
move(circle(index - 1), -width, speed);
move(index, 0, speed);
move(circle(index + 1), width, speed);
} else {
move(index - 1, -width, speed);
move(index, 0, speed);
move(index + 1, width, speed);
}
}
}
// kill touchmove and touchend event listeners until touchstart called again
element.removeEventListener('touchmove', events, false)
element.removeEventListener('touchend', events, false)
},
transitionEnd: function(event) {
if (parseInt(event.target.getAttribute('data-index'), 10) == index) {
if (delay) begin();
options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);
}
}
}
// trigger setup
setup();
// start auto slideshow if applicable
if (delay) begin();
// add event listeners
if (browser.addEventListener) {
// set touchstart event on element
if (browser.touch) element.addEventListener('touchstart', events, false);
if (browser.transitions) {
element.addEventListener('webkitTransitionEnd', events, false);
element.addEventListener('msTransitionEnd', events, false);
element.addEventListener('oTransitionEnd', events, false);
element.addEventListener('otransitionend', events, false);
element.addEventListener('transitionend', events, false);
}
// set resize event on window
window.addEventListener('resize', events, false);
} else {
window.onresize = function() {
setup()
}; // to play nice with old IE
}
// expose the Swipe API
return {
setup: function() {
setup();
},
slide: function(to, speed) {
// cancel slideshow
stop();
slide(to, speed);
},
prev: function() {
// cancel slideshow
stop();
prev();
},
next: function() {
// cancel slideshow
stop();
next();
},
stop: function() {
// cancel slideshow
stop();
},
getPos: function() {
// return current index position
return index;
},
getNumSlides: function() {
// return total number of slides
return length;
},
kill: function() {
// cancel slideshow
stop();
// reset element
element.style.width = '';
element.style.left = '';
// reset slides
var pos = slides.length;
while (pos--) {
var slide = slides[pos];
slide.style.width = '';
slide.style.left = '';
if (browser.transitions) translate(pos, 0, 0);
}
// removed event listeners
if (browser.addEventListener) {
// remove current event listeners
element.removeEventListener('touchstart', events, false);
element.removeEventListener('webkitTransitionEnd', events, false);
element.removeEventListener('msTransitionEnd', events, false);
element.removeEventListener('oTransitionEnd', events, false);
element.removeEventListener('otransitionend', events, false);
element.removeEventListener('transitionend', events, false);
window.removeEventListener('resize', events, false);
} else {
window.onresize = null;
}
}
}
}
if (window.jQuery || window.Zepto) {
(function($) {
$.fn.Swipe = function(params) {
return this.each(function() {
$(this).data('Swipe', new Swipe($(this)[0], params));
});
}
})(window.jQuery || window.Zepto)
}
ul,
li {
display: inline-block;
list-style: none
}
li,
.swipeBtn {
cursor: pointer
}
li+li {
margin-left: 10px
}
li::before {
content: '●';
color: grey
}
.dot_select::before {
color: blue
}
#mySwipe {
overflow: hidden
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="swipeBtn hotspot hspot cElement" id="prevBtn">❰</div>
<div id='mySwipe' style='width:600px;margin:0 auto' class='swipe hotspot hspot cElement'>
<div class='swipe-wrap'>
<div><span id="slide1">---- SLIDE1 ----</span></div>
<div><span id="slide2">---- SLIDE2 ----</span></div>
<div><span id="slide3">---- SLIDE3 ----</span></div>
</div>
</div>
<div class="swipeBtn hotspot hspot cElement" id="nextBtn">❱</div>
<ul class="swipeDot">
<li class="dot_select"></li>
<li></li>
<li></li>
</ul>
You do not need var prevDot. You might play it right using index:
$(".swipeDot li").removeClass("dot_select")
.eq(index).addClass("dot_select");
See the snippet:
function init() {
var elem = document.getElementById('mySwipe');
window.mySwipe = Swipe(elem, {
// startSlide: 4,
// auto: 3000,
// continuous: true,
// disableScroll: true,
// stopPropagation: true,
// callback: function(index, element) {},
transitionEnd: function(index, element) {
$(".swipeDot li").removeClass("dot_select")
.eq(index).addClass("dot_select");
}
});
//$(".swipe-wrap").find("div").eq(2).find("span").css({backgroundImage:"url('../images/p006_img_2.png')"})
$("#prevBtn").click(function() {
window.mySwipe.prev();
});
$("#nextBtn").click(function() {
window.mySwipe.next();
});
$(".swipeDot li").each(function(index) {
$(this).attr("slideIndex", index);
});
$(".swipeDot li").click(function() {
var slideIndex = $(this).attr("slideIndex");
window.mySwipe.slide(slideIndex);
});
}
$(document).ready(init);
function Swipe(container, options) {
console.log(container);
"use strict";
// utilities
var noop = function() {}; // simple no operation function
var offloadFn = function(fn) {
setTimeout(fn || noop, 0)
}; // offload a functions execution
// check browser capabilities
var browser = {
addEventListener: !!window.addEventListener,
touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
transitions: (function(temp) {
var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];
for (var i in props)
if (temp.style[props[i]] !== undefined) return true;
return false;
})(document.createElement('swipe'))
};
// quit if no root element
if (!container) return;
var element = container.children[0];
var slides, slidePos, width, length;
options = options || {};
var index = parseInt(options.startSlide, 10) || 0;
var speed = options.speed || 300;
options.continuous = options.continuous !== undefined ? options.continuous : true;
function setup() {
// cache slides
slides = element.children;
length = slides.length;
// set continuous to false if only one slide
if (slides.length < 2) options.continuous = false;
//special case if two slides
if (browser.transitions && options.continuous && slides.length < 3) {
element.appendChild(slides[0].cloneNode(true));
element.appendChild(element.children[1].cloneNode(true));
slides = element.children;
}
// create an array to store current positions of each slide
slidePos = new Array(slides.length);
// determine width of each slide
width = container.offsetWidth;
element.style.width = (slides.length * width) + 'px';
// stack elements
var pos = slides.length;
while (pos--) {
var slide = slides[pos];
slide.style.width = width + 'px';
slide.setAttribute('data-index', pos);
if (browser.transitions) {
slide.style.left = (pos * -width) + 'px';
move(pos, index > pos ? -width : (index < pos ? width : 0), 0);
}
}
// reposition elements before and after index
if (options.continuous && browser.transitions) {
move(circle(index - 1), -width, 0);
move(circle(index + 1), width, 0);
}
if (!browser.transitions) element.style.left = (index * -width) + 'px';
container.style.visibility = 'visible';
}
function prev() {
if (options.continuous) slide(index - 1);
else if (index) slide(index - 1);
}
function next() {
if (options.continuous) slide(index + 1);
else if (index < slides.length - 1) slide(index + 1);
}
function circle(index) {
// a simple positive modulo using slides.length
return (slides.length + (index % slides.length)) % slides.length;
}
function slide(to, slideSpeed) {
// do nothing if already on requested slide
if (index == to) return;
if (browser.transitions) {
var direction = Math.abs(index - to) / (index - to); // 1: backward, -1: forward
// get the actual position of the slide
if (options.continuous) {
var natural_direction = direction;
direction = -slidePos[circle(to)] / width;
// if going forward but to < index, use to = slides.length + to
// if going backward but to > index, use to = -slides.length + to
if (direction !== natural_direction) to = -direction * slides.length + to;
}
var diff = Math.abs(index - to) - 1;
// move all the slides between index and to in the right direction
while (diff--) move(circle((to > index ? to : index) - diff - 1), width * direction, 0);
to = circle(to);
move(index, width * direction, slideSpeed || speed);
move(to, 0, slideSpeed || speed);
if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place
} else {
to = circle(to);
animate(index * -width, to * -width, slideSpeed || speed);
//no fallback for a circular continuous if the browser does not accept transitions
}
index = to;
offloadFn(options.callback && options.callback(index, slides[index]));
}
function move(index, dist, speed) {
translate(index, dist, speed);
slidePos[index] = dist;
}
function translate(index, dist, speed) {
var slide = slides[index];
var style = slide && slide.style;
if (!style) return;
style.webkitTransitionDuration =
style.MozTransitionDuration =
style.msTransitionDuration =
style.OTransitionDuration =
style.transitionDuration = speed + 'ms';
style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';
style.msTransform =
style.MozTransform =
style.OTransform = 'translateX(' + dist + 'px)';
}
function animate(from, to, speed) {
// if not an animation, just reposition
if (!speed) {
element.style.left = to + 'px';
return;
}
var start = +new Date;
var timer = setInterval(function() {
var timeElap = +new Date - start;
if (timeElap > speed) {
element.style.left = to + 'px';
if (delay) begin();
options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);
clearInterval(timer);
return;
}
element.style.left = (((to - from) * (Math.floor((timeElap / speed) * 100) / 100)) + from) + 'px';
}, 4);
}
// setup auto slideshow
var delay = options.auto || 0;
var interval;
function begin() {
interval = setTimeout(next, delay);
}
function stop() {
delay = 0;
clearTimeout(interval);
}
// setup initial vars
var start = {};
var delta = {};
var isScrolling;
// setup event capturing
var events = {
handleEvent: function(event) {
switch (event.type) {
case 'touchstart':
this.start(event);
break;
case 'touchmove':
this.move(event);
break;
case 'touchend':
offloadFn(this.end(event));
break;
case 'webkitTransitionEnd':
case 'msTransitionEnd':
case 'oTransitionEnd':
case 'otransitionend':
case 'transitionend':
offloadFn(this.transitionEnd(event));
break;
case 'resize':
offloadFn(setup);
break;
}
if (options.stopPropagation) event.stopPropagation();
},
start: function(event) {
var touches = event.touches[0];
// measure start values
start = {
// get initial touch coords
x: touches.pageX,
y: touches.pageY,
// store time to determine touch duration
time: +new Date
};
// used for testing first move event
isScrolling = undefined;
// reset delta and end measurements
delta = {};
// attach touchmove and touchend listeners
element.addEventListener('touchmove', this, false);
element.addEventListener('touchend', this, false);
},
move: function(event) {
// ensure swiping with one touch and not pinching
if (event.touches.length > 1 || event.scale && event.scale !== 1) return
if (options.disableScroll) event.preventDefault();
var touches = event.touches[0];
// measure change in x and y
delta = {
x: touches.pageX - start.x,
y: touches.pageY - start.y
}
// determine if scrolling test has run - one time test
if (typeof isScrolling == 'undefined') {
isScrolling = !!(isScrolling || Math.abs(delta.x) < Math.abs(delta.y));
}
// if user is not trying to scroll vertically
if (!isScrolling) {
// prevent native scrolling
event.preventDefault();
// stop slideshow
stop();
// increase resistance if first or last slide
if (options.continuous) { // we don't add resistance at the end
translate(circle(index - 1), delta.x + slidePos[circle(index - 1)], 0);
translate(index, delta.x + slidePos[index], 0);
translate(circle(index + 1), delta.x + slidePos[circle(index + 1)], 0);
} else {
delta.x =
delta.x /
((!index && delta.x > 0 // if first slide and sliding left
||
index == slides.length - 1 // or if last slide and sliding right
&&
delta.x < 0 // and if sliding at all
) ?
(Math.abs(delta.x) / width + 1) // determine resistance level
:
1); // no resistance if false
// translate 1:1
translate(index - 1, delta.x + slidePos[index - 1], 0);
translate(index, delta.x + slidePos[index], 0);
translate(index + 1, delta.x + slidePos[index + 1], 0);
}
}
},
end: function(event) {
// measure duration
var duration = +new Date - start.time;
// determine if slide attempt triggers next/prev slide
var isValidSlide =
Number(duration) < 250 // if slide duration is less than 250ms
&&
Math.abs(delta.x) > 20 // and if slide amt is greater than 20px
||
Math.abs(delta.x) > width / 2; // or if slide amt is greater than half the width
// determine if slide attempt is past start and end
var isPastBounds = !index && delta.x > 0 // if first slide and slide amt is greater than 0
||
index == slides.length - 1 && delta.x < 0; // or if last slide and slide amt is less than 0
if (options.continuous) isPastBounds = false;
// determine direction of swipe (true:right, false:left)
var direction = delta.x < 0;
// if not scrolling vertically
if (!isScrolling) {
if (isValidSlide && !isPastBounds) {
if (direction) {
if (options.continuous) { // we need to get the next in this direction in place
move(circle(index - 1), -width, 0);
move(circle(index + 2), width, 0);
} else {
move(index - 1, -width, 0);
}
move(index, slidePos[index] - width, speed);
move(circle(index + 1), slidePos[circle(index + 1)] - width, speed);
index = circle(index + 1);
} else {
if (options.continuous) { // we need to get the next in this direction in place
move(circle(index + 1), width, 0);
move(circle(index - 2), -width, 0);
} else {
move(index + 1, width, 0);
}
move(index, slidePos[index] + width, speed);
move(circle(index - 1), slidePos[circle(index - 1)] + width, speed);
index = circle(index - 1);
}
options.callback && options.callback(index, slides[index]);
} else {
if (options.continuous) {
move(circle(index - 1), -width, speed);
move(index, 0, speed);
move(circle(index + 1), width, speed);
} else {
move(index - 1, -width, speed);
move(index, 0, speed);
move(index + 1, width, speed);
}
}
}
// kill touchmove and touchend event listeners until touchstart called again
element.removeEventListener('touchmove', events, false)
element.removeEventListener('touchend', events, false)
},
transitionEnd: function(event) {
if (parseInt(event.target.getAttribute('data-index'), 10) == index) {
if (delay) begin();
options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);
}
}
}
// trigger setup
setup();
// start auto slideshow if applicable
if (delay) begin();
// add event listeners
if (browser.addEventListener) {
// set touchstart event on element
if (browser.touch) element.addEventListener('touchstart', events, false);
if (browser.transitions) {
element.addEventListener('webkitTransitionEnd', events, false);
element.addEventListener('msTransitionEnd', events, false);
element.addEventListener('oTransitionEnd', events, false);
element.addEventListener('otransitionend', events, false);
element.addEventListener('transitionend', events, false);
}
// set resize event on window
window.addEventListener('resize', events, false);
} else {
window.onresize = function() {
setup()
}; // to play nice with old IE
}
// expose the Swipe API
return {
setup: function() {
setup();
},
slide: function(to, speed) {
// cancel slideshow
stop();
slide(to, speed);
},
prev: function() {
// cancel slideshow
stop();
prev();
},
next: function() {
// cancel slideshow
stop();
next();
},
stop: function() {
// cancel slideshow
stop();
},
getPos: function() {
// return current index position
return index;
},
getNumSlides: function() {
// return total number of slides
return length;
},
kill: function() {
// cancel slideshow
stop();
// reset element
element.style.width = '';
element.style.left = '';
// reset slides
var pos = slides.length;
while (pos--) {
var slide = slides[pos];
slide.style.width = '';
slide.style.left = '';
if (browser.transitions) translate(pos, 0, 0);
}
// removed event listeners
if (browser.addEventListener) {
// remove current event listeners
element.removeEventListener('touchstart', events, false);
element.removeEventListener('webkitTransitionEnd', events, false);
element.removeEventListener('msTransitionEnd', events, false);
element.removeEventListener('oTransitionEnd', events, false);
element.removeEventListener('otransitionend', events, false);
element.removeEventListener('transitionend', events, false);
window.removeEventListener('resize', events, false);
} else {
window.onresize = null;
}
}
}
}
if (window.jQuery || window.Zepto) {
(function($) {
$.fn.Swipe = function(params) {
return this.each(function() {
$(this).data('Swipe', new Swipe($(this)[0], params));
});
}
})(window.jQuery || window.Zepto)
}
ul,
li {
display: inline-block;
list-style: none
}
li,
.swipeBtn {
cursor: pointer
}
li+li {
margin-left: 10px
}
li::before {
content: '●';
color: grey
}
.dot_select::before {
color: blue
}
#mySwipe {
overflow: hidden
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="swipeBtn hotspot hspot cElement" id="prevBtn">❰</div>
<div id='mySwipe' style='width:600px;margin:0 auto' class='swipe hotspot hspot cElement'>
<div class='swipe-wrap'>
<div><span id="slide1">---- SLIDE1 ----</span></div>
<div><span id="slide2">---- SLIDE2 ----</span></div>
<div><span id="slide3">---- SLIDE3 ----</span></div>
</div>
</div>
<div class="swipeBtn hotspot hspot cElement" id="nextBtn">❱</div>
<ul class="swipeDot">
<li class="dot_select"></li>
<li></li>
<li></li>
</ul>

Scrolling with an easing function

I have written a function that when invoked, will scroll the page downwards in a smooth fashion.
The problem is that the amount scrolled is not precise. It can be off by one pixel or so depending on the distance scrolled and the duration of the scroll.
Is there a better way to do this such that the scroll can move the exact number of desired pixels?
const EASING = BezierEasing(0, .1, .1, 1); // Example values.
const DURATION = 1000; // ms.
document.addEventListener('DOMContentLoaded',
() => {
document.querySelector('#foo')
.addEventListener('click', onClick, false);
});
function onClick(e) {
scroll(400); // px.
e.preventDefault();
return false;
}
function scroll(distance) {
var start, travelled, html;
start = performance.now();
travelled = 0;
html = document.querySelector('html');
(function move(now) {
var fractionDone, dy, toMove;
fractionDone = (now-start) / DURATION;
if((1 - fractionDone) <= 0) {
return; // Done!
}
if(window.scrollY + window.innerHeight
=== html.offsetHeight) {
return; // At bottom of page.
}
dy = ((EASING.get(fractionDone)) * distance);
toMove = Math.floor((dy - travelled)); // `scrollBy` only accepts integers.
if(toMove > 0) {
window.scrollBy(0, toMove);
travelled += toMove;
}
requestAnimationFrame(move);
}(start));
}
<!DOCTYPE html>
<html>
<head>
<script src="https://rawgit.com/gre/bezier-easing/master/build.js"></script>
</head>
<body>
<a href id="foo">Click Me!</a>
<script>
/* print some numbers to
the DOM to help visualize
the scrolling */
var body = document.querySelector('body');
for(var x = 0; x < 50; x++) {
var div = document.createElement("div");
var text = document.createTextNode(x);
div.appendChild(text);
body.appendChild(div);
}
</script>
</body>
</html>
could you do something like this to account for the last iteration?
basically if toMove gets rounded down to 0, but distance hasnt been travelled yet, force it to do scroll one more?
if(toMove > 0 || (toMove == 0 && travelled != distance) {
window.scrollBy(0, (toMove ? toMove : 1));
travelled += toMove;
}
I decided to modify the done logic to move any remaining distance:
// ...
if((1 - fractionDone) <= 0) {
window.scrollBy(0, (distance - travelled)); // Scroll any remaining distance.
return; // Done!
}
// ...
Which, I think, solves the issue.

fadeIn and fadeOut functions not working

I am working on fadein and fadeout functions using pure javascript, here is the code:
(function() {
var fx = {
easing: {
linear: function(progress) {
return progress;
},
quadratic: function(progress) {
return Math.pow(progress, 2);
},
swing: function(progress) {
return 0.5 - Math.cos(progress * Math.PI) / 2;
},
circ: function(progress) {
return 1 - Math.sin(Math.acos(progress));
},
back: function(progress, x) {
return Math.pow(progress, 2) * ((x + 1) * progress - x);
},
bounce: function(progress) {
for (var a = 0, b = 1, result; 1; a += b, b /= 2) {
if (progress >= (7 - 4 * a) / 11) {
return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2);
}
}
},
elastic: function(progress, x) {
return Math.pow(2, 10 * (progress - 1)) * Math.cos(20 * Math.PI * x / 3 * progress);
}
},
animate: function(options) {
var start = new Date;
var id = setInterval(function() {
var timePassed = new Date - start;
var progress = timePassed / options.duration;
if (progress > 1) {
progress = 1;
}
options.progress = progress;
var delta = options.delta(progress);
options.step(delta);
if (progress == 1) {
clearInterval(id);
options.complete();
}
}, options.delay || 10);
},
fadeOut: function(element, options) {
var to = 1;
this.animate({
duration: options.duration,
delta: function(progress) {
progress = this.progress;
return fx.easing.swing(progress);
},
complete: options.complete,
step: function(delta) {
element.style.opacity = to - delta;
}
});
},
fadeIn: function(element, options) {
var to = 0;
this.animate({
duration: options.duration,
delta: function(progress) {
progress = this.progress;
return fx.easing.swing(progress);
},
complete: options.complete,
step: function(delta) {
element.style.opacity = to + delta;
}
});
}
};
window.fx = fx;
})()
I am using the following code to activate the function:
document.getElementById('in').addEventListener('click', function() {
FX.fadeIn(document.getElementById('type'), {
duration: 2000,
});
}, false);
But when I load the page I get an error with the activation code.
Does anyone know what I have done wrong?
Thank you so much.
With a few adjustments, your code should work fine...
As Richard Dalton noticed, call the object with fx not FX.
Add default values for all options in case an option isn't provided (your example code throws an error because options.complete is undefined). Example:
this.animate({
duration: options.duration || 1000,
...
complete: options.complete || function() { },
...
});
Adjust the styles before animating (imagine an element is hidden using display: none instead of opacity: 0)... Example (for fadeIn):
element.style.opacity = 0;
element.style.visibility = "visible";
element.style.display = "block";
Check if an action is needed at all (e.g. call to fadeIn despite element already being visible):
isVisible: function(element) {
return element.style.display !== "none" &&
element.style.visibility !== "hidden" &&
element.style.opacity !== "0";
}
// In fadeIn:
if (!this.isVisible(element)) {
...
}
Here's the corrected code with an example: JSFiddle
Certainly one could tweak this a little further, deal with some corner cases (e.g. what happens if there is a call to fadeIn while the element is currently being faded out?), and I'm pretty sure it doesn't work in all browsers (old IEs)... But I hope I pointed you in the right direction. :)

Smooth scroll without the use of jQuery

I'm coding up a page where I only want to use raw JavaScript code for UI without any interference of plugins or frameworks.
And now I'm struggling with finding a way to scroll over the page smoothly without jQuery.
Native browser smooth scrolling in JavaScript is like this:
// scroll to specific values,
// same as window.scroll() method.
// for scrolling a particular distance, use window.scrollBy().
window.scroll({
top: 2500,
left: 0,
behavior: 'smooth'
});
// scroll certain amounts from current position
window.scrollBy({
top: 100, // negative value acceptable
left: 0,
behavior: 'smooth'
});
// scroll to a certain element
document.querySelector('.hello').scrollIntoView({
behavior: 'smooth'
});
Try this smooth scrolling demo, or an algorithm like:
Get the current top location using self.pageYOffset
Get the position of element till where you want to scroll to: element.offsetTop
Do a for loop to reach there, which will be quite fast or use a timer to do smooth scroll till that position using window.scrollTo
See also the other popular answer to this question.
Andrew Johnson's original code:
function currentYPosition() {
// Firefox, Chrome, Opera, Safari
if (self.pageYOffset) return self.pageYOffset;
// Internet Explorer 6 - standards mode
if (document.documentElement && document.documentElement.scrollTop)
return document.documentElement.scrollTop;
// Internet Explorer 6, 7 and 8
if (document.body.scrollTop) return document.body.scrollTop;
return 0;
}
function elmYPosition(eID) {
var elm = document.getElementById(eID);
var y = elm.offsetTop;
var node = elm;
while (node.offsetParent && node.offsetParent != document.body) {
node = node.offsetParent;
y += node.offsetTop;
} return y;
}
function smoothScroll(eID) {
var startY = currentYPosition();
var stopY = elmYPosition(eID);
var distance = stopY > startY ? stopY - startY : startY - stopY;
if (distance < 100) {
scrollTo(0, stopY); return;
}
var speed = Math.round(distance / 100);
if (speed >= 20) speed = 20;
var step = Math.round(distance / 25);
var leapY = stopY > startY ? startY + step : startY - step;
var timer = 0;
if (stopY > startY) {
for ( var i=startY; i<stopY; i+=step ) {
setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
leapY += step; if (leapY > stopY) leapY = stopY; timer++;
} return;
}
for ( var i=startY; i>stopY; i-=step ) {
setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
}
}
Related links:
https://www.sitepoint.com/smooth-scrolling-vanilla-javascript/
https://github.com/zengabor/zenscroll/blob/dist/zenscroll.js
https://github.com/cferdinandi/smooth-scroll/blob/master/src/js/smooth-scroll.js
https://github.com/alicelieutier/smoothScroll/blob/master/smoothscroll.js
Algorithm
Scrolling an element requires changing its scrollTop value over time. For a given point in time, calculate a new scrollTop value. To animate smoothly, interpolate using a smooth-step algorithm.
Calculate scrollTop as follows:
var point = smooth_step(start_time, end_time, now);
var scrollTop = Math.round(start_top + (distance * point));
Where:
start_time is the time the animation started;
end_time is when the animation will end (start_time + duration);
start_top is the scrollTop value at the beginning; and
distance is the difference between the desired end value and the start value (target - start_top).
A robust solution should detect when animating is interrupted, and more. Read my post about Smooth Scrolling without jQuery for details.
Demo
See the JSFiddle.
Implementation
The code:
/**
Smoothly scroll element to the given target (element.scrollTop)
for the given duration
Returns a promise that's fulfilled when done, or rejected if
interrupted
*/
var smooth_scroll_to = function(element, target, duration) {
target = Math.round(target);
duration = Math.round(duration);
if (duration < 0) {
return Promise.reject("bad duration");
}
if (duration === 0) {
element.scrollTop = target;
return Promise.resolve();
}
var start_time = Date.now();
var end_time = start_time + duration;
var start_top = element.scrollTop;
var distance = target - start_top;
// based on http://en.wikipedia.org/wiki/Smoothstep
var smooth_step = function(start, end, point) {
if(point <= start) { return 0; }
if(point >= end) { return 1; }
var x = (point - start) / (end - start); // interpolation
return x*x*(3 - 2*x);
}
return new Promise(function(resolve, reject) {
// This is to keep track of where the element's scrollTop is
// supposed to be, based on what we're doing
var previous_top = element.scrollTop;
// This is like a think function from a game loop
var scroll_frame = function() {
if(element.scrollTop != previous_top) {
reject("interrupted");
return;
}
// set the scrollTop for this frame
var now = Date.now();
var point = smooth_step(start_time, end_time, now);
var frameTop = Math.round(start_top + (distance * point));
element.scrollTop = frameTop;
// check if we're done!
if(now >= end_time) {
resolve();
return;
}
// If we were supposed to scroll but didn't, then we
// probably hit the limit, so consider it done; not
// interrupted.
if(element.scrollTop === previous_top
&& element.scrollTop !== frameTop) {
resolve();
return;
}
previous_top = element.scrollTop;
// schedule next frame for execution
setTimeout(scroll_frame, 0);
}
// boostrap the animation process
setTimeout(scroll_frame, 0);
});
}
You can use the new Scroll Behaviour CSS Property.
for example, add the below line to your CSS.
html{
scroll-behavior:smooth;
}
and this will result in a native smooth scrolling feature.
see demo here
All modern browsers support the scroll-behavior property.
Read More about Scroll behavior
I've made an example without jQuery here : http://codepen.io/sorinnn/pen/ovzdq
/**
by Nemes Ioan Sorin - not an jQuery big fan
therefore this script is for those who love the old clean coding style
#id = the id of the element who need to bring into view
Note : this demo scrolls about 12.700 pixels from Link1 to Link3
*/
(function()
{
window.setTimeout = window.setTimeout; //
})();
var smoothScr = {
iterr : 30, // set timeout miliseconds ..decreased with 1ms for each iteration
tm : null, //timeout local variable
stopShow: function()
{
clearTimeout(this.tm); // stopp the timeout
this.iterr = 30; // reset milisec iterator to original value
},
getRealTop : function (el) // helper function instead of jQuery
{
var elm = el;
var realTop = 0;
do
{
realTop += elm.offsetTop;
elm = elm.offsetParent;
}
while(elm);
return realTop;
},
getPageScroll : function() // helper function instead of jQuery
{
var pgYoff = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
return pgYoff;
},
anim : function (id) // the main func
{
this.stopShow(); // for click on another button or link
var eOff, pOff, tOff, scrVal, pos, dir, step;
eOff = document.getElementById(id).offsetTop; // element offsetTop
tOff = this.getRealTop(document.getElementById(id).parentNode); // terminus point
pOff = this.getPageScroll(); // page offsetTop
if (pOff === null || isNaN(pOff) || pOff === 'undefined') pOff = 0;
scrVal = eOff - pOff; // actual scroll value;
if (scrVal > tOff)
{
pos = (eOff - tOff - pOff);
dir = 1;
}
if (scrVal < tOff)
{
pos = (pOff + tOff) - eOff;
dir = -1;
}
if(scrVal !== tOff)
{
step = ~~((pos / 4) +1) * dir;
if(this.iterr > 1) this.iterr -= 1;
else this.itter = 0; // decrease the timeout timer value but not below 0
window.scrollBy(0, step);
this.tm = window.setTimeout(function()
{
smoothScr.anim(id);
}, this.iterr);
}
if(scrVal === tOff)
{
this.stopShow(); // reset function values
return;
}
}
}
Modern browsers has support for CSS "scroll-behavior: smooth" property. So, we even don't need any Javascript at all for this. Just add this for the "html" element, and use usual anchors and links.
scroll-behavior MDN docs
I recently set out to solve this problem in a situation where jQuery wasn't an option, so I'm logging my solution here just for posterity.
var scroll = (function() {
var elementPosition = function(a) {
return function() {
return a.getBoundingClientRect().top;
};
};
var scrolling = function( elementID ) {
var el = document.getElementById( elementID ),
elPos = elementPosition( el ),
duration = 400,
increment = Math.round( Math.abs( elPos() )/40 ),
time = Math.round( duration/increment ),
prev = 0,
E;
function scroller() {
E = elPos();
if (E === prev) {
return;
} else {
prev = E;
}
increment = (E > -20 && E < 20) ? ((E > - 5 && E < 5) ? 1 : 5) : increment;
if (E > 1 || E < -1) {
if (E < 0) {
window.scrollBy( 0,-increment );
} else {
window.scrollBy( 0,increment );
}
setTimeout(scroller, time);
} else {
el.scrollTo( 0,0 );
}
}
scroller();
};
return {
To: scrolling
}
})();
/* usage */
scroll.To('elementID');
The scroll() function uses the Revealing Module Pattern to pass the target element's id to its scrolling() function, via scroll.To('id'), which sets the values used by the scroller() function.
Breakdown
In scrolling():
el : the target DOM object
elPos : returns a function via elememtPosition() which gives the position of the target element relative to the top of the page each time it's called.
duration : transition time in milliseconds.
increment : divides the starting position of the target element into 40 steps.
time : sets the timing of each step.
prev : the target element's previous position in scroller().
E : holds the target element's position in scroller().
The actual work is done by the scroller() function which continues to call itself (via setTimeout()) until the target element is at the top of the page or the page can scroll no more.
Each time scroller() is called it checks the current position of the target element (held in variable E) and if that is > 1 OR < -1 and if the page is still scrollable shifts the window by increment pixels - up or down depending if E is a positive or negative value. When E is neither > 1 OR < -1, or E === prev the function stops. I added the DOMElement.scrollTo() method on completion just to make sure the target element was bang on the top of the window (not that you'd notice it being out by a fraction of a pixel!).
The if statement on line 2 of scroller() checks to see if the page is scrolling (in cases where the target might be towards the bottom of the page and the page can scroll no further) by checking E against its previous position (prev).
The ternary condition below it reduce the increment value as E approaches zero. This stops the page overshooting one way and then bouncing back to overshoot the other, and then bouncing back to overshoot the other again, ping-pong style, to infinity and beyond.
If your page is more that c.4000px high you might want to increase the values in the ternary expression's first condition (here at +/-20) and/or the divisor which sets the increment value (here at 40).
Playing about with duration, the divisor which sets increment, and the values in the ternary condition of scroller() should allow you to tailor the function to suit your page.
JSFiddle
N.B.Tested in up-to-date versions of Firefox and Chrome on Lubuntu, and Firefox, Chrome and IE on Windows8.
I've made something like this.
I have no idea if its working in IE8.
Tested in IE9, Mozilla, Chrome, Edge.
function scroll(toElement, speed) {
var windowObject = window;
var windowPos = windowObject.pageYOffset;
var pointer = toElement.getAttribute('href').slice(1);
var elem = document.getElementById(pointer);
var elemOffset = elem.offsetTop;
var counter = setInterval(function() {
windowPos;
if (windowPos > elemOffset) { // from bottom to top
windowObject.scrollTo(0, windowPos);
windowPos -= speed;
if (windowPos <= elemOffset) { // scrolling until elemOffset is higher than scrollbar position, cancel interval and set scrollbar to element position
clearInterval(counter);
windowObject.scrollTo(0, elemOffset);
}
} else { // from top to bottom
windowObject.scrollTo(0, windowPos);
windowPos += speed;
if (windowPos >= elemOffset) { // scroll until scrollbar is lower than element, cancel interval and set scrollbar to element position
clearInterval(counter);
windowObject.scrollTo(0, elemOffset);
}
}
}, 1);
}
//call example
var navPointer = document.getElementsByClassName('nav__anchor');
for (i = 0; i < navPointer.length; i++) {
navPointer[i].addEventListener('click', function(e) {
scroll(this, 18);
e.preventDefault();
});
}
Description
pointer—get element and chceck if it has attribute "href" if yes,
get rid of "#"
elem—pointer variable without "#"
elemOffset—offset of "scroll to" element from the top of the page
You can use
document.querySelector('your-element').scrollIntoView({behavior: 'smooth'});
If you want to scroll top the top of the page, you can just place an empty element in the top, and smooth scroll to that one.
With using the following smooth scrolling is working fine:
html {
scroll-behavior: smooth;
}
<script>
var set = 0;
function animatescroll(x, y) {
if (set == 0) {
var val72 = 0;
var val73 = 0;
var setin = 0;
set = 1;
var interval = setInterval(function() {
if (setin == 0) {
val72++;
val73 += x / 1000;
if (val72 == 1000) {
val73 = 0;
interval = clearInterval(interval);
}
document.getElementById(y).scrollTop = val73;
}
}, 1);
}
}
</script>
x = scrollTop
y = id of the div that is used to scroll
Note:
For making the body to scroll give the body an ID.
Here is my solution. Works in most browsers
document.getElementById("scrollHere").scrollIntoView({behavior: "smooth"});
Docs
document.getElementById("end").scrollIntoView({behavior: "smooth"});
body {margin: 0px; display: block; height: 100%; background-image: linear-gradient(red, yellow);}
.start {display: block; margin: 100px 10px 1000px 0px;}
.end {display: block; margin: 0px 0px 100px 0px;}
<div class="start">Start</div>
<div class="end" id="end">End</div>
There are many different methods for smooth scrolling in JavaScript. The most common ones are listed below.
To scroll to a certain position in an exact amount of time, window.requestAnimationFrame can be put to use, calculating the appropriate current position each time. setTimeout can be used to a similar effect when requestAnimationFrame is not supported. (To scroll to a specific element with the function below, just set the position to element.offsetTop.)
/*
#param pos: the y-position to scroll to (in pixels)
#param time: the exact amount of time the scrolling will take (in milliseconds)
*/
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
Demo:
/*
#param time: the exact amount of time the scrolling will take (in milliseconds)
#param pos: the y-position to scroll to (in pixels)
*/
function scrollToSmoothly(pos, time) {
var currentPos = window.pageYOffset;
var start = null;
if(time == null) time = 500;
pos = +pos, time = +time;
window.requestAnimationFrame(function step(currentTime) {
start = !start ? currentTime : start;
var progress = currentTime - start;
if (currentPos < pos) {
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos);
} else {
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time));
}
if (progress < time) {
window.requestAnimationFrame(step);
} else {
window.scrollTo(0, pos);
}
});
}
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 300)">
Scroll To Div (300ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 200)">
Scroll To Div (200ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 100)">
Scroll To Div (100ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 50)">
Scroll To Div (50ms)
</button>
<button onClick="scrollToSmoothly(document.querySelector('div').offsetTop, 1000)">
Scroll To Div (1000ms)
</button>
<div style="margin: 500px 0px;">
DIV<p/>
<button onClick="scrollToSmoothly(0, 500)">
Back To Top
</button>
<button onClick="scrollToSmoothly(document.body.scrollHeight)">
Scroll To Bottom
</button>
</div>
<div style="margin: 500px 0px;">
</div>
<button style="margin-top: 100px;" onClick="scrollToSmoothly(500, 3000)">
Scroll To y-position 500px (3000ms)
</button>
For more complex cases, the SmoothScroll.js library can be used, which handles smooth scrolling both vertically and horizontally, scrolling inside other container elements, different easing behaviors, scrolling relatively from the current position, and more.
var easings = document.getElementById("easings");
for(var key in smoothScroll.easing){
if(smoothScroll.easing.hasOwnProperty(key)){
var option = document.createElement('option');
option.text = option.value = key;
easings.add(option);
}
}
document.getElementById('to-bottom').addEventListener('click', function(e){
smoothScroll({yPos: 'end', easing: easings.value, duration: 2000});
});
document.getElementById('to-top').addEventListener('click', function(e){
smoothScroll({yPos: 'start', easing: easings.value, duration: 2000});
});
<script src="https://cdn.jsdelivr.net/gh/LieutenantPeacock/SmoothScroll#1.2.0/src/smoothscroll.min.js" integrity="sha384-UdJHYJK9eDBy7vML0TvJGlCpvrJhCuOPGTc7tHbA+jHEgCgjWpPbmMvmd/2bzdXU" crossorigin="anonymous"></script>
<!-- Taken from one of the library examples -->
Easing: <select id="easings"></select>
<button id="to-bottom">Scroll To Bottom</button>
<br>
<button id="to-top" style="margin-top: 5000px;">Scroll To Top</button>
Alternatively, you can pass an options object to window.scroll which scrolls to a specific x and y position and window.scrollBy which scrolls a certain amount from the current position:
// Scroll to specific values
// scrollTo is the same
window.scroll({
top: 2500,
left: 0,
behavior: 'smooth'
});
// Scroll certain amounts from current position
window.scrollBy({
top: 100, // could be negative value
left: 0,
behavior: 'smooth'
});
Demo:
<button onClick="scrollToDiv()">Scroll To Element</button>
<div style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
var elem = document.querySelector("div");
window.scroll({
top: elem.offsetTop,
left: 0,
behavior: 'smooth'
});
}
</script>
If you only need to scroll to an element, not a specific position in the document, you can use Element.scrollIntoView with behavior set to smooth.
document.getElementById("elemID").scrollIntoView({
behavior: 'smooth'
});
Demo:
<button onClick="scrollToDiv()">Scroll To Element</button>
<div id="myDiv" style="margin: 500px 0px;">Div</div>
<script>
function scrollToDiv(){
document.getElementById("myDiv").scrollIntoView({
behavior: 'smooth'
});
}
</script>
Modern browsers support the scroll-behavior CSS property, which can be used to make scrolling in the document smooth (without the need for JavaScript). Anchor tags can be used for this by giving the anchor tag a href of # plus the id of the element to scroll to). You can also set the scroll-behavior property for a specific container like a div to make its contents scroll smoothly.
Demo:
html, body{
scroll-behavior: smooth;
}
Scroll To Element
<div id="elem" style="margin: 500px 0px;">Div</div>
Here's my variation:
let MenuItem = function ( _menuItem ) {
// I had a sticky header, so its height had to be taken into account when scrolling
let _header = document.querySelector('.site-header');
let _scrollToBlock = function( e, menuItem ) {
let id = menuItem.getAttribute('href'), // the href attribute stores the id of the block to which the scroll will be
headerHeight = _header.offsetHeight; // determine the height of the header
id = id.replace(/#/, ''); // remove the # sign from the id block
let elem = document.getElementById( id ), // define the element to which we will scroll
top = elem.getBoundingClientRect().top + window.scrollY - headerHeight; // determine the height of the scroll
window.scroll({
top: top,
left: 0,
behavior: 'smooth'
});
},
_addEvents = function() {
_menuItem.addEventListener('click', function (e){
e.preventDefault(); // Disable redirect on click
_scrollToBlock(e, _menuItem);
});
},
_init = function() {
_addEvents();
};
_init();
};
// Initialize the class MenuItem to all links with class .menu__item
document.querySelectorAll('.menu__item').forEach( function(item) {
new MenuItem(item);
} );
Here's the code that worked for me.
`$('a[href*="#"]')
.not('[href="#"]')
.not('[href="#0"]')
.click(function(event) {
if (
location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '')
&&
location.hostname == this.hostname
) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
event.preventDefault();
$('html, body').animate({
scrollTop: target.offset().top
}, 1000, function() {
var $target = $(target);
$target.focus();
if ($target.is(":focus")) {
return false;
} else {
$target.attr('tabindex','-1');
$target.focus();
};
});
}
}
});
`

Categories