Swiper.js autoplay / loop with nested dynamic Alpine.js x-for loop - swiper.js

I've used auto autoplay loop Swiper in many instances where slides are rendered server-side but this is the first time I'm using in conjunction with a client-side framework such as Alpine.js.
I got things working client-side within Alpine... And it works when using the left right navigation buttons, BUT... it does NOT autoplay scroll, due to, what I'm guessing, is the fact that Swiper doesn't know how many slides it has to work with in the Alpine x-for loop?
Is there a way that the number of slides can be set during Swiper init so that it knows how many slides it has to work with and in-turn autoplay? Or am I just missing something?
I've simplified and parsed out my code below for readability sake, just use as an example of what's generally happening with the nested Alpine loop.
<div x-data="{suggestions: [], 'isLoading': true}"
x-init="fetch('/p/items/suggested/?i=123')
.then(response => response.json())
.then(response => {
suggestions = response.suggestions;
isLoading = false;
})">
<div x-data="{swiper: null}"
x-init="swiper = new Swiper($refs.suggcontainer, {
loop: true,
autoplay: {
delay: 2500,
disableOnInteraction: true,
pauseOnMouseEnter: true
},
slidesPerView: 2,
spaceBetween: 8
})">
<div class="swiper" x-ref="suggcontainer">
<div class="swiper-wrapper">
<template x-for="sugg in suggestions" :key="sugg.id">
<a x-bind:href="sugg.url_slug" class="swiper-slide">
<img :src="sugg.image_thumb" :alt="sugg.name" />
</a>
</template>
</div>
</div>
</div>
</div>

Related

Swiper 5 with lazysizes

