I have the following code working properly to responsively lazy load background images for a number of divs on a page:
// get frames
// REFACTOR LIST:
var frame1 = document.getElementById('frame1');
var frame2 = document.getElementById('frame2');
var frame3 = document.getElementById('frame3');
var frame4 = document.getElementById('frame4');
var frame5 = document.getElementById('frame5');
// create Lazy loader
var myLazyLoad = new LazyLoad({
elements_selector: ".lazy"
});
// load images responsively
function loadImgs() {
console.log('Loading images...');
if(window.matchMedia("only screen and (max-width:700px)").matches) {
// viewport is less than or equal to 700 pixels wide
// REFACTOR LIST:
var src1 = frame1.getAttribute('data-src-small');
var src2 = frame2.getAttribute('data-src-small');
var src3 = frame3.getAttribute('data-src-small');
var src4 = frame4.getAttribute('data-src-small');
var src5 = frame5.getAttribute('data-src-small');
} else {
// viewport is greater than 700 pixels wide
// REFACTOR LIST:
var src1 = frame1.getAttribute('data-src-large');
var src2 = frame2.getAttribute('data-src-large');
var src3 = frame3.getAttribute('data-src-large');
var src4 = frame4.getAttribute('data-src-large');
var src5 = frame5.getAttribute('data-src-large');
}
// set data-src for lazy loader
// REFACTOR LIST:
frame1.setAttribute('data-src', src1);
frame2.setAttribute('data-src', src2);
frame3.setAttribute('data-src', src3);
frame4.setAttribute('data-src', src4);
frame5.setAttribute('data-src', src5);
// tell lazy loader that the data should be re-processed
// REFACTOR LIST:
frame1.removeAttribute('data-was-processed');
frame2.removeAttribute('data-was-processed');
frame3.removeAttribute('data-was-processed');
frame4.removeAttribute('data-was-processed');
frame5.removeAttribute('data-was-processed');
// tell lazy loader to update
myLazyLoad.update();
}
// load images initially
loadImgs();
// reload images when window is resized across the 700px breakpoint
var lastWindowSize = window.innerWidth;
window.onresize = function(event) {
var currentWindowSize = window.innerWidth;
if((lastWindowSize <= 700 && currentWindowSize > 700) || (lastWindowSize > 700 && currentWindowSize <= 700)) {
loadImgs();
}
lastWindowSize = currentWindowSize;
};
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
&:focus {
outline: none;
}
}
* {
font-family: monaco, courier;
}
body {
margin: 0;
padding: 0;
}
.wrapper {
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center ;
background: #ddd;
}
p {
position: absolute;
top: 0;
left: 0;
margin: 0;
padding: 8px;
color: darkslategray;
background: gold;
}
.frame {
width: 80vw;
height: 200px;
margin: 0 0 1rem 0;
padding: 0;
position: relative;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border: 2px solid gold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-lazyload/8.7.1/lazyload.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- https://stackoverflow.com/questions/50431531/lazylaoding-css-background-not-html-img-tags -->
<main class="wrapper">
<a href="#">
<div id="frame1" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=1"
data-src-large="https://source.unsplash.com/random/1200x600?sig=1">
<p>1</p>
</div>
</a>
<a href="#">
<div id="frame2" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=2"
data-src-large="https://source.unsplash.com/random/1200x600?sig=2">
<p>2</p>
</div>
</a>
<a href="#">
<div id="frame3" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=3"
data-src-large="https://source.unsplash.com/random/1200x600?sig=3">
<p>3</p>
</div>
</a>
<a href="#">
<div id="frame4" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=4"
data-src-large="https://source.unsplash.com/random/1200x600?sig=4">
<p>4</p>
</div>
</a>
<a href="#">
<div id="frame5" class="frame lazy"
data-src-small="https://source.unsplash.com/random/400x200?sig=5"
data-src-large="https://source.unsplash.com/random/1200x600?sig=5">
<p>5</p>
</div>
</a>
</main>
CodePen here
But I'd like to refactor the code to dry it up. I'm thinking for loops can be used to replace each of the 5 lists under a REFACTOR LIST: comment. My aim is to enable the code for any unknown number of divs with a class of frame.
To start, as an example, I've attempted to refactor the variable declarations at the beginning with the following loop:
var FramesQuantity = document.getElementsByClassName("frame").length
var frameVariables = [];
function createframeVariables() {
for (var i = 0; i <= FramesQuantity; ++i) {
var frameIndex = 'frame' + i;
console.log("frameIndex: " + frameIndex);
frameVariables[i] = document.getElementById(frameIndex);
}
return frameVariables;
}
createframeVariables();
console.log("frameVariables[0]: " + frameVariables[0]);
but that second console log returns null and I'm not sure if this is the right direction anyway.
Any ideas?
As was suggested, I was able to refactor the code, DRYing it up, by using .forEach with .querySelectorAll which obviated the need to set variables:
// for loop demo
document.querySelectorAll('.frame[data-src-small]').forEach( (frame, index) => {
console.log( "index: " + index);
console.log( "frame.dataset.srcSmall: " + frame.dataset.srcSmall);
console.log( "frame.dataset.srcLarge: " + frame.dataset.srcLarge);
})
// create Lazy loader
var myLazyLoad = new LazyLoad({
elements_selector: ".lazy"
});
// load images responsively
function loadImgs(context) {
console.log('Loading images ' + context);
if(window.matchMedia("only screen and (max-width:700px)").matches) {
// viewport is less than or equal to 700 pixels wide
document.querySelectorAll('.frame[data-src-small]').forEach( (frame, index) => {
var srcSmall = frame.dataset.srcSmall;
// set data-src for lazy loader
frame.setAttribute('data-src', srcSmall);
// tell lazy loader that the data should be re-processed
frame.removeAttribute('data-was-processed');
})
} else {
document.querySelectorAll('.frame[data-src-small]').forEach( (frame, index) => {
// viewport is greater than 700 pixels wide
var srcLarge = frame.dataset.srcLarge;
// set data-src for lazy loader
frame.setAttribute('data-src', srcLarge);
// tell lazy loader that the data should be re-processed
frame.removeAttribute('data-was-processed');
})
}
// tell lazy loader to update
myLazyLoad.update();
}
// load images initially
loadImgs("initially");
// reload images when window is resized across the 700px breakpoint
var lastWindowSize = window.innerWidth;
window.onresize = function(event) {
var currentWindowSize = window.innerWidth;
if((lastWindowSize <= 700 && currentWindowSize > 700) || (lastWindowSize > 700 && currentWindowSize <= 700)) {
loadImgs("on resize across breakpoint");
}
lastWindowSize = currentWindowSize;
};
Forked updated version of the original CodePen here.
Related
I've followed a tutorial about JS image sliders. I'm trying to have a text box display on each image (figured that out) but I need the text to be specific for each image. The images being grabbed from an img folder and are in order (image-0, image-1, etc). I'm guessing I'll need some array but I can't figure out how to do this in JS and have the corresponding text display on each correct image. Code provided. Any help?
HTML
<body>
<div class="images">
<div id="btns">
<button type="button" class="btn prevBtn">โฉ</button>
<button type="button" class="btn nextBtn">โช</button>
</div>
<div id="textBlock">
<h4>This is the image</h4>
</div>
</div>
<script src="script.js"></script>
</body>
JS
const nextBtn = document.querySelector(".nextBtn");
const prevBtn = document.querySelector(".prevBtn");
const container = document.querySelector(".images");
let counter = 0;
nextBtn.addEventListener("click",nextSlide);
prevBtn.addEventListener("click",prevSlide);
function nextSlide () {
container.animate([{opacity:"0.1"},{opacity:"1.0"}],{duration:1000,fill:"forwards"});
if(counter === 4){
counter = -1;
}
counter++;
container.style.backgroundImage = `url(img/image-${counter}.jpg`
}
function prevSlide () {
container.animate([{opacity:"0.1"},{opacity:"1.0"}],{duration:1000,fill:"forwards"});
if(counter === 0){
counter = 5;
}
counter--;
container.style.backgroundImage = `url(img/image-${counter}.jpg`
}
Since you counter is indexed 0 and goes up to ๐ all you need is an array:
const descriptions = [
"A nice walk in the park", // for the image counter 0
"My dog and me", // for the image counter 1
// etc.
];
than all you need to do is:
textBlock.textContent = descriptions[counter];
But...
I don't know where you found that toturial but it's a really a great example on how not to build a gallery. The animation is odd, it's overly simplistic and cannot account for multiple galleries. It's repetitive and unmodular. And the total number of slides should never be hardcoded, that's why we use a programming language after all. And yes, it can count the number of items using .length.
Code should be reusable:
class Gallery {
constructor(id, slides) {
this.slides = slides || [];
this.total = this.slides.length;
this.curr = 0;
this.EL = document.querySelector(id);
this.EL_area = this.EL.querySelector(".Gallery-area");
this.EL_prev = this.EL.querySelector(".Gallery-prev");
this.EL_next = this.EL.querySelector(".Gallery-next");
this.EL_desc = this.EL.querySelector(".Gallery-desc");
const NewEL = (tag, prop) => Object.assign(document.createElement(tag), prop);
// Preload images
this.ELs_items = this.slides.reduce((DF, item) => (DF.push(NewEL("img", item)), DF), []);
this.EL_area.append(...this.ELs_items);
// Events
this.EL_prev.addEventListener("click", () => this.prev());
this.EL_next.addEventListener("click", () => this.next());
// Init
this.anim();
}
// Methods:
anim() {
this.curr = this.curr < 0 ? this.total - 1 : this.curr >= this.total ? 0 : this.curr;
this.ELs_items.forEach((EL, i) => EL.classList.toggle("is-active", i === this.curr));
this.EL_desc.textContent = this.slides[this.curr].alt;
}
prev() {
this.curr -= 1;
this.anim();
}
next() {
this.curr += 1;
this.anim();
}
}
// Use like:
new Gallery("#gallery-one", [
{alt: "My fluffy dog and me", src: "https://picsum.photos/400/300"},
{alt: "Here, we seem happy!", src: "https://picsum.photos/300/300"},
{alt: "We are making pizza?", src: "https://picsum.photos/600/300"},
]);
.Gallery {
position: relative;
height: 300px;
max-height: 100vh;
}
.Gallery-area > * {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: opacity 0.3s;
opacity: 0;
}
.Gallery-area > *.is-active {
opacity: 1;
}
.Gallery-btns {
position: absolute;
bottom: 20px;
width: 100%;
text-align: center;
}
.Gallery-desc {
position: absolute;
top: 20px;
width: 100%;
text-align: center;
font-size: 3em;
}
<div class="Gallery" id="gallery-one">
<div class="Gallery-area"></div>
<div class="Gallery-btns">
<button type="button" class="btn Gallery-prev">โ</button>
<button type="button" class="btn Gallery-next">โ</button>
</div>
<div class="Gallery-desc"></div>
</div>
Apologies if this seems simple, but I am really struggling here and new to JS so not sure if I am just missing something!!!
Page: https://www.villaslegianbali.com/reviews/
I have added the sidebar to allow people to find the reviews they want. However, it appears over the footer. I tried changing the z-index (sidebar to 0, footer to 99) for now, but even that isn't working.
When I try to save the below JS, it comes up with an error:
"Your PHP code changes were rolled back due to an error on line 325 of file wp-content/themes/villalegianbali/functions.php. Please fix and try saving again.
syntax error, unexpected 'var' (T_VAR), expecting end of file"
(line 325 is the first line of the JS above)
Code:
//JS I added to the functions.php file:
var sideNav = document.querySelector('.sidenav');
var footer = document.querySelector('.footer');
function checkOffset() {
function getRectTop(el) {
var rect = el.getBoundingClientRect();
return rect.top;
}
if ((getRectTop(sideNav) + document.body.scrollTop) + sideNav.offsetHeight >= (getRectTop(footer) + document.body.scrollTop) - 10)
sideNav.style.position = 'absolute';
if (document.body.scrollTop + window.innerHeight < (getRectTop(footer) + document.body.scrollTop))
sideNav.style.position = 'fixed'; // restore when you scroll up
sideNav.innerHTML = document.body.scrollTop + window.innerHeight;
}
document.addEventListener("scroll", function() {
checkOffset();
});
.footer {
height: 243px;
z-index: 999 !important
}
.sidenav {
width: 180px;
position: fixed;
bottom: 0;
left: 30px;
background-color: #fff;
overflow: hidden;
padding: 10px;
}
<div class="sidenav-parent">
<div class="sidenav reviews-menu" style="position: fixed; float: left; bottom:0; margin-left: -30px; margin-bottom: 15px">
<h4 class="reviews-menu-title">Choose your Villa:</h4>
<a href="#karma" margin="0">
<h3 class="reviews-menu-item">Villa Karma Legian</h3>
</a>
<a href="#poppy" margin="0">
<h3 class="reviews-menu-item">Villa Poppy Legian</h3>
</a>
<a href="#aniela" margin="0">
<h3 class="reviews-menu-item">Villa Aniela</h3>
</a>
<a href="#segara" margin="0">
<h3 class="reviews-menu-item">Villa Segara Legian</h3>
</a>
<a href="#tropical" margin="0">
<h3 class="reviews-menu-item">Tropical House</h3>
</a>
<a href="#zakira" margin="0">
<h3 class="reviews-menu-item">Villa Zakira Legian</h3>
</a>
</div>
</div>
(content)
Please put below CSS
#media screen and (min-width: 980px) {
.footer {
z-index: 99 !important;
position: absolute;
}
}
We can't directly put Javascript in the functions.php file. To add JS first save in the .js file and include it in the functions.php file.
For example. Follow below steps
1] Create a .js file as name scrollToTop.js
2] Put the below code in that JS file
var sideNav = document.querySelector('.sidenav');
var footer = document.querySelector('.footer');
function checkOffset() {
function getRectTop(el){
var rect = el.getBoundingClientRect();
return rect.top;
}
if((getRectTop(sideNav) + document.body.scrollTop) + sideNav.offsetHeight >= (getRectTop(footer) + document.body.scrollTop) - 10)
sideNav.style.position = 'absolute';
if(document.body.scrollTop + window.innerHeight < (getRectTop(footer) + document.body.scrollTop))
sideNav.style.position = 'fixed'; // restore when you scroll up
sideNav.innerHTML = document.body.scrollTop + window.innerHeight;
}
document.addEventListener("scroll", function(){
checkOffset();
});
3] Put the below code in the functions.php file and check.
function villas_adding_scripts_for_scroll() {
wp_register_script('villas_scrolltop_script', get_template_directory_uri() . '/scrollToTop.js', array('jquery'),'1.1', true);
wp_enqueue_script('villas_scrolltop_script');
}
add_action( 'wp_enqueue_scripts', 'villas_adding_scripts_for_scroll', 999 );
I have slider and when i mouseover on slider play button is displaying, but slider images are inside a tag and when play button is not hidden i can't click on images inside a tag. i tried set same z-index for both (slider images and play button) but still not working
i need to click on play button when it shown and go to link placed bottom of this play button
if it is possible please help, and sorry for my bad english.
Main question: how can i click on play button with and redirect to link placed inside a tag?
Here is image how slider looks like onmouseover and image when mouse is out of slider
here is my html code:
<style type="text/css">
#slider-play-button-container{
position: absolute;
z-index: 2;
left: 0;
right: 0;
text-align: center;
cursor: pointer;
}
#slider-play-button{
position: relative;
top: 25vh;
width: 2vw;
opacity: 0;
}
.slide-img{
width: 100%;
height: 55vh;
object-fit: cover;
border-radius: .7vw;
overflow:hidden;
}
</style>
<main class=content>
<span id="slider-play-button-container"><img src="https://i.imgur.com/md7vyI8.png" id="slider-play-button"></span>
<div id="slider">
<a href="Link to go after play button click" target="_Blank">
<h3 class="slider-movie-name">แฏแแ แแแแ: III แแแแ - แแแ แแแแแฃแแ</h3>
<img src="https://i.imgur.com/OP3AITl.jpg" class="slide-img">
</a>
<a href="Another link to go after play button click" target="_Blank">
<h3 class="slider-movie-name">แจแฃแ แแกแแแซแแแแแแแ: แแแกแแกแ แฃแแ</h3>
<img src="https://i.imgur.com/3vDzVHa.jpg" class="slide-img">
</a>
</div>
</main>
<script>
function bid(n){return document.getElementById(n)}
function qs(n){return document.querySelector(n)}
function qsa(n){return document.querySelectorAll(n)}
let slider = bid('slider');
let arrowTop = bid('slide_arrow_top');
let arrowBottom = bid('slide_arrow_bottom');
let sliderImage = qsa('.slide-img');
let sliderPlayButtonContainer = bid('slider-play-button-container');
let sliderPlayButton = bid('slider-play-button');
let count = 0;
let imageOffset = 0;
let imgOffset = 0;
var slideInterval;
let sliderImageOffset;
/* autoscroll */
window.addEventListener('load',winLoadForSlide);
function winLoadForSlide(){
/* slider */
slider.addEventListener('wheel',slideMouseScroll);
arrowBottom.addEventListener('click',scrollBottom);
arrowTop.addEventListener('click',scrollTop);
function bottomSlide(){
if (count < 4) {
count++;
}
imageOffset = sliderImage[count].offsetTop;
slider.scrollTo(0,imageOffset);
}
function topSlide(){
if (count > 0) {
count--;
}
imageOffset = sliderImage[count].offsetTop;
slider.scrollTo(0,imageOffset-5);
}
function slideMouseScroll(){
if (event.deltaY < 0){
topSlide();
}else if (event.deltaY > 0){
bottomSlide();
}
}
function scrollBottom(){
bottomSlide();
}
function scrollTop(){
topSlide();
}
slideInterval = setInterval(repeatScroll,100 * 20);
function showSliderPlayButton(){
sliderPlayButton.style.transform = "scale(5)";
sliderPlayButton.style.opacity = "1";
sliderPlayButton.style.transition = "250ms";
}
function hideSliderPlayButton(){
sliderPlayButton.style.transform = "scale(1)";
sliderPlayButton.style.opacity = "0";
sliderPlayButton.style.transition = "250ms";
}
[slider,arrowBottom,arrowTop,sliderPlayButtonContainer,sliderPlayButton].forEach(slideElements => {
slideElements.addEventListener('mouseover',()=>{
clearInterval(slideInterval);
});
slideElements.ondragstart = function(){ return false; }
});
[slider,sliderPlayButtonContainer,sliderPlayButton].forEach(slideElementsWithoutButtons => {
slideElementsWithoutButtons.addEventListener('mouseover',()=>{
showSliderPlayButton();
});
});
slider.addEventListener('mouseleave',()=>{
slideInterval = setInterval(repeatScroll,100 * 20);
hideSliderPlayButton();
});
function repeatScroll(){
if( (slider.scrollHeight - slider.scrollTop - slider.clientHeight) !== 4 ){
if (imgOffset < 4) {
imgOffset++;
}
sliderImageOffset = sliderImage[imgOffset].offsetTop;
slider.scrollTo(0,sliderImageOffset);
}else{
imgOffset = 0;
slider.scrollTo(0,0);
}
}
/* END slider */
}
/* END autoscroll */
</script>
There are a few ways to get around this problem.
One would involve getting rid of the anchor tags altogether, grouping each image inside a single container and assigning a click event listener to each one to ultimately open the link. If you then add another click listener to the arrow button which executes event.preventDefault(); the click event will be passed through to the object below - the <div> including your image.
If you want to keep the anchor tags, things are a little tricky. Luckily there are some helpful JavaScript functions, foremost document.elementsFromPoint(x,y).
If you feed the current mouse coordinates to this function - e.g. by clicking on the arrow button - it will return an array of objects below this point.
This array contains the anchor element in the background, so it's just a matter of picking it out of the array, get the link assigned to it and open it using the window.open() command.
Here's an example:
function bid(n) {
return document.getElementById(n)
}
let sliderPlayButtonContainer = bid('slider-play-button-container');
let sliderPlayButton = bid('slider-play-button');
sliderPlayButtonContainer.addEventListener('click', (event) => {
var list = document.elementsFromPoint(event.clientX, event.clientY)
var anchorElement = list.find(element => element instanceof HTMLImageElement && element.className == 'slide-img').parentElement;
window.open(anchorElement.href, anchorElement.target);
});
function showSliderPlayButton() {
sliderPlayButton.style.transform = "scale(5)";
sliderPlayButton.style.opacity = "1";
sliderPlayButton.style.transition = "250ms";
}
sliderPlayButtonContainer.addEventListener('mouseover', () => {
showSliderPlayButton();
});
#slider-play-button-container {
position: absolute;
z-index: 2;
left: 0;
right: 0;
text-align: center;
cursor: pointer;
}
#slider-play-button {
position: relative;
top: 25vh;
width: 2vw;
opacity: 1;
}
.slide-img {
width: 100%;
height: 55vh;
object-fit: cover;
border-radius: .7vw;
overflow: hidden;
}
<span id="slider-play-button-container"><img src="https://i.imgur.com/md7vyI8.png" id="slider-play-button"></span>
<div id="slider">
<a href="https://www.startpage.com" target="_blank">
<h3 class="slider-movie-name">แฏแแ แแแแ: III แแแแ - แแแ แแแแแฃแแ</h3>
<img src="https://i.imgur.com/OP3AITl.jpg" class="slide-img">
</a>
</div>
parentElement property helped a lot to solve my problem
playButtonATagHref = sliderImage[imgOffset].parentElement.href;
sliderPlayButton.addEventListener('click',()=>{
window.location.href = playButtonATagHref;
});
So I have to code a button that uses z-index to go to the next picture in the slideshow. I am having difficulty trying to get it to work and I feel as though I am doing something wrong. It has to have a count of 0, 1, 2, 0, 1, 2
<!DOCTYPE html>
<html lang = "en">
<head>
<title>Lab 5, Part 1</title>
<meta charset = "utf-8"/>
<script type = "text/javascript">
function Next() {
document.getElementById('anime1').style.zIndex = 0;
document.getElementById('anime2').style.zIndex = 1;
document.getElementById('anime3').style.zIndex = 2;
}
</script>
<style type = "text/css">
.anime1 {position: absolute;
top: 150px; left: 250px; z-index: 10;}
.anime2 {position: absolute;
top: 200px; left: 300px; z-index: 15;}
.anime3 {position: absolute;
top: 250px; left: 350px; z-index: 20;}
</style>
</head>
<body>
<h1 style= "text-align: center">Lab 5, Part 1</h1>
<p>
<div class="slideshow">
<img class = "anime1" id = "anime1" height = "300"
width = "450" src = "http://images5.fanpop.com/image/photos/29300000/Megurine-Luka-megurine-luka-29391390-1680-1050.jpg"
alt = "First Image"/>
<img class = "anime2" id = "anime2" height = "300"
width = "450" src = "http://orig06.deviantart.net/a28f/f/2015/079/9/a/hinata_final_lr_by_artgerm-d8me6vb.jpg"
alt = "Second Image"/>
<img class = "anime3" id = "anime3" height = "300"
width = "450" src = "http://images6.fanpop.com/image/photos/35700000/Hatsune-Miku-snowangel_-35736242-1600-1200.jpg"
alt = "Third Image"/>
</p>
<input type="button" value="Next" onclick="Next();">
</body>
</html>
I have looked every where online to see if anything could help me but I can't find anything
If you're willing to take a more programmatic approach, you can use an array to hold the order and iterate it to set the z-indexes.
Using this method you can
pop() the item from the end of the array and unshift() it onto the beginning, or
shift() the item from the beginning of the array and push() it onto the end.
Which allows you to easily handle any number of elements while keeping your code DRY.
I've taken the liberty of making a back button as well as the next button, to show you how easy it is when approaching it this way. I've also generalized the class names and used different placeholder images for the demo.
(function(){ // keep it safe
var slideshow = document.querySelector('.slideshow'); // store the parent
var controls = slideshow.querySelector('.controls'); // store the controls
var els = slideshow.querySelectorAll('.slide'); // store the slides
var order = Object.keys(els); // store the order
var cn; // make the class holder
// assign a click handler to the parent
controls.onclick = function(e) {
// if the class is back or next, store it, otherwise stop here
if(!(cn = (e.target.className.match(/back|next/)||[false])[0])) return;
// if back clicked, move the last element to the beginning
if(cn === "back") order.unshift(order.pop());
// if next clicked, move the first element to the end
if(cn === "next") order.push(order.shift());
// iterate the order, set the z-index of each element sequentially
for(var i in order) els[order[i]].style.zIndex = i;
}
})();
.slides { position: relative; margin-top: 5px; }
.slide { position: absolute; }
.slide2 { top: 25px; left: 25px; }
.slide3 { top: 50px; left: 50px; }
<div class="slideshow">
<div class="controls">
<button class="back">Back</button>
<button class="next">Next</button>
</div>
<div class="slides">
<img class="slide slide1" src="http://placehold.it/150x150/f9fd42/fff">
<img class="slide slide2" src="http://placehold.it/150x150/42f9fd/fff">
<img class="slide slide3" src="http://placehold.it/150x150/fd42f9/fff">
</div>
</div>
Further Reading
Array.prototype.pop() (MDN)
Array.prototype.unshift() (MDN)
Array.prototype.shift() (MDN)
Array.prototype.push() (MDN)
Don't Repeat Yourself (Wikipedia)
Are you looking for something like this: https://jsfiddle.net/5L7jk73g/
var cnt = 0;
function Next() {
if (cnt == 0) {
document.getElementById('anime1').style.zIndex = 0;
document.getElementById('anime2').style.zIndex = 1;
document.getElementById('anime3').style.zIndex = 2;
cnt++;
} else if (cnt == 1) {
document.getElementById('anime1').style.zIndex = 1;
document.getElementById('anime2').style.zIndex = 2;
document.getElementById('anime3').style.zIndex = 0;
cnt++;
} else {
document.getElementById('anime1').style.zIndex = 2;
document.getElementById('anime2').style.zIndex = 0;
document.getElementById('anime3').style.zIndex = 1;
cnt = cnt - 2;
}
}
Good morning.
First, thanks in advance! I've been a stack overflow spectator for quite a while, and you guys are great.
I am looking to create a photo layout for my webpage www.eden-koru.com, where photos are presented in rows. Due to cropping, and different cameras, each photo may have different aspect ratios and therefor there are many uneven gaps when just placed in a row.
A perfect example of what I want to do is www.flickr.com/childe-roland. Those are my photos, all laid out perfectly despite aspect ratio.
On a different, but similar question I found an 80% solution with this JSFiddle http://jsfiddle.net/martinschaer/aJtdb/:
var container_width = $('#container2').width();
var container_width_temp = 0.0; // must be float!
var container_height = 100.0; // random initial container heigth for calculations
$('#container2 img').each(function(){
var newwidth = (this.width / this.height) * container_height;
this.width = newwidth;
$(this).data('width', newwidth);
container_width_temp += newwidth;
});
$('#container2 img').each(function(){
this.width = $(this).data('width') * (container_width / container_width_temp);
});
Now, that only works for one row. I have no experience with JQuery, but I was able to see the math and create a "row_counter" variable that counted the number of image wrapper divs... That got me to 90%. I just multiplied the final width by that row count, then subtracted a few pixels to make up for margins.
it looks like this:
$('.imageWrapper').each(function(){
rows +=1;
});
My div layout looks like this:
<div class="mainWrapper">
<div class="imageWrapper">
<img width="326" src="images/_DSC4434.jpg"></img>
<img width="276" src="images/_DSC4537.jpg"></img>
<img width="254" src="images/_DSC4483.jpg"></img>
</div>
<div class="imageWrapper">
<img width="276" src="images/_DSC0253.jpg"></img>
<img width="306" src="images/The_Alaska_RangeIR.jpg"></img>
<img width="275" src="images/DSC_9111.jpg"></img>
</div>
<div class="imageWrapper">
<img width="276" src="images/_DSC4689.jpg"></img>
<img width="276" src="images/_DSC4718.jpg"></img>
<img width="276" src="images/_DSC4738.jpg"></img>
</div>
</div>
and my CSS like this:
.mainWrapper {
background-color: black;
margin: 0 auto 50px auto;
width: 70%;
height: auto;
border: 2px solid white;
border-radius: 10px;
clear: both;
padding: 7px 7px 7px 7px;
text-align: center;
overflow: hidden;
}
.mainWrapper .imageWrapper {
overflow: hidden;
width: 100%x;
margin: 0px auto 0px auto;
}
.mainWrapper .imageWrapper img {
display: inline-block;
border: 1px solid #fff;
}
Now, it looks better than it did, but there is still a lot of unevenness that I can't account for with styling. Additionally I can no longer use width: 100% to make my images shrink as the viewport changes.
I hope that I have given enough detail. Please keep in mind that I know nothing about JQuery and haven't touched JavaScript in 5 years. I was an IT major who joined the navy after graduation and never coded again until last week.
Cheers!
Wes
This is something quite complex. I managed to make a jQuery plugin that almost achieves what you want, I'm having some issues with making it dynamic when a user resizes their browser window, but ignoring this, it should do what you're asking for.
jQuery Plugin
(function ( $ ) {
$.fn.gallery = function( options ) {
var settings = $.extend({
imgs: [],
row_height: 300,
margin: 10
}, options);
var container = $(this);
//create a div for each image
for(var i=0;i<settings.imgs.length;i++){
$(this).append("<div class='imgwrapper'></div>");
}
//setup the css for the imgwrappers
$("head").append("<style type='text/css'>.imgwrapper{ float: left; margin-left: "+settings.margin+"px; margin-top: "+settings.margin+"px; height: 261px; background-repeat: no-repeat; background-position: center; background-size: cover;}</style>")
//define some global vars
var imgs_aspect = [];
var imgs_rows = [0];
var tot = 0;
var loaded = 0;
function setup(){
var imgs = settings.imgs;
var row_width = 0;
$(".imgwrapper").each(function(index){
var imgwrapper = $(this);
var img = new Image();
img.src = imgs[index];
img.onload = function(){
//determine the aspect ratio of the image
var img_aspect = img.height/img.width;
imgs_aspect.push(img_aspect);
//work out a rough width for the image
var w = settings.row_height*img_aspect;
row_width += w;
//check if there is still space on this row for another image
if(row_width >= container.width()){
imgs_rows.push(1);
row_width = 0;
}
else{
imgs_rows[imgs_rows.length-1]++;
}
//set some of the css vars
imgwrapper.css("width",w+"px");
imgwrapper.css("height",settings.row_height+"px");
imgwrapper.css("background-image","url("+imgs[index]+")");
loaded++;
checkIfLoaded();
}
});
}
function checkIfLoaded(){
//make sure all images are loaded
if(loaded == $(".imgwrapper").length){
setHeight();
}
}
function setHeight(){
for(var r=0;r<imgs_rows.length;r++){
if(r==0){
var y = 0;
}
else{
var y = 0;
for(var j=0;j<r;j++){
y += imgs_rows[j]
}
}
if(imgs_rows[r] == 0){
}
else{
tot = 0;
for(var i=y;i<(y+imgs_rows[r]);i++){
tot += imgs_aspect[i];
}
//work out optimum height of image to fit perfectly on the row
var h = ((container.width()-(settings.margin*(imgs_rows[r]+1)))/tot);
$(".imgwrapper").each(function(index){
if(index >= y && index < (y+imgs_rows[r])){
//work out width using height
var w = h*imgs_aspect[index];
$(this).css("width",w+"px");
}
});
}
}
}
setup();
};
}( jQuery ));
How To Use
var images = ["http://lorempixel.com/300/300",
"http://lorempixel.com/250/250",
"http://lorempixel.com/200/200",
"http://lorempixel.com/210/220",
"http://lorempixel.com/210/230",
"http://lorempixel.com/260/230",
"http://lorempixel.com/410/830",
"http://lorempixel.com/300/200",
"http://lorempixel.com/250/250",
"http://lorempixel.com/200/200",
"http://lorempixel.com/210/220",
"http://lorempixel.com/210/230",
"http://lorempixel.com/260/230",
"http://lorempixel.com/410/830"];
$(".container").gallery({imgs:images, margin: 0, row_height: 300});
images is an array which should contain the images url that you wish to use. The container can have any width desired (define in the css). The margin value allows you to have a similar white border around the images. Since I saw in your code that you had a row height, this is also implemented, by just changing the row_height value.
Demo: http://codepen.io/motorlatitude/pen/iHgCx
This is far from perfect, but it might give you an idea of what you need to do.
Hope it helps!