How to load Photoswipe with Ajax to get server side pictures? - javascript

I'm searching for a gallery library and I see PhotoSwipe. Actually I just made the tutorial in the documentation.
I don't see any tutorial to load my server side pictures dynamically.
I need to load them with Ajax because I have a datatables, and inside each row I set an icon. The user can click on this icon and it will appears a bootstrap modal. In this modal I have to show the thumbnails related with the clicked row. And when the user click on the thumbnails I need to show the gallery.
It's possible to load dynamically server side pictures ?

I think you can achieve this by initiating the gallery from the click event. If you make this a delegated event, it will also get triggered on newly created images. Then you only need to create the image array upon triggering the click event and fire up the gallery.
Your images should be added like this:
<img class="myAjaxLoadedImage" src="myAjaxLoadedImage1_thumbnail.jpg" alt=""
data-img-title="My title 1" data-img-src="myAjaxLoadedImage1.jpg"
data-img-width="800" data-img-height="600">
<img class="myAjaxLoadedImage" src="myAjaxLoadedImage2_thumbnail.jpg" alt=""
data-img-title="My title 2" data-img-src="myAjaxLoadedImage2.jpg"
data-img-width="400" data-img-height="700">
...
And the JS would then be:
(function($) {
var pswp;
$(function() {
pswp = $('.pswp')[0];
setGalleryClickEvents();
});
function setGalleryClickEvents() {
$(document).on('click','.myAjaxLoadedImage',function(e) {
if (pswp) {
var options = {
index: $(this).index()
// + other PhotoSwipe options here...
}
var images = [];
$('.myAjaxLoadedImage').each(function() {
var $img = $(this);
images.push({
src: $img.data('imgSrc'),
w: $img.data('imgWidth'),
h: $img.data('imgHeight'),
title: $img.data('imgTitle')
});
});
var gallery = new PhotoSwipe(pswp, PhotoSwipeUI_Default, images, options);
gallery.init();
}
});
}
})(jQuery);

Related

Remove Button is deleting all img tags and not the selected one