I need help with lazy load. I am using this swiper here (https://github.com/nolimits4web/swiper)to create a full screen swiper on a website which is set on autoplay and has a fade in effect. To reduce the loading time I also included lazysizes (https://github.com/aFarkas/lazysizes) because with the lazy load within the swiper my captions didn't work. They were always overlapping.
However my issue now is that somehow it loads images even then they are not in the viewpoint. I am pretty new to coding, so please bare with me :) I post my code below and hope that someone can help me out here or explain what I need to do. Thanks so much!
<div class="swiper-container">
<div class="swiper-wrapper">
<picture alt="" class="swiper-slide">
<source class="lazyload" media="(max-width:600px)"
data-srcset="images/Loremipsum_mobile.jpg"
/>
<source class="lazyload"
media="(max-width: 1024px)"
data-srcset="images/Loremipsum.jpg"
/>
<img
class="lazyload"
data-src="images/Loremipsum.jpg"
/>
<div class="image_caption">Titel: Lorem Ipsum</div>
</picture>
</div>
<!-- Add Arrows -->
<div class="swiper-button-next swiper-button-white"></div>
<div class="swiper-button-prev swiper-button-white"></div>
<div class="navbar">
<a href="4s_impressum.html" id="desktop" >Impressum & Datenschutzerklärung</a>
i
</div>
</div>
<!-- Swiper JS -->
<script src="js/swiper.js"></script>
<!-- Lazysizes -->
<script src="js/lazysizes.min.js" async=""></script>
<!-- Initialize Swiper -->
<script>
var swiper = new Swiper('.swiper-container', {
// Disable preloading of all images
preloadImages: false,
// Enable lazy loading
lazyload: true,
loop: true,
effect: 'fade',
// speed: 0,
keyboard: {
enabled: true,
// onlyInViewport: false,
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
autoplay: {
delay: 3000,
//disableOnInteraction: true,
loop: true,
},
});
</script>

Vue Animation Script misses the timing

I have a web api project and I want to play an animation on one of my html spans.Specificly the vue returns a number and I want that number to animate increasing from 00.00% up to its value.It actually works fine with a js script that I found online if I delete a part of my html but it doesnt work and the numbers appears as 00.00 if I use my entire HTML Code.
Here is my code:
<html>
<body>
//This is the part that I want to work but appears as 00.00
<span class="Animation">{{number}}</span>%
//This is the part that if I delete it the upper part works but I want both parts in my project
<div v-for="item in items">
<h3>{{item1.name1}}</h3>
<h5 class="prediction-2">Score:{{item2.name2}}%</h5>
<a v-bind:href="'test.html?id=' + item.other_id" class="btn">
<span style="color:white;">click here</span>
</a>
</div>
<script>
$('.Animate').each(function () {
var $this = $(this);
jQuery({
Counter: 0
}).animate({
Counter: $this.text()
}, {
duration: 5000,
easing: 'swing',
step: function () {
$this.text(this.Counter.toFixed(2));
}
});
});
</script>
</body>
</html>
````````````````````
Your Vue markup is broken. Because it errors it blocks the execution of JavaScript in your page and one of the side effects is the animator doesn't run anymore.
This code is invalid:
<div v-for="item in items">
<h3>{{item1.name1}}</h3>
<h5 class="prediction-2">Score:{{item2.name2}}%</h5>
<a v-bind:href="'test.html?id=' + item.other_id" class="btn">
<span style="color:white;">click here</span>
</a>
</div>
It loops through items defining item for each iteration but then tries to use item1 and item2 (which are not defined).
My advice is to exclude the animator from your question (as it clearly works as intended when there are no errors in the page) and rewrite your question by describing what the Vue code should do.
An example of how a valid v-for looks like:
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({
items: [{
name: 'Item 1',
prediction: 56,
other_id: 101
}, {
name: 'Item 2',
prediction: 82,
other_id: 102
}]
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="item in items">
<h3>{{item.name}}</h3>
<h5 class="prediction-2">Score: {{item.prediction}}%</h5>
<a :href="'test.html?id=' + item.other_id" class="btn">
<span>click here</span>
</a>
</div>
</div>
As you can see, it's looping through each item and rendering them according to each of their values. You'll also notice the links are correct. Without more definition on what the code should actually do/render, I can't help you more.

jQuery UI Draggable & Droppable in Vue JS Component with Dynamic Data

I have a view component where I want to be able to drag certain elements (.draggable) onto other certain elements (.droppable) like this:
I can set it up for static <div> elements in mounted() like this:
mounted(){
$('.draggable').draggable({
revert: true
})
$('.droppable').droppable({
hoverClass: 'drop-hover',
drop: function(){
console.log('-= dropped =-')
}
})
}
...
<div class="droppable">Drop Here</div>
<div class="draggable">Drag Me</div>
<div class="draggable">Drag Me</div>
<div class="draggable">Drag Me</div>
But when I want to do this on dynamically in Vue JS with v-for then the jQuery UI bindings seem to get lost:
<div v-for="(item, index) in items">
<div class="droppable">{{ item.name }}</div>
<div v-for="(folder, index) in item.folders" class="draggable">{{ folder.name }}</div>
</div>
I can no longer drag the .draggable items.
As I understand it, jQuery Draggable/Droppable does not work with .on since I suspect one of the issues is that my DOM elements are generated after jQuery is initialized. I tried wrapping my jQuery code in this.$nextTick(() => { ... }) but it still doesn't work.
I have also looked at Vue.Draggable but I want the drag and drop without the simultaneous reordering.
Is it possible to combine jQuery Draggable/Droppable with dynamic data in Vue JS?

Magnific Popup fails to work with multiple galleries on a page

I am using magnific popup on a new website that i am building but i am running into a problem getting it to work when multiple galleries on the same page.
I'm using wordpress, Slick Slider to display a slider full of thumbnails and when one of the thumbnails is clicked it should open the larger version of the image in the Magnific Popup.
My HTML looks like this:
<div class="media-carousel slick-initialized slick-slider" id="carousel-links">
<div class="slick-list draggable" tabindex="0">
<div class="slick-track" style="opacity: 1; width: 1152px; transform: translate3d(0px, 0px, 0px);">
<div class="slick-slide slick-active" data-slick-index="0" style="width: 288px;">
<div class="standard_media_element media_title">
<a href="image1-large.jpg" title="" class="gallery-item" data-effect="mfp-move-horizontal">
<img src="image1.jpg" alt="" class="standard-image"></a>
</div>
</div>
<div class="slick-slide slick-active" data-slick-index="1" style="width: 288px;">
<div class="standard_media_element media_title">
<a href="image2-large.jpg" title="" class="gallery-item" data-effect="mfp-move-horizontal">
<img src="image2.jpg" alt="" class="standard-image"></a>
</div>
</div>
<div class="slick-slide slick-active" data-slick-index="2" style="width: 288px;">
<div class="standard_media_element media_title">
<a href="image3-large.jpg" title="" class="gallery-item" data-effect="mfp-move-horizontal">
<img src="image3.jpg" alt="" class="standard-image"></a>
</div>
</div>
<div class="slick-slide slick-active" data-slick-index="3" style="width: 288px;">
<div class="standard_media_element media_title">
<a href="image4-large.jpg" title="" class="gallery-item" data-effect="mfp-move-horizontal">
<img src="image4.jpg" alt="" class="standard-image"></a>
</div>
</div>
</div>
</div>
I have a site.js file that contains all of the javascript for my site in it. I'm calling magnific popup using the following function. I've added some extra stuff in it to add effects.
jQuery('#carousel-links').each(function() {
jQuery(this).magnificPopup({
delegate: 'a',
type: 'image',
tClose: 'Close (Esc)',
tLoading: '',
preload: [1,4],
gallery:{
enabled:true,
tPrev: 'previous',
tNext: 'next',
tCounter: '%curr% of %total%'
},
image: {
verticalFit: true,
titleSrc: function(item) {
return item.el.attr('title') + ' · <a class="image-source-link" href="'+item.src+'" target="_blank"><i class="fa fa-file-image-o"></i> open original</a>';
}
},
closeBtnInside: false,
closeOnContentClick: false,
mainClass: 'mfp-zoom-in',
removalDelay: 300, //delay removal by X to allow out-animation
callbacks: {
lazyLoad: function (item) {
console.log(item); // Magnific Popup data object that should be loaded
},
beforeOpen: function() {
jQuery('#carousel-links a').each(function(){
jQuery(this).attr('title', $(this).find('img').attr('alt'));
});
},
open: function() {
//overwrite default prev + next function. Add timeout for css3 crossfade animation
jQuery.magnificPopup.instance.next = function() {
var self = this;
self.wrap.removeClass('mfp-image-loaded');
setTimeout(function() { jQuery.magnificPopup.proto.next.call(self); }, 120);
}
jQuery.magnificPopup.instance.prev = function() {
var self = this;
self.wrap.removeClass('mfp-image-loaded');
setTimeout(function() { jQuery.magnificPopup.proto.prev.call(self); }, 120);
}
},
imageLoadComplete: function() {
var self = this;
setTimeout(function() { self.wrap.addClass('mfp-image-loaded'); }, 16);
}
}
});
});
I have the same exact HTML code building different galleries outputting on the same page and for some reason the first carousel with lightbox works great. But for whatever reason, the second gallery does not work. The carousel works fine, but Magnific Popup does not fire. When i click on one of the thumbnails, it opens the larger image in the browser window.
I tested my js function by adding a "console.log("clicked");" after the .each. I see the console that the thumbnail is being clicked and that part is working.
Any idea how i can get the multiple sliders to work on my page?
I've tried the example from the documentation for the multiple galleries by removing everything from the function and making it as barebone as possible. I get the same result, the first lightwindow works but the second one does not.
I'm not seeing any js errors on the console either.
UPDATE #1
This isn't an ideal solution, but i did find a way to make this work. I had to use unique IDs on each slider in order to get the lightbox to work if multiple sliders were on the same page using the same ID value. In this case, I was using #carousel-links for all of the sliders. I guess you can't share the same ID for sliders across the board and get this lightbox to work?
I also moved the javascript code to be right after the carousel in the module template page. I removed it for now from my site.js file.
I'm not looking for a way to optimize the code so i can put it in my site.js. I'll try to grab that ID value by using the class on each carousel. I'll report back if i can get that to work.
UPDATE #2 - OMG
Could the problem i was having when the lightbox only worked with the first instance of the lightbox be because i was an ID instead of a CLASS as my selector for the lightbox? I changed to the CLASS and now they are all working.
What fixed my problem is I changed the selector from the ID to a class on the same element. This allowed for multiple galleries on the same page to work together with no problem.

dijit widgets inside a custom widget -- handling their events

JS:
define(["dojo/_base/declare","dojo/dom",
"dijit/_Widget", "dijit/_TemplatedMixin",
"dojo/text!./templates/MainViewWidget.html",
"dijit/layout/TabContainer", "dijit/layout/ContentPane","dijit/layout/BorderContainer","dijit/form/TextBox", "dijit/layout/AccordionContainer"],
function(declare, dom, _Widget, _TemplatedMixin, template){
return declare("package.MainViewWidget", [_Widget, _TemplatedMixin], {
widgetsInTemplate: true,
templateString: template,
constructor: function(){
},
startup: function(){
},
search: function(evt){
alert('hi');
alert(evt);
}
});
});
templates/MainViewWidget.html:
<div class="mainContainer">
<div data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design:'sidebar', gutters:true, liveSplitters:true" style="width:100%;height:100%;">
<div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'left', splitter:true">
<h2>List of trips</h2>
<br />
<input type="text" data-dojo-type="dijit.form.TextBox" data-dojo-props="placeHolder:'Search...'" data-dojo-attach-event="onchange:'search'"/>
<br />
</div>
<div data-dojo-type="dijit.layout.TabContainer" data-dojo-attach-point="tabContainerDiv" data-dojo-props="region: 'center', tabPosition: 'top', tabStrip:'true', style:'width:80%;height:100%'">
<div data-dojo-type="dijit.layout.ContentPane" title="Summary" data-dojo-props="selected:'true', title:'About'">Welcome. Navigate through the Left pane.</div>
</div>
</div>
</div>
The thing is, I want to capture events on the TextBox. I was looking to do this with just markup as you can see from data-dojo-attach-event="onchange:'search'". I have tried many variations of this and I can't get it to work. Basically what I want is to define a function in JS and attach it as handler in the markup. Please help.
This isn't supported, sadly. I just spent about two hours discovering this. Any node in your template with a data-dojo-type attribute is ignored by _TemplatedMixin._attachTemplateNodes (see line 177). In other words, data-dojo-attach-event will only bind to plain DOM nodes (not Dijits).
This at least applies to v1.8. The attach processing is different in v1.9 (there's an _AttachMixin), so it might work there.
Try:
data-dojo-attach-event="onChange:search"
Camelcase onChange, and no quotes around search.