PhotoSwipe: closing gallery animates wrong rectangle (thumbnail) - javascript

I'm new to Javascript and I have a problem implementing PhotoSwipe plugin:
I'm trying to implement PhotoSwipe plugin for a web page using jQuery. Most of it is working correctly (opening a gallery, navigating slides). The problem happens when I close the gallery. The problem:
I click on image 1, this opens PhotoSwipe lightbox, I navigate to slide 2, and then close the gallery. This closes the gallery. But closing animation is played for image 1, while I am expecting it to be played for Image 2.
It works correctly on PhotoSwipe demo page, so it is an error in my code. If I copy/paste demo page Javascript code, it works correctly.
I believe I am missing some code that binds currently shown slide with respective thumbnail. My main issue with demo page is: I have a hard time understanding vanilla JS demo, what part is responsible for what action. Please help me find missing functionality.
Here's my code for PhotoSwipe "click to start gallery" event:
$('.my-gallery').each( function() {
var $pic = $(this);
var items = itemArray;
var $pswp = $('.pswp')[0];
$pic.on('click', 'figure', function(event) {
event.preventDefault();
var $index = $(this).index();
var options = {
index: $index,
getThumbBoundsFn: function(index) {
// get element we clicked on to determine its position in window
var thumbnail = event.target;
// get position of element relative to viewport
var rect = thumbnail.getBoundingClientRect();
// get window scroll Y
var pageYScroll = window.pageYOffset ||
document.documentElement.scrollTop;
return {x:rect.left, y:rect.top + pageYScroll, w:rect.width};
}
}
// Initialize PhotoSwipe
var lightBox = new PhotoSwipe($pswp, PhotoSwipeUI_Default, items, options);
lightBox.init();
});
});
My gallery (stripped down to 2 slides):
<div class="row my-gallery" itemscope itemtype="http://schema.org/ImageGallery" id="img-gallery">
<figure itemprop="associatedMedia" itemscope="" itemtype="http://schema.org/ImageObject">
<a href="images/nature/DSC_7216.jpg" itemprop="contentUrl" data-size="1200x795">
<img src="images/nature/DSC_7216_t.jpg" itemprop="thumbnail">
</a>
</figure>
<figure itemprop="associatedMedia" itemscope="" itemtype="http://schema.org/ImageObject">
<a href="images/nature/DSC_7218.jpg" itemprop="contentUrl" data-size="1200x795">
<img src="images/nature/DSC_7218_t.jpg" itemprop="thumbnail">
</a>
</figure>
</div>
Item array is generated from JSON:
[
{
"src": "images/nature/DSC_7216.jpg",
"msrc": "images/nature/DSC_7216_t.jpg",
"w":1200,
"h":795
},
{
"src": "images/nature/DSC_7218.jpg",
"msrc": "images/nature/DSC_7218_t.jpg",
"w":1200,
"h":795
}
]
JSON is hardcoded into a p element, is parsed using jQuery parser:
var itemArray = jQuery.parseJSON($(imageListSelector).html());
You can find the full page with problem on GitHub
PhotoSwipe demo on Codepen
Can you help me find what I am missing?
Edit:
I've tracked the issue down to this part of code in the original PhotoSwipe demo:
var thumbnail = items[index].el.getElementsByTagName('img')[0], // find thumbnail
If I replace this part with a fixed thumbnail selector (like I have in my jQuery - it contains "event target" reference), I can force PhotoSwipe demo to repeat my behavior - zoom out gets performed on same image. Not exactly the same thing that happens in my case, but close enough.
Now I just need to figure out how to change my getThumbBoundsFn to use actual thumbnail reference instead of event.target... I'll probably have to update my itemArray to include links to figure element, like the original PhotoSwipe demo. As I wrote earlier, I'm still new to Javascript, so figuring out some things takes time. Any help will be appreciated.