This is my image uploader:
My Code for adding an image which works perfect:
jQuery(function($){
// Set all variables to be used in scope
var frame, selections, attachment,
metaBox = $('#gallery-meta-box.postbox'), // Your meta box id here
addImgLink = metaBox.find('.upload-custom-img'),
delImgLink = metaBox.find('.delete-custom-img'),
imgContainer = metaBox.find('.custom-img-container'),
imgIdInput = metaBox.find('.custom-img-id' );
// Add image from frame
addImgLink.on( 'click', function( event ){
event.preventDefault();
// If the media frame already exists, reopen it
if ( frame ) {
frame.open();
return;
}
// Create a new media frame
frame = wp.media({
title: 'Select Images',
button: {
text: 'Add Image'
},
multiple: true
});
// When an image is selected in the media frame
frame.on( 'select', function() {
// Get media attachments details from the frame state
selections = frame.state().get('selection');
selections.map(function(attachment){
attachment = attachment.toJSON();
// Send the attachment URL to our custom image input field
imgContainer.append(
'<li>'
+ '<img data-attachment-id="id-media-1993'+attachment.id+'" src="'+attachment.url+'" class="gallery-thumbnail" alt="'+attachment.title+'" style="max-width:150px; max-height:150px;"/>'
+ '<a class="delete-custom-img" href="#">Remove Image</a>'
+ '</li>');
// Send the attachment id to our hidden input
imgIdInput.val(attachment.id);
console.log(attachment);
});
});
// Finally, open the modal on click
frame.open();
});
// MY DELETE BUTTON :
imgContainer.on( 'click', delImgLink, function(event){
event.preventDefault();
var galleryThumbnail = $(this).find('img');
console.log(galleryThumbnail);
});
});
When you do watch the image uploader you can see the remove links. When I click on the remove and it doesn't matter which one of the remove button it's giving my the id's of both and same for the src.
see result:
When I click on the remove link, I want information about the current image, not all the images inside my div element.
Hopefully someone can explain it.
The issue is that, while you are using event delegation to handle dynamic elements, the delegation is pre-determined, so does not pick up the elements correctly
delImgLink = metaBox.find('.delete-custom-img'),
Change
imgContainer.on( 'click', delImgLink, ...
to
imgContainer.on('click', 'a.delete-custom-img',
then this will be the button and you can find the relevant image either with .closest().find() or .prevAll("img").first() (or other method):
imgContainer.on('click', 'a.delete-custom-img', function(event){
event.preventDefault();
var galleryThumbnail = $(this).closest("li").find('img');
console.log(galleryThumbnail);
});
In your original code, if this was the delete button then
$(this).find('img')
would not find anything as find find child items and there are no child items under your delete anchor, so this must be referring to something else, higher up.
You need jquery closet() to find nearest img and then delete it.
Or you can do it by
$(this).parent().find('img');
To achieve expected reult, use below option of adding event to imageContainer images and $(this) will provide the details of selected image
$(".imgContainer img").on( 'click', function(event){
event.preventDefault();
var galleryThumbnail = $(this);
console.log(galleryThumbnail[0].id);
});
https://codepen.io/nagasai/pen/VQJoZj

jQuery Lightbox gallery only working once

I am trying to build my own simple jQuery lightbox gallery. My logic behind it is as follows: Only thumbnails will be shown & created at first. These link to the full size images.
<section class="gallery-set">
<a href="img/about/gallery/a1.1.jpg">
<img src="img/about/gallery/thumb1.1.jpg" alt=""
height="192" width="383">
</a>
<a href="img/about/gallery/a1.jpg">
<img src="img/about/gallery/thumb1.jpg" alt=""
height="192" width="383">
</a>
<a href="img/about/gallery/a2.1.jpg">
<img src="img/about/gallery/thumb2.1.jpg" alt=""
height="192" width="383">
</a>
</section>
Therefore, when you click on any of these thumbnails, I dynamically create an overlay-lightbox and all full size images, showing only the one that links to the thumbnail you clicked. Although the rest of the images has been created too, these are hidden for now.
function lightBox() {
var gallery = $('.gallery-set'),
overlay = $('<div/>', {id: 'overlay'});
overlay.appendTo('body').hide();
gallery.on('click', 'a', function(event) {
event.preventDefault();
var clickedThumb = $(this),
clickedThumbPath = $(this).attr('href'),
clickedImg = $('<img>', {src: clickedThumbPath, alt: 'fullSizeImage', class: 'current'}),
prevThumbs = clickedThumb.prevAll(),
nextThumbs = clickedThumb.nextAll();
prevThumbs.each(function() {
var prevImg = $('<img>', {src: $(this).attr('href'), class: 'prev non-current'});
prevImg.appendTo(overlay);
});
clickedImg.appendTo(overlay);
nextThumbs.each(function() {
var nextImg = $('<img>', {src: $(this).attr('href'), class: 'next non-current'});
nextImg.appendTo(overlay);
});
overlay.show();
})
.....
.....
}
Now, when you click the second thumbnail, jQuery dynamically creates all the fullsize images and this is how HTML structure looks like:
Now that I have this structure, I can easily traverse the full sized images by left and right arrows. The current image gets hidden and the next one gets shown. For this logic I am using two classes, current and non-current where the first one has set display to block and the second one to none. This piece of code is within the lightbox() function:
$(document).on('keyup', function(event) {
var pressed = event.keyCode || event.which,
arrow = {left: 37, right: 39};
switch(pressed) {
case arrow.left:
var curr = overlay.find('.current'),
prev = curr.prev();
if(curr.hasClass('current')) {
curr.removeClass('current').addClass('non-current');
} else {
curr.addClass('non-current');
}
if(prev.hasClass('non-current')) {
prev.removeClass('non-current').addClass('current');
} else {
prev.addClass('current');
}
break;
case arrow.right:
var curr = overlay.find('.current'),
next = curr.next();
curr.removeClass('current').addClass('non-current');
next.removeClass('non-current').addClass('current');
break;
}
});
overlay.on('click', function() {
overlay.hide();
overlay.find('img').remove();
});
});
Everything works fine the first time. However, once I close the lightbox and try to open it again, the correct image opens but the arrows functionality is gone. I do not understand why - since I am dynamically creating the full sized images everytime user clicks on the gallery and putting event listeners (arrows) only once these have been created.
Just for the record, I am calling this lightbox() function from the HTML file right before the closing tag.
Any ideas much appreciated. Also, if there's a simpler / better way of doing this, please do let me know! I don't want to use any plugin as I think this is pretty simple and straightforward. Or, I thought it WOULD BE SIMPLE I should rather say.

