I'm looking for a combination of Swiper slider and Photoswipe (Or other lightbox).
Trying to make a product slider with 3 products in a slide.
Each product has a lightbox/modal with video and a gallery. The modals are generated within the boundaries of the product div. When you click an 'open gallery' / 'show video' link. The lightbox opens fullscreen.
The problem I'm having is: the lightbox won't (but has to) exceed the boundary of the slider product boundary.
Looking for a solution.
Something like an empty modal/lightbox containers outside the slider with dynamic content when an 'open modal' link is clicked within the product slide.
You can check it, here is example:
<header>
<h1>
<a title="swiper.js" href="http://idangero.us/swiper/" target="_blank">Swiper.js (5.3.7)</a> &
<a title="photoswipe" href="http://photoswipe.com/" target="_blank">Photoswipe.js (4.1.3)</a> - Mobile Native feel
slider gallery
</h1>
<p>Combine two of the most powerfull JS plugins (Endless options / Great docs / Fast / Modern / Mobile freindly) -
<a title="swiper.js" href="http://idangero.us/swiper/" target="_blank">SWIPER</a> IS PERFECT FOR THIS IDEA BEACUSE OF
ITS unique <code>preventClicks</code> Parameter (Prevent accidental unwanted clicks on links during swiping) -
<strong>Works like magic</strong>. Also its really <b>hard</b> to find - Code example of working photoswipe
combination with any slider out there(slick, flickity, owl etc.) and
in general slider & lightbox - so i hope this example be usefull for you.</p>
</header>
<!-- https://swiperjs.com/get-started/ -->
<!-- Slider main container -->
<div class="swiper-container">
<!-- Additional required wrapper -->
<ul class="swiper-wrapper my-gallery" itemscope itemtype="http://schema.org/ImageGallery">
<!-- Slides -->
<li id="1" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a id="first" title="click to zoom-in" href="https://picsum.photos/id/1079/1200/600" itemprop="contentUrl" data-size="1200x600">
<img src="https://picsum.photos/id/1079/1200/600" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="2" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="http://placehold.it/1200x601/AB47BC/ffffff?text=Zoom-image-2"
itemprop="contentUrl" data-size="1200x601">
<img src="http://placehold.it/600x300/AB47BC/ffffff?text=Thumbnail-image-2" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="3" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="http://placehold.it/1200x600/EF5350/ffffff?text=Zoom-image-3" itemprop="contentUrl" data-size="1200x600">
<img src="http://placehold.it/600x300/EF5350/ffffff?text=Thumbnail-image-3" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="4" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="http://placehold.it/1200x600/1976D2/ffffff?text=Zoom-image-4" itemprop="contentUrl" data-size="1200x600">
<img src="http://placehold.it/600x300/1976D2/ffffff?text=Thumbnail-image-4" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="5" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="https://picsum.photos/id/1011/1200/600" itemprop="contentUrl"
data-size="1200x600">
<img src="https://picsum.photos/id/1011/1200/600" itemprop="thumbnail" alt="Image description" />
</a>
</li>
</ul>
<!-- Add Pagination -->
<div class="swiper-pagination"></div>
<!-- If we need navigation buttons -->
<div title="Prev" class="swiper-button-prev"></div>
<div title="Next" class="swiper-button-next"></div>
</div>
<!-- https://photoswipe.com/documentation/getting-started.html -->
<!-- add PhotoSwipe (.pswp) element to DOM -
Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element, as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
<!-- Container that holds slides. PhotoSwipe keeps only 3 slides in DOM to save memory. -->
<!-- don't modify these 3 pswp__item elements, data is added later on. -->
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title="Share"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
<!-- ////////////////////////
DOCS
////////////////////////////
-->
<!-- Include Tippy -->
<script src="https://unpkg.com/tippy.js@3/dist/tippy.all.min.js"></script>
<!-- OPTIONAL: Set the defaults for the auto-initialized tooltips -->
<script>
tippy('.swiper-button-prev', {
content: "Prev",
theme: "light",
arrow: true,
})
tippy('.swiper-button-next', {
content: "Next",
theme: "light",
arrow: true,
})
</script>
<section id="docs">
<br>
<br>
<br>
<hr>
<h2>Noted / Important</h2>
<ol>
<li>
<h3>
A non-jQuery dependent
</h3>
</li>
<li>
<h3>Cdns</h3>
<h4>Head (CSS)</h4>
<code>
<!-- photoswipe CSS -->
<br>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.css" />
<br>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/default-skin/default-skin.min.css" />
<br>
<!-- swiper CSS -->
<br>
<link rel="stylesheet" href="https://unpkg.com/swiper/css/swiper.min.css" />
</code>
<h4>Before body (JS)</h4>
<code>
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.js"></script>
<br>
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe-ui-default.min.js"></script>
<br>
<script src="https://unpkg.com/swiper/js/swiper.min.js"></script>
<br>
<!-- copy-paste the code under codepen js tab her (wrap with script tag) -->
</code>
</li>
<li>
<h3>Loop & Counter</h3>
<p>
<strong> Wont work well </strong> with swiper: <code>loop = true;</code> & photoswipe: <code>counterEl:
true,</code>(What is counter? example: 1/5...2/5) - "loop" duplicate images - the photoswipe counter will be
wrong. *** If you dont want a
loop - you can set photoswipe counter <code>counterEl: true,</code>
</p>
</li>
<li>
<h3><a target="_blank" href="https://schema.org/ImageGallery">Schema.org - ImageGallery</a> Markup (0 ERRORS 0 WARNINGS )</h3>
<p>
<a href="">Schema.org</a> markup + semantic HTML: use unordered (bulleted) list (If you want a <code>div</code> under photoswipe - change JS -
<strong>"(find) control+f-->"</strong> tagname value) . Copy-paste - this code to check: <a target="_blank"
href="https://search.google.com/structured-data/testing-tool">Structured Data Testing Tool - Google</a>
</p>
</li>
<li>
<h3>Match index - BY API</h3>
<p>
<strong>
Extra CODE "match index"
</strong> - EXAMPLE: When you click(zoom) image1 -- goes to image 2 - close image2 (X) - also the swiper update
is position (<strong>BETTER</strong> User Experience) (find<kbd>(ctr +f)</kbd>-->
<code>mySwiper.slideTo(getCurrentIndex, false);</code>) -
This idea miss
in most slider & lightbox examples/plugins mixed.
<br>
Very simple code idea (100% API solution) - get photoswipe index (for example 2) and swiper slideTo index (2 - in this example).
<ul>
<li >
<a target="_blank" href="https://photoswipe.com/documentation/api.html">Photoswipe API - <strong>pswp.getCurrentIndex()</strong></a>
<li style="border-top-width: 0;"> <a target="_blank" href="https://swiperjs.com/api/#methods">Swiper API - <strong>slideTo(index);</strong></a>
</li>
</li>
</ul>
</p>
</li>
<li>
<h3>Photoswipe options</h3>
<p>
JS - line (find) -ctr +f --> the term:<code>// define options (if needed)</code>. You find endless options for
<strong>photoswipe</strong> - This is the place to add/modify options. Full Options list her
<a href="http://photoswipe.com/documentation/options.html" target="_blank">PhotoSwipe
Options</a>
</p>
</li>
<li>
<h3>SWIPER options</h3>
<h4>slideperview</h4>
<p>
<code>slideperview</code> - option1: Set number (1,2,3 and so on) <a
href="http://idangero.us/swiper/demos/110-slides-per-view.html" target="_blank"> - example </a> |||||
option2(<b>"Carousel Mode"</b> this example): Set to "<code>auto</code>"
than add CSS <a href="https://www.w3schools.com/cssref/pr_dim_width.asp" target="_blank">width
Property</a></code> <code>.swiper-slide</code> (in thie case eash slide is 88% width) - <a
href="http://idangero.us/swiper/demos/120-slides-per-view-auto.html" target="_blank">example</a>.
</p>
<h4>spaceBetween & centeredSlides</h4>
<p>
Space Between slide by js option <code>spaceBetween</code> - and also usefull to change
<code>centeredSlides</code>(true/flase). <br>
<a href="http://idangero.us/swiper/api/" target="_blank">Swiper API</a>
</p>
</li>
</ol>
<hr>
<h3>Related Example</h3>
<p>
<a title="FancyBox3 & Flickity" href="https://codepen.io/ezra_siton/pen/OQmjoq" target="_blank">#FancyBox3 -
lightbox & Flickity Slider</a>
</p>
</section>
/* zero custom styles for photoswipe */
/*==================================
SWIPER - minimal styling
===================================*/
/* semantic HTML - remove bullet and space from the list */
ul.swiper-wrapper {
list-style-type: none;
margin: 0;
padding: 0;
}
/* Swiper container */
.swiper-container {
max-width: 100%;
}
/* swiper responive image */
.swiper-container img {
width: 100%;
height: auto;
}
.swiper-slide {
/* Remove this if you want 1 slide perview - than change slidesPerView js-option to 1 -or- 2+ instead of 'auto' */
width: 80%;
}
/* Swiper custom pagination */
.swiper-pagination-bullet {
width: 34px;
height: 34px;
text-align: center;
line-height: 34px;
font-size: 14px;
color: #000;
opacity: 1;
background: rgba(0, 0, 0, 0.3);
transition: background-color 0.5s ease, color 0.5s ease;
}
/* Swiper custom pagination */
.swiper-pagination-bullet:hover {
transition: background-color 0.5s ease;
background: rgba(0, 0, 0, 1);
color: white;
}
/* Swiper custom pagination active state */
.swiper-pagination-bullet-active {
color: #fff;
background: black;
}
/*==================================================================
CODEPEN STYLES -(under codepen gear/setting icon)
==============================================++++++================*/
.tippy-tooltip.light-theme {
color: #26323d;
box-shadow: 0 0 20px 4px rgba(154, 161, 177, 0.15),
0 4px 80px -8px rgba(36, 40, 47, 0.25),
0 4px 4px -2px rgba(91, 94, 105, 0.15);
background-color: white;
}
.tippy-backdrop {
background-color: white;
}
.tippy-roundarrow {
fill: white;
}
/* Default (sharp) arrow */
.tippy-popper[x-placement^='top'] .tippy-tooltip.light-theme .tippy-arrow {
border-top-color: #fff;
}
.tippy-popper[x-placement^='bottom'] .tippy-tooltip.light-theme .tippy-arrow {
border-bottom-color: #fff;
}
.tippy-popper[x-placement^='left'] .tippy-tooltip.light-theme .tippy-arrow {
border-left-color: #fff;
}
.tippy-popper[x-placement^='right'] .tippy-tooltip.light-theme .tippy-arrow {
border-right-color: #fff;
}
/* Round arrow */
.tippy-tooltip.light-theme .tippy-roundarrow {
fill: #fff;
}
/* TOC
part one - Swiper instilaze
part two - photoswipe instilaze
part three - photoswipe define options
part four - extra code (update swiper index when image close and micro changes)
/* 1 of 4 : SWIPER ################################### */
var mySwiper = new Swiper(".swiper-container", {
// If swiper loop is true set photoswipe counterEl: false (line 175 her)
loop: true,
/* slidesPerView || auto - if you want to set width by css like flickity.js layout - in this case width:80% by CSS */
slidesPerView: "auto",
spaceBetween: 10,
centeredSlides: true,
slideToClickedSlide: false,
autoplay: { /* remove/comment to stop autoplay */
delay: 3000,
disableOnInteraction: false /* true by deafult */
},
// If we need pagination
pagination: {
el: ".swiper-pagination",
clickable: true,
renderBullet: function(index, className) {
return '<span class="' + className + '">' + (index + 1) + "</span>";
}
},
// Navigation arrows
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
// keyboard control
keyboard: {
enabled: true,
}
});
// 2 of 4 : PHOTOSWIPE #######################################
// https://photoswipe.com/documentation/getting-started.html //
var initPhotoSwipeFromDOM = function(gallerySelector) {
// parse slide data (url, title, size ...) from DOM elements
// (children of gallerySelector)
var parseThumbnailElements = function(el) {
var thumbElements = el.childNodes,
numNodes = thumbElements.length,
items = [],
figureEl,
linkEl,
size,
item;
for (var i = 0; i < numNodes; i++) {
figureEl = thumbElements[i]; // <figure> element
// include only element nodes
if (figureEl.nodeType !== 1) {
continue;
}
linkEl = figureEl.children[0]; // <a> element
size = linkEl.getAttribute("data-size").split("x");
// create slide object
item = {
src: linkEl.getAttribute("href"),
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
};
if (figureEl.children.length > 1) {
// <figcaption> content
item.title = figureEl.children[1].innerHTML;
}
if (linkEl.children.length > 0) {
// <img> thumbnail element, retrieving thumbnail url
item.msrc = linkEl.children[0].getAttribute("src");
}
item.el = figureEl; // save link to element for getThumbBoundsFn
items.push(item);
}
return items;
};
// find nearest parent element
var closest = function closest(el, fn) {
return el && (fn(el) ? el : closest(el.parentNode, fn));
};
// triggers when user clicks on thumbnail
var onThumbnailsClick = function(e) {
e = e || window.event;
e.preventDefault ? e.preventDefault() : (e.returnValue = false);
var eTarget = e.target || e.srcElement;
// find root element of slide
var clickedListItem = closest(eTarget, function(el) {
return el.tagName && el.tagName.toUpperCase() === "LI";
});
if (!clickedListItem) {
return;
}
// find index of clicked item by looping through all child nodes
// alternatively, you may define index via data- attribute
var clickedGallery = clickedListItem.parentNode,
childNodes = clickedListItem.parentNode.childNodes,
numChildNodes = childNodes.length,
nodeIndex = 0,
index;
for (var i = 0; i < numChildNodes; i++) {
if (childNodes[i].nodeType !== 1) {
continue;
}
if (childNodes[i] === clickedListItem) {
index = nodeIndex;
break;
}
nodeIndex++;
}
if (index >= 0) {
// open PhotoSwipe if valid index found
openPhotoSwipe(index, clickedGallery);
}
return false;
};
// parse picture index and gallery index from URL (#&pid=1&gid=2)
var photoswipeParseHash = function() {
var hash = window.location.hash.substring(1),
params = {};
if (hash.length < 5) {
return params;
}
var vars = hash.split("&");
for (var i = 0; i < vars.length; i++) {
if (!vars[i]) {
continue;
}
var pair = vars[i].split("=");
if (pair.length < 2) {
continue;
}
params[pair[0]] = pair[1];
}
if (params.gid) {
params.gid = parseInt(params.gid, 10);
}
return params;
};
var openPhotoSwipe = function(
index,
galleryElement,
disableAnimation,
fromURL
) {
var pswpElement = document.querySelectorAll(".pswp")[0],
gallery,
options,
items;
items = parseThumbnailElements(galleryElement);
// #################### 3/4 define photoswipe options (if needed) ####################
// https://photoswipe.com/documentation/options.html //
options = {
/* "showHideOpacity" uncomment this If dimensions of your small thumbnail don't match dimensions of large image */
//showHideOpacity:true,
// Buttons/elements
closeEl: true,
captionEl: true,
fullscreenEl: true,
zoomEl: true,
shareEl: false,
counterEl: false,
arrowEl: true,
preloaderEl: true,
// define gallery index (for URL)
galleryUID: galleryElement.getAttribute("data-pswp-uid"),
getThumbBoundsFn: function(index) {
// See Options -> getThumbBoundsFn section of documentation for more info
var thumbnail = items[index].el.getElementsByTagName("img")[0], // find thumbnail
pageYScroll =
window.pageYOffset || document.documentElement.scrollTop,
rect = thumbnail.getBoundingClientRect();
return { x: rect.left, y: rect.top + pageYScroll, w: rect.width };
}
};
// PhotoSwipe opened from URL
if (fromURL) {
if (options.galleryPIDs) {
// parse real index when custom PIDs are used
// http://photoswipe.com/documentation/faq.html#custom-pid-in-url
for (var j = 0; j < items.length; j++) {
if (items[j].pid == index) {
options.index = j;
break;
}
}
} else {
// in URL indexes start from 1
options.index = parseInt(index, 10) - 1;
}
} else {
options.index = parseInt(index, 10);
}
// exit if index not found
if (isNaN(options.index)) {
return;
}
if (disableAnimation) {
options.showAnimationDuration = 0;
}
// Pass data to PhotoSwipe and initialize it
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
/* ########### PART 4 - EXTRA CODE ########### */
/* EXTRA CODE (NOT FROM photoswipe CORE) -
1/2. UPDATE SWIPER POSITION TO THE CURRENT ZOOM_IN IMAGE (BETTER UI) */
// photoswipe event: Gallery unbinds events
// (triggers before closing animation)
gallery.listen("unbindEvents", function() {
// This is index of current photoswipe slide
var getCurrentIndex = gallery.getCurrentIndex();
// Update position of the slider
mySwiper.slideTo(getCurrentIndex, false);
// 2/2. Start swiper autoplay (on close - if swiper autoplay is true)
mySwiper.autoplay.start();
});
// 2/2. Extra Code (Not from photo) - swiper autoplay stop when image zoom */
gallery.listen('initialZoomIn', function() {
if(mySwiper.autoplay.running){
mySwiper.autoplay.stop();
}
});
};
// loop through all gallery elements and bind events
var galleryElements = document.querySelectorAll(gallerySelector);
for (var i = 0, l = galleryElements.length; i < l; i++) {
galleryElements[i].setAttribute("data-pswp-uid", i + 1);
galleryElements[i].onclick = onThumbnailsClick;
}
// Parse URL and open gallery if it contains #&pid=3&gid=1
var hashData = photoswipeParseHash();
if (hashData.pid && hashData.gid) {
openPhotoSwipe(hashData.pid, galleryElements[hashData.gid - 1], true, true);
}
};
// execute above function
initPhotoSwipeFromDOM(".my-gallery");