Figured it out myself. I really screwed things up by using event.target. The correct way of working with PhotoSwipe requires me to provide actual DOM element instead of a fixed one (like event target).
The correct way of doing this is just like demo:
save DOM element (selector) when creating itemArray
use DOM element from itemArray in order to provide correct element to calculate bounding rectangle.
Correct jQuery code:
var gallerySelector = "#img-gallery";
var imageListSelector = "#imageList";
// parse server reply (JSON, imitated, saved into a tag)
var itemArray = jQuery.parseJSON($(imageListSelector).html());
var index = 1;
// HTML structure of gallery image
var imageHTML = "<figure class=\"col-xs-12 col-sm-6 col-md-4\" " +
"itemprop=\"associatedMedia\" itemscope " +
"itemtype=\"http://schema.org/ImageObject\">\n" +
"<a href=\"{imageSource}\" itemprop=\"contentUrl\" data-size=\"{imageSize}\">\n" +
"<img class=\"img-responsive\" src=\"{imageThumb}\" itemprop=\"thumbnail\" />\n" +
"</a>\n</figure>";
// generate images based on JSON request (imitated) and appended them to the page
itemArray.forEach(function(item) {
var imageTags = imageHTML.replace("{imageSource}", item.src);
var imageTags = imageTags.replace("{imageSize}", (""+item.w+"x"+item.h));
var imageTags = imageTags.replace("{imageThumb}", item.msrc);
$(gallerySelector).append(imageTags);
item.el = $(gallerySelector + " figure:last img")[0];
});
$('.my-gallery').each( function() {
var $pic = $(this);
var $pswp = $('.pswp')[0];
$pic.on('click', 'figure', function(event) {
event.preventDefault();
var $index = $(this).index();
var options = {
index: $index,
getThumbBoundsFn: function(index) {
// get element we clicked on to determine its position in window
//var thumbnail = event.target;
var thumbnail = itemArray[index].el;
// get position of element relative to viewport
var rect = thumbnail.getBoundingClientRect();
// get window scroll Y
var pageYScroll = window.pageYOffset ||
document.documentElement.scrollTop;
return {x:rect.left, y:rect.top + pageYScroll, w:rect.width};
}
}
// Initialize PhotoSwipe
var lightBox = new PhotoSwipe($pswp, PhotoSwipeUI_Default, itemArray, options);
lightBox.init();
});
});
Summary of changes:
added item.el property, which saves last added element when it is appended to the gallery (in my case - figure img, since I need the img object to calculate its bounding rectangle).
Replaced event.target with respective itemArray[index].el (previously saved node).
Hope this helps! It took me a couple of hours and some trial and error with PhotoSwipe demo page to figure this out.

Related

Set an image from Firebase as the background of div via jQuery

I'm trying to get an image from fire base and set it as the background of a div. Does anyone know how to do that? I tried all of my ideas and they did not work. Can someone provide any examples?
<div class="museBGSize rounded-corners grpelem" id="u525"></div>
I tried it this way and it didn't work:
fbRef4.on("child_added", snap => {
var image = snap.child("Image").val();
//concatenated the img tag using the image variable at the top
$("#tableBody3").append("<img src=" + image + "/img>");
});
hello every one i found an answer for it and will post the answer just in case if some one want to know
((my html))
<div class="museBGSize rounded-corners grpelem" id="u525"><!-- simple frame --></div>
((my js))
var fbRef4 = firebase.database().ref().child("slide");
fbRef4.on("child_added", snap => {
var image = snap.child("Image").val();
var newimgsrc = 'image' + (new Date().getTime());
var newimg = $('#u525');
$('#u525').css("background-image", "url("+image+")");
newimg.css({'background-image': 'url('+image+')'});
newimg.show();
});
((my firebase))
-slide
-asdasdasd
Image:"https://firebasestorage.googleapis.com/v0/b/awe..."

How to display the slider which is clicked after navigating to the same page after page reload in Jssor image slider

