I'm using iDangero.us Swiper js for a webpage, and initialization code is as following:
var mySwiper = new Swiper( '.swiper-container', {
direction: 'horizontal',
loop: true,
speed: 600,
nextButton: '.slider-control-next',
prevButton: '.slider-control-prev',
} );
And I need to get current slider index and total count of sliders. Swiper API provides mySwiper.activeIndex property and mySwiper.slides but the problem is that when loop is true they don't give correct index and count.
Is there any way to get these numbers correctly when loop is true?
As of May 2016 they have added the realIndex property!
Things to be aware of: 1.) the realIndex property is returned as a string instead of an integer (just in case you need to do math with it) 2.) the realIndex property starts with 0(as it should), unlike activeIndex in loop mode which in my case started with 1
https://github.com/nolimits4web/Swiper/pull/1697
The number of slides, and thus sometimes the activeIndex, is "wrong" by design when loops are involved: https://github.com/nolimits4web/Swiper/issues/1205
Best way I could find to get the total number of slides is:
mySwiper.slides.length - 2
You could use that to get the current index (this one is zero-based):
(mySwiper.activeIndex - 1) % (mySwiper.slides.length - 2)
This is not ideal, of course. You could open a GitHub issue and propose adding more convenient ways of accessing these values.
Just adding yet another answer, since Swiper hasn't included the realIndex property yet. Here is a nice little way of getting the real index when looping, without subtracting a hard coded number (which might easily change).
var realIndex = e.slides.eq(e.activeIndex).attr('data-swiper-slide-index');
Used like this:
var slider = new Swiper('.swiper-container');
slider.on('slideChangeEnd', function(e) {
var realIndex = e.slides.eq(e.activeIndex).attr('data-swiper-slide-index');
// do whatever
});
Although this question has been answered already, I thought I'd add my working code based off the accepted answer.
Main issue I had with a looping gallery, is if you go back from the first slide, the current slide reads as 0. Possibly because it's a clone?
Anyway, here's a stripped-back (slightly untested) working solution:
(function($) {
'use strict';
var gallery;
gallery = $('#gallery').swiper({
parallax: false,
initialSlide: 0,
direction: 'horizontal',
loop: true,
autoplay: 5000,
autoplayDisableOnInteraction: false,
speed: 700,
preventClicks: true,
grabCursor: true,
});
var totalSlides = gallery.slides.length - 2;
$('#total').html(totalSlides);
gallery.on('slideChangeEnd', function(instance) {
var currentCount = (instance.activeIndex - 1) % (totalSlides) + 1;
if(currentCount === 0) {
$('#current').html(totalSlides);
} else {
$('#current').html(currentCount);
}
});
})(jQuery);
Use the above to display current and total slides on your page. Obviously adjust the ID's in your HTML accordingly.
I would think this value of the actual index value should be available in the Swiper API, although it's nowhere to be found, so for now you'll have to roll your own function to get that value.
This function (tested and works) was provided to me in this thread on the Swiper GitHub Issues page: Need a way to get the accurate activeIndex in loop mode
In loop mode active index value will be always shifted on a number of looped/duplicated slides. you can get attribute 'data-swiper-slide-index' with a function like:
function getSlideDataIndex(swipe){
var activeIndex = swipe.activeIndex;
var slidesLen = swipe.slides.length;
if(swipe.params.loop){
switch(swipe.activeIndex){
case 0:
activeIndex = slidesLen-3;
break;
case slidesLen-1:
activeIndex = 0;
break;
default:
--activeIndex;
}
}
return activeIndex;
}
Usage:
var mySwiper = new Swiper('.swiper-container', {
direction: 'vertical',
loop:true,
onSlideChangeEnd:function(swipe){
console.log(getSlideDataIndex(swipe))
}
});
this work in both modes, loop or not
var $slideActive = $('.swiper-slide-active');
var realIndex = $slideActive.data('swiper-slide-index');
if(typeof realIndex === 'undefined') {
realIndex = $slideActive.index();
}
also, the number of total slides in both modes:
var totalSlides = $('.swiper-slide:not(.swiper-slide-duplicate)').length;
update in 2020:
Say you are using ionic angular:
<ion-slides #slider [options]="slideOps" (ionSlideDidChange)="changeSlide($event)">
Then in your typescript:
#ViewChild('slider', {static: true}) slider: IonSlides;
changeBoss(e){
let realIndex=e.target.swiper.realIndex;
console.log(realIndex);
}
This will give you the real index
The simplest way I found is to simply count the number of .swiper-slide before Swiper initialize code runs (and duplicates the slides).
var numOfSlides = document.querySelectorAll(".swiper-slide").length;
<!-- swiper 6 CSS -->
<link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.min.css">
<h4>slider 1</h4>
<!-- Swiper -->
<section class="swiper-container" data-swiper="projects">
<div class="swiper-wrapper">
<!-- slide -->
<a href="#" class="swiper-slide">
<h3>
Slide 1
</h3>
</a>
<!-- slide -->
<a href="#" class="swiper-slide">
<h3>
Slide 2
</h3>
</a>
<!-- slide -->
<a href="#" class="swiper-slide">
<h3>
Slide 3
</h3>
</a>
</div>
<!-- If we need navigation buttons -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</section>
<!-- swiper 6 JS -->
<script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script>
<!-- swiper JS Initialize -->
<script>
var numOfSlides = document.querySelectorAll(".swiper-slide").length;
console.log("numOfSlides: " + numOfSlides);/* 3 */
var my_swiper = new Swiper('.swiper-container', {
slidesPerView: 3,
spaceBetween: 12,
// Navigation arrows
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
loop: true,
});
</script>
Related
I have this small project I'm working at, and it's my first time ever using KnockoutJS (and a long while since I used javascript).
Any javascript carousel works with foreach, until the array is updated. I have already tried using Glider, Slick and Owl plugins and they all end up doing the same thing:
Document starts, foreach initiates, populates the carousel with cards fetched from URL. OK
Using the <select>, I change the option which will return a different API URL based on it. OK
The foreach is restarted, the new content is thrown in the data-bind, but the last carousel remains in HTML, working (?)
The new content doesn't work well with the carousel, as the items show stacked on top of each other.
The fourth step actually happened long before I understood what happened with carousels and lifecycle from KO; I had to use a handleBinding to start the plugin function after the foreach was made. Problem is, when the foreach is updated, KO won't restart the whole view, just what's inside of it, so the handleBinding is ignored.
Also, I can't explain why, in the 3rd step, the last carousel keeps there.
Code:
Select
<div id="select-regiao" data-bind="with: localizacoes" class="form-group mb-0">
<select id="selected-option" data-bind="options: planosPorLocalizacao, value: planosSelecionados" class="custom-select select-oi mr-2">
</select>
</div>
Carousel, foreach, card...
<div class="owl-carousel owl-theme" data-bind="foreach: planos, carousel">
<!-- #region card do plano -->
<div class="mt-4 m-2">
<div class="card card-plano">
[... card content... ]
</div>
</div>
<!-- #endregion -->
</div>
Before </body>
<script src="assets/js/jquery-3.3.1.slim.min.js" type="text/javascript"></script>
<script src="assets/js/popper.min.js" type="text/javascript"></script>
<script src="assets/js/bootstrap.min.js" type="text/javascript"></script>
<script src='assets/bower_components/knockout/dist/knockout.js' type='text/javascript'></script>
<script src="assets/js/ko-models.js" type="text/javascript"></script>
<script src="assets/js/owl-2-2.3.4/dist/owl.carousel.min.js"></script>
My ko-models.js:
$(document).ready(function() {
apiUrl = "link";
localizacoes = {
planosPorLocalizacao: ["Todos", "Rio de Janeiro, RJ", "São Paulo, SP"],
planosSelecionados: ["Todos"]
};
planos = [];
var vm = null;
var select = document.getElementById("selected-option");
select.addEventListener("change", function() {
if (select.value == "Rio de Janeiro, RJ") {
apiUrl = "other link";
} else if (select.value == "São Paulo, SP") {
apiUrl = "other link";
} else if (select.value == "Todos") {
apiUrl = "link";
}
console.log(select.value);
MostraPlanos();
});
function MostraPlanos() {
fetch(apiUrl).then(function(next) {
next.json().then(function(res) {
// ko.cleanNode({planos: res});
res.forEach(el => {
el.dependentePreco = el["dependente-preco"];
el.precoReal = el["preco"].split(",")[0];
el.precoCentavo = el["preco"].split(",")[1];
});
planos = res;
if(vm == null) {
vm = {
planos: ko.observable(planos),
localizacoes
};
ko.applyBindings(vm);
}
else{
vm.planos(planos);
vm.gliderCarousel();
}
});
});
}
MostraPlanos();
ko.bindingHandlers.carousel = {
update: function() {
$(".owl-carousel").owlCarousel({
loop:true,
margin:10,
nav:true,
responsive:{
0:{
items:1
},
600:{
items:3
},
1000:{
items:5
}
}
});
}
}
});
I found out the problem was the way the plugins used to track the carousel. Glider, for instance, was the last one I tried and spent most time at.
From the possible options in Glider.js:
addTrack
Type: Boolean
Default: true
Whether or not Glider.js should wrap it's children with a
'glider-track' .
NOTE: If false, Glider.js will assume that the
'glider-track' element has been added manually. All slides must be
children of the track element.
Adding .glider-track to the outer div (parent of cards) and setting addTrack: false in JS solved the issue!
I have slidesPerView: 3 and slidesPerGroup: 3 initialised, and I have a class called .section-xx-start to separate each 'section' within a timeline sort of a carousel, and everytime the slide with the class .section-xx-start is in the view (no matter which position, 1st 2nd or 3rd), the background colour changes. I can't seem to find a parameter/method for this. This is what I have so far:
<div class="swiper-slide section-one-start">
...
</div>
<div class="swiper-slide">
...
</div>
<div class="swiper-slide">
...
</div>
<div class="swiper-slide">
...
</div>
<div class="swiper-slide">
...
</div>
<div class="swiper-slide section-two-start">
...
</div>
<script>
var mySwiper = new Swiper ('.swiper-container', {
slidesPerView: 3,
slidesPerGroup: 3,
spaceBetween: 50,
grabCursor: true,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
}
});
const nextButton = document.querySelector('.swiper-button-next');
const prevButton = document.querySelector('.swiper-button-prev');
function changeBgColor() {
if(slideWithClassIsInViewPort) {
this.style.backgroundColor = "var(--navy)";
} else {
//do nothing
}
}
nextButton.addEventListener('click', changeBgColor);
prevButton.addEventListener('click', changeBgColor);
</script>
I am just unsure on how to apply that "slideWithClassIsInViewPort" part. If there's any other suggestions to improve my code, please suggest! Appreciate any help!
Edit: Visit (deleted) to see slider.
You could just check the display state for the current slide, if you are using the CSS rule display to hide/show slides.
Example:
if(slide.style.display ='block'){
// do whatever
}
Edit:
Im not sure if you already fixed it or not, but in case you didnt, here is a code in your main.js file which logs the current index of the slider to the console.
const button = document.querySelector('.swiper-button-next');
function consoleLog() {
console.log(mySwiper.activeIndex);
}
button.addEventListener('click', consoleLog);
When clicking next in your slide, i assume the number of columns it has showed appears. And since each slide shows 3 columns each, the index probly works around that, there of 3, 6, 9, 12 etc. So to set the background different or whatever you are trying to do, for each slide, you could do something like this:
Example:
var currentIndex = mySwiper.activeIndex;
function changeBg(){
if(currentIndex == 3) // second slide {
slider.style.background='white';
}
if(currentIndex == 6) // third slide {
slider.style.background='blue';
}
button.addEventListener('click', changeBg);
There is probaly better ways to do it, but i dont have much time right now so thats the best i could do.
I have a slick slider on my site and its working.
What i need is to add a custom class to each item when the carousel renders.
ex: This is one done with owl carousel
if you use the following jQuery here:
http://codepen.io/OwlFonk/pen/dpjhB
jQuery(document).ready(function () {
var carousel = jQuery("#owl-demo");
carousel.owlCarousel({
itemsCustom: [[0,5]],
navigation : false, autoPlay : 10000, mouseDrag : true, touchDrag : true, pagination : false, rewindNav: true,
afterAction: function(el){
//remove class active
for ( var i = 0; i < 10; i++ )
{
this.$owlItems.removeClass('active' + i)
}
//add class active
for ( var i = 0; i < 10; i++ )
{
this.$owlItems.eq(this.currentItem + i).addClass('active' + i)
}
}
});
});
you will be able to see what i need.
I cant seem to get it working with Slick.
The reason im using Slick and not Owl is because Owl doesnt have infinite scrolling and Owl 2 is too buggy.
I would consider using Owl 2 if i can do the same as above but they changed the script so much i cant get it working.
I am working with the glide.js library to make an image slider on my website. I would like to have three pre made buttons to act as the slider buttons instead of the default navigation. The default nav seems to be using <a> tags.
Looking through the js file It seems the default navigation is created here:
Glide.prototype.navigation = function() {
this.navigation.items = {};
//CLASS
// Navigation wrapper
this.navigation.wrapper = $('<div />', {
'class': this.options.navigationClass
}).appendTo(
/**
* Setting append target
* If option is true set default target, that is slider wrapper
* Else get target set in options
* #type {Bool or String}
*/
(this.options.navigation === true) ? this.parent : this.options.navigation
);
//Navigation controls
for (var i = 0; i < this.slides.length; i++) {
this.navigation.items[i] = $('<li />', {
'href': '#',
'class': this.options.navigationItemClass,
// Direction and distance -> Item index forward
'data-distance': i
}).appendTo(this.navigation.wrapper);
}
// Add navCurrentItemClass to the first navigation item
this.navigation.items[0].addClass(this.options.navigationCurrentItemClass);
// If centered option is true
if (this.options.navigationCenter) {
// Center bullet navigation
this.navigation.wrapper.css({
'left': '50%',
'width': this.navigation.wrapper.children().outerWidth(true) * this.navigation.wrapper.children().length,
'margin-left': -(this.navigation.wrapper.outerWidth(true)/2)
});
}
};
I adjusted the code, I replaced the loop with the code below to use 3 buttons I placed on my html page but it has no effect. I'm just wondering if I am doing something wrong, or if it is even possible? This is the changes I made to the code:
this.navigation.items[0] = $('.b1', {
'href': '#',
'class': this.options.navigationItemClass,
'data-distance': 0
}).appendTo(this.navigation.wrapper);
this.navigation.items[1] = $('.b2', {
'href': '#',
'class': this.options.navigationItemClass,
'data-distance': 1
}).appendTo(this.navigation.wrapper);
this.navigation.items[2] = $('.b3', {
'href': '#',
'class': this.options.navigationItemClass,
'data-distance': 2
}).appendTo(this.navigation.wrapper);
Does anyone have any idea how I might implement this?
I just solved the issue. Might be helpful to anyone trying to do the same thing. It was very easy, I don't know how I didn't figure it out initially.
Basically initialize the slider as follows:
$('.slider').glide({
autoplay: 5000,
arrows: 'none',
navigation: 'none'
});
Get an instance of the API:
var glide = $('.slider').glide().data('api_glide');
Then get references to each button and code the required action you want to execute when the button is clicked:
$('.b1').click(function(){
console.log("Button 1 Clicked");
glide.jump(1, console.log('1'));
});
$('.b2').click(function(){
console.log("Button 2 Clicked");
glide.jump(2, console.log('2'));
});
$('.b3').click(function(){
console.log("Button 3 Clicked");
glide.jump(3, console.log('3'));
});
All of this assumes you've got three buttons on your page like so:
<button class="b1" id="b1" name="b1" >Button 1</button>
<button class="b2" id="b2" name="b2">Button 2</button>
<button class="b3" id="b3" name="b3">Button 3</button>
Picked ui datepicker as calendar and used cluetip to show events. Script is working until I change the month (push button <- or ->).
Main idea was to set title to the element that holds date and on hover show & split text in lines using cluetip.
EDIT: Here is example - hope it will help to understand my problem.
Here is the javascript code:
$(document).ready(function() {
var dates =[ // adding events
[new Date(2010,8,01),new Date(2010,8,03),"Event1 | text | next line"]
];
$('#calendar').datepicker({
beforeShowDay: highlightEvents,
});
function highlightEvents(date) {
for (var i = 0; i < dates.length; i++) {
if (dates[i][0] <= date && dates[i][2] >= date) {
return [true, 'odd', dates[i][2]]; } // odd == style
}
$('td.odd').cluetip({ // cluetip main function
splitTitle: '|',
cluetipClass: 'jtip',
arrows: true,
dropShadow: true,
});
});
Html code:
<div id="calendar"></div>
Thanks in advance!
Thanks to UberNeet post:
jQuery UI Datepicker with jQuery tipsy
Found the answer.
// Because the the event `onChangeMonthYear` get's called before updating
// the items, we'll add our code after the elements get rebuilt. We will hook
// to the `_updateDatepicker` method in the `Datepicker`.
// Saves the original function.
var _updateDatepicker_o = $.datepicker._updateDatepicker;
// Replaces the function.
$.datepicker._updateDatepicker = function(inst){
// First we call the original function from the appropiate context.
_updateDatepicker_o.apply(this, [inst]);
// No we can update the Tipsys.
addTipsys(inst.drawYear, inst.drawMonth+1, inst.id);
};
Hope this will help someone.