Photoswipe pswp class Not Clearing After Closing Image

I have a Photoswipe (http://photoswipe.com) image gallery on my site, and the css class is not resetting/clearing to remove the view after I close a gallery for the second time.
ex.
User opens item 1, AJAX populates the figure(s) into the picture div.
User clicks an image from item 1 and Photoswipe opens the image properly (setting the following class):
class="pswp pswp--supports-fs pswp--open pswp--animate_opacity pswp--notouch pswp--css_animation pswp--svg pswp--animated-in pswp--visible"
User closes the image from item 1, class resets as normal:
class="pswp"
User closes item 1 and JS/JQuery clears all html in picture div. User opens item 2, AJAX populates the figure into the picture div. User clicks an image from item 2 and Photoswipe opens the image properly setting the same class as before.
class="pswp pswp--supports-fs pswp--open pswp--animate_opacity pswp--notouch pswp--css_animation pswp--svg pswp--animated-in pswp--visible"
This is where the problem occurs. User closes the image from item 2 and the only thing that changes is:
aria-hidden="true"
but the class does not clear, it remains:
class="pswp pswp--supports-fs pswp--open pswp--animate_opacity pswp--notouch pswp--css_animation pswp--svg pswp--animated-in pswp--visible"
when it should change to:
class="pswp"
This disables all interaction on the website since there is an invisible div/class on top of everything. The class needs to be changed back to pswp somehow.
AJAX/JS To Populate picture div (I added an id to the div):
if (i == 0) {
$('#listing_photos_container').append('<figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject"><img src="' + json[i].image_url + '" height="400" width="600" itemprop="thumbnail" alt="listingPhoto" class="listing-photo"></figure>');
} else {
$('#listing_photos_container').append('<figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject" class="listing-photo-holder"><img src="' + json[i].image_url + '" height="400" width="600" itemprop="thumbnail" alt="listingPhoto" class="listing-photo-holder"></figure>');
}
JS/JQuery to clear photo div:
$('#listing_photos_container').html('');
EDIT: The click listener function is running twice when a users clicks the photo to bring full screen. This is the code for the listener:
$.ajax({
type: "POST",
url: 'http://example.com/action?action=photos',
data: {id: id},
success: function (data) {
console.log('API Call - Photos');
json = JSON.parse(data);
$('#listing_photos_container').html('');
for (var i = 0; i < json.length; i++) {
// Styling code here
}
$('#list_header').html(
(function($) {
$('.picture').each( function() {
var $pic = $(this),
getItems = function() {
var items = [];
$pic.find('a').each(function() {
var $href = $(this).attr('href'),
$size = $(this).data('size').split('x'),
$width = $size[0],$height = $size[1];
var item = {
src : $href,
w : $width,
h : $height
}
items.push(item);
});
return items;
}
var items = getItems();
console.log('Items for PSWP' + items);
alert('Alert Point 1'); // This is called once, (as it should).
var $pswp = $('.pswp')[0];
$pic.on('click', 'figure', function(event) {
// This block is called twice..
alert('Click Funct');
event.preventDefault();
var $index = $(this).index();
var options = {
index: $index,
bgOpacity: 0.7,
showHideOpacity: true
}
// Initialize PhotoSwipe
alert('Setting new PhotoSwipe');
var lightBox = new PhotoSwipe($pswp, PhotoSwipeUI_Default, items, options);
lightBox.init();
}); // End $pic.on
});// End .picture each
})(jQuery)
); // End list_header.html
} // End AJAX Success
}); // End AJAX
You may have already fixed this, but in case someone else falls upon this.
This can happen if you trigger opening the gallery more than once without closing it. It may be that you have registered multiple click handlers to open the gallery or for some reason the event is being fired twice.
It happens because in the init function the current class name of the pswp element is retrieved and cached, then on destroy the class name is restored. When the second open occurs without destroy being called _initialClassName will be set to class="pswp pswp--supports-fs pswp--open pswp--animate_opacity pswp--notouch pswp--css_animation pswp--svg pswp--animated-in pswp--visible" as your are seeing
Line 776 of photoswipe.js where initialclass is set
_initalClassName = template.className;
Breakpoint this in your browser to see if it is called multiple times when opening
Line 942 onwards destroy function
destroy: function() {
_shout('destroy');
Breakpoint this in your browser to ensure it is being called for every time open is called
Final Solution
The problem is that when opening the popup and loading the images you are filling #listing_photos_container with your photos, then adding a click handler to open photoswipe. This click handler is added to the top element, so will remain when the popup is closed, then the next time it is opened a new click handler will be added.
To fix this you just need to unbind the click handler when closing the popup, you can do this with $(".picture").off('click'); somewhere inside your closeListing() function
It's quite simple, before every .click(...) you need to write .unbind('click').
Example:
$('a#open-photoswipe').unbind('click').click(function() {
// open photoswipe here
});
Was having similar problem - just define lightBox as global variable. And on destroy define it as null. And in beginning of function where You initialize lightBox just check if lighBox is already defined, then do return.

$(this).parent().find() is not working

I am making a Wordpress widget showing an image, and to upload/change this image I use the Wordpress media uploader.
This is the admin form markup I'm using:
<p class="media-control"
data-title="Choose an image"
data-update-text="Choose image">
<input class="image-url" name="image-url" type="hidden" value="">
<img class="image-preview" src=""><br>
<a class="button" href="#">Pick image</a>
</p>
When I click ".media-control a" the uploader appears and I can pick an image. But when I've picked an image ".image-preview" and ".image-url" isn't updated.
Here's my javascript: http://jsfiddle.net/etzolin/DjADM/
Everything is working as intended except these lines:
jQuery(this).parent().find('.image-url').val(attachment.url);
jQuery(this).parent().find('.image-preview').attr('src', attachment.url);
When I write them like this, input value is set and image preview is updated:
jQuery('.media-control .image-url').val(attachment.url);
jQuery('.media-control .image-preview').attr('src', attachment.url);
But since I use more than one of these widgets it updates the input value and image preview in every widget.
How can I set the input value and update the image preview only in the widget I'm editing? What am I doing wrong?
Inside the event handler for the media frame this is not the clicked element
var media_frame;
jQuery('.media-control a').live('click', function (event) {
var self = this;
event.preventDefault();
if (media_frame) {
media_frame.open();
return;
}
media_frame = wp.media.frames.media_frame = wp.media({
title: jQuery(self).parent().data('title'),
button: {
text: jQuery(self).parent().data('update-text'),
},
multiple: false
});
media_frame.on('select', function () {
attachment = media_frame.state().get('selection').first().toJSON();
jQuery(self).parent().find('.image-url').val(attachment.url); // set .image-url value
jQuery(self).parent().find('.image-preview').attr('src', attachment.url); // update .image-preview
// ^^ this is not the element from the click handler but the media frame
});
media_frame.open();
});
and you should be using on() as live() is deprecated and removed from jQuery

Create Fancybox gallery when integrated with PikaChoose [duplicate]

I'm using the Fancybox integration with Pikachoose as explained here:
http://www.pikachoose.com/how-to-fancybox/
I'm trying to get the lightbox to display next and previous arrows but not on the pikachoose stage and I'm having a bit of trouble. I tried to add the options of showNavArrows: true in the fancybox section of the script but it wouldn't work. So then I tried the nav options on pikachoose to display using this: {text: {previous: "Previous", next: "Next" }}
but I keep getting an error, possibly my syntax isn't going in the right place?
Can someone help please?
this is the code I'm using :
$(document).ready(function () {
var a = function (self) {
self.anchor.fancybox({
transitionIn: elastic,
transitionOut: elastic,
speedIn: 600,
speedOut: 200,
overlayShow: false
});
};
$("#pikame").PikaChoose({
showCaption: false,
buildFinished: a,
autoPlay: false,
transition: [0],
speed: 500,
showCaption: false
});
});
The problem with the method explained in http://www.pikachoose.com/how-to-fancybox/ is that you bind fancybox to the current pikachoose element self.anchor.
With that approach, there is no way to know what group of images will belong to a fancybox gallery (you would need more than one element sharing the same rel attribute) because there is just a single pikachoose image : every image is displayed toggling dynamically its href and src attributes (<a> and <img> tags respectively) inside the .pika-stage container.
As a workaround, you would need to built the fancybox group of elements BEFORE binding your html structure to pikachoose (pikachoose will modify the DOM structure)
1). So having this html structure :
<div class="pikachoose">
<ul id="pikame">
<li>
<a title="one" href="image01.jpg" id="single_1"><img alt="" src="thumb01.jpg" /></a>
</li>
<li>
<a title="two" href="image02.jpg" id="single_2"><img alt="" src="thumb02.jpg" /></a>
</li>
<li>
<a title="three" href="image03.jpg" id="single_3"><img alt="" src="thumb03.jpg" /></a>
</li>
</ul>
</div>
2). Create the fancybox group of elements iterating through each anchor with this script :
var fancyGallery = []; // fancybox gallery group
$(document).ready(function () {
$("#pikame").find("a").each(function(i){
// buidl fancybox gallery group
fancyGallery[i] = {"href" : this.href, "title" : this.title};
});
}); // ready
3). Then bind pikachoose to the same selector #pikame. You can use the .end() method to do it over the first decelerated selector without duplicating it ;)
var fancyGallery = []; // fancybox gallery group
$(document).ready(function () {
// build fancybox group
$("#pikame").find("a").each(function(i){
// buidl fancybox gallery
fancyGallery[i] = {"href" : this.href, "title" : this.title};
}).end().PikaChoose({
autoPlay : false, // optional
// bind fancybox to big images element after pikachoose is built
buildFinished: fancy
}); // PikaChoose
}); // ready
Notice that we used the pikachoose option buildFinished: fancy, which actually will fire the fancybox gallery when we click on the big image.
4). Here is the function :
var fancy = function (self) {
// bind click event to big image
self.anchor.on("click", function(e){
// find index of corresponding thumbnail
var pikaindex = $("#pikame").find("li.active").index();
// open fancybox gallery starting from corresponding index
$.fancybox(fancyGallery,{
// fancybox options
"cyclic": true, // optional for fancybox v1.3.4 ONLY, use "loop" for v2.x
"index": pikaindex // start with the corresponding thumb index
});
return false; // prevent default and stop propagation
}); // on click
}
Notice that we bound a click event using .on() (requires jQuery v1.7+) to the pikachoose element self.anchor to fire fancybox gallery using the manual method $.fancybox([group]).
This workaround works equally fine for fancybox v1.3.4 or v2.x. See DEMO using v1.3.4 that seems to work fine even with IE7 ;)
JFK response is great, but there is something to correct :
if carousel is enabled in Pikachoose, the computed index using this method will give you an invalid one, beacause pikachoose will manipulate DOM by appending existing li in ul:
var pikaindex = $("#pikame").find("li.active").index();
Solution :
function getCurrentIndex(fancyGallery) {
var activeLi = $(""#pikame").find("li.active");
if (activeLi.length != 1) {
console.error('(getCurrentIndex) - only one image must have an active class set by Pikachoose');
return -1;
}
var activeLiHtml0 = activeLi[0];
var activeHref = $(activeLiHtml0).find('img').attr('src'); // do not look for <a> tags, PikaChoose will remove them
if (activeHref === null || activeHref.length == 0) {
console.error('(getCurrentIndex) - can not get href attribute from selected image');
return -1;
}
for (var i=0 ; i<fancyGallery.length ;i++) {
var obj = fancyGallery[i];
if (obj.href.indexOf(activeHref) >= 0){
console.debug('(getCurrentIndex) - found index: ' + i);
return i;
}
}
console.error('(getCurrentIndex) - this href: <' + activeHref + '> was not found in configured table');
return -1;
};

Categories

Resources