I am using Jssor image slider in my application. the requirement is when the user clicks an image with index number 5, the user is navigated to any other page. When the user goes back to the slider page, same image should be displayed (i.e image with index number 5)instead of image with index number 1.
I am using query string to get the index number of the clicked image.
var sbIndex = '<%= Request.QueryString["SubIndex"]%>';
$('#jssor_2').$GoTo = sbIndex;
Can anyone give me a solution to get the index number of image which is clicked.
I don't know jssor, I just took a look at it. Anyway, it looks like jssor should not conflict with my answer.
You can put the index in the url, so when the client reloads, you read that index and set it. The way to do this, is putting a # in the url. Anything that is changed at the right hand side of the # will not cause a page reload, so this is a nice place to put variables meant for javascript.
Let me show a simple example of the principle. It's a photo gallery, the photos are Brussels sprouts.
<div id="buttons"></div>
<img id="image" src="">
<script>
// these are images of Brussels Sprouts
var images = [
'https://d2t35vvdhj0e6p.cloudfront.net/m/i/brussels_sprouts.jpg',
'http://www.polskafoods.com/sites/all/sites/default/files/images/brussels-sprouts-polish-recipe.jpg',
'http://www.onlyfoods.net/wp-content/uploads/2013/05/Brussels-Sprouts.jpg',
'http://www.qvm.com.au/wp-content/uploads/2013/04/Brussel-Sprouts.jpg',
'https://www.smartkitchen.com/assets/images/resources/large/1281295928Brussels%20Sprouts.jpg'
];
function generateButton(index) {
return '<input type="button" value="' + index + '" onclick="goto(' + index + ')" />';
}
function goto(index) {
if(images[index]) {
// set the url
location = '#' + index;
// set the image source
document.getElementById('image').src = images[index];
}
}
window.onload = function() {
var index = 0;
// check if any index is in the url
if(Number(location.hash.substr(1))) {
index = Number(location.hash.substr(1));
}
var buttons = '';
for(var i in images) {
buttons += generateButton(i);
}
document.getElementById('buttons').innerHTML = buttons;
goto(index);
}
</script>

Set position of a <div> from data saved in localStorage

I am trying to learn how to use localStorage.
Partly imitating, I have written html that generates a page with a few tiles that you can drag and drop around the page.
For example
<script type="text/javascript">
function drag_start(event){
var style = window.getComputedStyle(event.target, null);
var str = (parseInt(style.getPropertyValue("left")) - event.clientX) + ',' + (parseInt(style.getPropertyValue("top")) - event.clientY)+ ',' + event.target.id;
event.dataTransfer.setData("Text",str);
event.stopPropagation();
}
function drop(event){
var offset = event.dataTransfer.getData("Text").split(',');
var dm = document.getElementById(offset[2]);
dm.style.left = (event.clientX + parseInt(offset[0],10)) + 'px';
dm.style.top = (event.clientY + parseInt(offset[1],10)) + 'px';
localStorage.setItem(dm.id,dm.style.left);
event.preventDefault();
return false;
}
function drag_over(event){
event.preventDefault();
return false;
}
</script>
I think that with a line like the one above beginning with "localStorage" I can save in localStorage the position after the drop. [The current line is just a mock example. Later, when I understand these things, I will actually store the position, or the offset.]
The part that I am confused is about how to retrieve the position from localStorage when the page is being loaded.
Say, that I am going to have one of the tiles being
<div id="tile3" draggable="true" ondragstart="drag_start(event)">
<a href="http://www.link.somewhere">
Link
</a>
</div>
I can say that the tile has style="position:absolute" and then I would need to retrieve the offset from localStorage and set as a property of the div.
But how to do this last part?
for the saving you use this javascript command:
(assuming thePosition is an array with two values (x and y position))
localStorage.setItem("position", JSON.Stringify(thePosition));
on the pageload you can do something like this (assuming you use jquery):
$(document).ready(function(){
var position = JSON.parse(localStorage.getItem("position"));
$('#the-divs-id').css({'left': position[0], 'top': position[1]});
});
edit: added JSON stringify/parse for the array
If you want to use no jquery:
window.onload = setDiv();
function setDiv(){
var position = JSON.parse(localStorage.getItem("position"));
document.getElementById(the-divs-id).style.left = position[0];
document.getElementById(the-divs-id).style.top = position[1];
}
edit: the looping question:
$(document).ready(function(){
// loops trough all divs with the-class
$('.the-class').each(function(){
// get the id from the current div
// and get the corresponding position from local storage
var id = $(this).attr('id'),
position = JSON.parse(localStorage.getItem(id));
// sets the css values for the current div
$(this).css({'left': position[0], 'top': position[1]});
});
});

Load content in div from a href tag in jQuery

I want to load all images before displaying them in a slideshow. I have been searching a lot but nothing seems to work. This is what i have so far and it doesn't work. I am loading images from the <a> tag from another div.
$('.slideshow').load(function(){
if (loaded<length){
first = $(settings.thumb).eq(loaded).find('a').attr("href");
$('<img src="'+first1+'"/>').appendTo('.slideshow');
}
else{ $('.slideshow').show(); }
loaded++;
});
Add an event listener to each image to respond to when the browser has finished loading the image, then append it to your slideshow.
var $images = $("#div_containing_images img");
var numImages = $images.length;
var numLoaded = 0;
var $slideshow = $(".slideshow");
$images.each(function() {
var $thisImg = $(this);
$thisImg.on("load", function() {
$thisImg.detach().appendTo($slideshow);
numLoaded++;
if (numLoaded == numImages) {
$slideshow.show();
}
});
});
It's a good idea to also listen for the error event as well, in case the image fails to load. That way you can increase numLoaded to account for broken image. Otherwise, your slideshow will never be shown in the event the image is broken.
Also note, that by calling detach() followed by appendTo() I am am moving the image in the DOM. If instead, you want to copy the image, use clone() instead of detach().
* EDIT TO MODIFY USER'S EXACT USE CASE *
var $images = $("li.one_photo a");
var numImages = $images.length;
var numLoaded = 0;
$images.each(function() {
$('<img />',
{ src: $(this).attr("href") })
.appendTo('.slideshow')
.on("load error", function() {
numLoaded++;
if(numLoaded == numImages) {
$('.slideshow').show();
}
});
});
* EDIT #2 *
Just realized you were putting everything in the $(".slideshow").load() function. Since $(".slideshow") represents a DIV, it will never raise a load event, and the corresponding function will never execute. Edited above accordingly.

How can I re-render a specific element with backbone.js(or maybe do not need it) after whole page been loaded?

Say my page has loaded successfully.
There is an img element in the document like this:
<div class="pro_list_imgbox">
<img src="http://XXXX.com/bazinga.jpg" />
</div>
And I have backbone.js code like this:
events: {
'click .pro_list_imgbox': 'loadPic',
},
loadPic: function (e) {
var target = $(e.target),
pic = target[0].nodeName === 'IMG' ? target : target.find('img');
if (!pic.data('loadState')) {
pic.attr('src', pic[0].src += '?t' + new Date().getTime());
}
},
My question is how can I re-render this img element after I clicked it?
Will my loadPic function works? with a random num as suffix of src
And maybe something else I should do?
Looks like you're retrieving your image from a back-end controller, and that image is tagged according to the DateTime recorded upon retrieval.
To retrieve the same image, you'd have to save the source you used to retrieve that image. Given the code you already have, I'd say the most immediate answer would be to store the image source in the <img> element for the image, using $.data(), like this
loadPic: function (e) {
var target = $(e.target),
pic = target[0].nodeName === 'IMG' ? target : target.find('img');
if (!pic.data('loadState')) {
var picSource = pic[0].src += '?t' + new Date().getTime();
pic.data({ source: picSource });
pic.attr('src', picSource);
}
}
The <img> element that was clicked will now have the last source string for that image and can be recalled using
var imageSource = $('img-selector').data(source);

Categories

Resources