Weird mobile nav animation bug - javascript

CodePen of the nav
On the first interaction with the mobile nav, it will open and close as expected but anything after that and there's a bug. It will begin to open and close instantly or the links will appear is weird orders.
What I need is for the mobile nav to first open from right to left, have each of the links to cascade into the view, starting from About all the way to Blog, and then I would like it to reverse when leaving the view.
Right now I don't have the logic implemented for the reverse but I need to work out this bug before I get to that.
SNIPPET
const bars = document.querySelector('.fa-bars');
bars.addEventListener('click', () => {
const navItemsContainer = document.querySelector('.navbar__links-container');
const navItems = document.querySelectorAll('.navbar__links-container__item');
const sleep = ms => {
return new Promise(resolve => {
setTimeout(() => {
return resolve();
}, ms);
});
};
const startNavAnimation = async () => {
let count = 0;
for (let item of navItems) {
if (item.classList.contains('navbar__links-container__item--show')) {
setTimeout(() => {
item.style.transitionDelay = `${ count }s`
item.classList.remove('navbar__links-container__item--show');
count += .15;
}, count);
}
else {
item.style.transitionDelay = `${ count }s`
item.classList.add('navbar__links-container__item--show');
count += .15;
}
}
};
if (navItemsContainer.classList.contains('navbar__links-container--open')) {
navItems[ navItems.length - 1 ].addEventListener('transitionend', () => {
navItemsContainer.classList.remove('navbar__links-container--open');
});
}
else {
navItemsContainer.classList.add('navbar__links-container--open');
}
startNavAnimation();
});
body {
margin: 0;
}
.navbar {
background: #f2f2f2;
display: flex;
flex-wrap: wrap;
}
.navbar__mobile-container {
display: flex;
justify-content: space-around;
width: 100%;
}
.fa-bars {
cursor: pointer;
}
.navbar__links-container {
background: inherit;
display: flex;
flex-direction: column;
align-items: flex-end;
list-style: none;
margin: 0;
overflow: hidden;
padding: 0;
position: absolute;
top: 20px;
left: 100%;
transition: left .25s, width .25s;
width: 0%;
}
.navbar__links-container__item {
left: 52px;
margin-bottom: 1rem;
position: relative;
transition: left .25s;
width: auto;
}
.navbar__links-container--open {
left: 0;
width: 100%;
}
.navbar__links-container__item--show {
left: -63px;
}
<nav class="navbar">
<div class="navbar__mobile-container">
<div class="navbar__logo-container">
<a class="navbar__logo-container__logo">BB</a>
</div>
<div class="navbar__hamburger-container">
<i class="fas fa-bars">MENU</i>
</div>
</div>
<ul class="navbar__links-container">
<li class="navbar__links-container__item">
<a class="navbar__links-container__item__link">About</a>
</li>
<li class="navbar__links-container__item">
<a class="navbar__links-container__item__link">Portfolio</a>
</li>
<li class="navbar__links-container__item">
<a class="navbar__links-container__item__link">Blog</a>
</li>
</ul>
</nav>
NOTES
I think the problem is the first if statement in the bars event-handler. Something about the way it's waiting for the transitionend event but the startNavAnimation hasn't been called.

There are two issues.
One is that you are adding a new event listener inside of the click event listener. I moved that outside.
The second issue is that the --open class is going to be there while the menu is opening or closing so you need another way to test open or closed status. To make the Codepen clear to understand, I just used an isOpen flag.
https://codepen.io/Jason_B/pen/jzGwQX?editors=0010
I like using classes for this, and your code shows that you do too, so you might want to have a class for open status and a class for visibility.

Related

Marquee Slider lacks after few iterations

I have created a marquee slider. After a few iterations the marquee lacks.
I want to have the marquee to have full view-width. As I can see the marquee is lacking less on smaller devices. I have created other similar marquees before with different elements and never had that problem. The empty list elements I have implemented to have a spacing between the text elements, but the icons should be next to the text as they are currently.
(function marquee() {
var marqueeWrapper = $('.js-marquee');
var FPS = (60/100); // 60fps
var SLIDESPEED = 1000; // default | lower is faster
marqueeWrapper.each(function (index, element) {
var _marqueeWrapper = $(element);
var _marqueeTracks = $('>ul', _marqueeWrapper);
var _marqueeSlides = $('>ul>li', _marqueeWrapper);
var _marqueeWidth = parseFloat(_marqueeSlides.last().position().left + _marqueeSlides.last().outerWidth(true));
var shifted = _marqueeWrapper.attr('data-marquee-shift') || false;
var SPEED = (_marqueeWrapper.attr('data-marquee-speed') * _marqueeSlides.length) || (SLIDESPEED * _marqueeSlides.length);
var frames = SPEED * FPS;
var steps = _marqueeWidth / frames; // distance elems will move each frames
var posX = 0;
var tempSteps;
function _clone() {
var times = Math.ceil(_marqueeWrapper.outerWidth(true) / _marqueeWidth) + 1;
_marqueeTracks.each(function () {
$('>ul', _marqueeWrapper).empty();
var sliders = _marqueeSlides;
for (i = 1; i <= times; i++) {
sliders.clone().appendTo(($(this)));
}
})
}
function _animated() {
posX += -steps;
_marqueeTracks.css({
transform: 'translate3d(' + posX + 'px, 0, 0)'
});
if (Math.abs(posX) >= _marqueeWidth) {
posX = 0;
}
window.requestAnimationFrame(_animated);
}
function _pause() {
tempSteps = steps;
return steps = 0;
}
function _resume() {
return steps = tempSteps;
}
function _shiftPosition() {
if(shifted) return posX = -(_marqueeSlides.first().outerWidth(true)) / 2 ;
}
/*
function _registerEvents() {
_marqueeTracks.on('mouseenter', _pause);
_marqueeTracks.on('mouseleave', _resume);
$(window).on('resize', debounce(_clone, 300))
}*/
function init() {
_shiftPosition()
_clone();
_animated();
/*_registerEvents();*/
}
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};// debounce
init();
})
})();
.marquee {
background: black;
width: 100%;
overflow: hidden;
}
.marquee__track {
display: flex;
padding: 0;
margin: 0;
list-style: none;
}
.marquee__item {
display: flex;
justify-content: center;
align-items: center;
color: white;
height: 100px;
margin-right: 10px;
padding-bottom: 10px;
}
.marquee__item {
height: 80px;
}
.marquee__item_vegan {
flex: 0 0 120px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_gluten {
flex: 0 0 160px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_natural {
flex: 0 0 130px;
font-size: 50px;
font-family: Gothic821;
}
.marquee__item_empty {
flex: 0 0 180px;
}
.marquee__item_small {
flex: 0 0 80px;
}
.marquee__item_icon {
width: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-marquee-speed="100" data-marquee-shift="false" class="marquee js-marquee">
<ul class="marquee__track">
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_vegan">VEGAN</li>
<li class="marquee__item marquee__item_empty"></li>
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item marquee__item_empty"></li>
<li class="marquee__item marquee__item_small"><img class="marquee__item_icon" src="{{ 'Haken_weiss.svg' | asset_url }}"/></li>
<li class="marquee__item marquee__item_natural">NATURAL</li>
<li class="marquee__item marquee__item_empty"></li>
</ul>
</div>
The given code seems rather complex for a simple marquee.
While you will need to invoke a little JS at the start (on a load or a resize) to make sure the timing is right for the speed required (it varies depending on viewport width) there seems little to be gained from using JS for the actual animation.
And there may be some loss in using JS which does not guarantee real-time timing (your system may be busy doing other things) and that can introduce lag. Using a CSS method can help to smooth things as there is less need to move back and forth between CPU and GPU.
The given code also performs the animation in steps, moving position, which can be jerkier than translating.
This snippet is stripped down to demonstrate the basic ideas in using HTML and CSS without JS.
As in the code in the question, a second copy of the items is made. This allows the marquee to appear continuous, as the first item disappears to the left it can be seen coming in from the right.
The units used are related to the viewport width so the whole thing is responsive. A CSS animation is used to get the movement, moving the whole 50% of its width, then when the copy comes in it is overwritten by the first part so the movement looks continuous (this is the same technique as used in the given code but with pure CSS rather than JS/CSS).
.marquee,
.marquee * {
margin: 0;
padding: 0;
border-width: 0;
}
.marquee {
background: black;
overflow: hidden;
}
.marquee>ul {
display: flex;
width: 200vw;
justify-content: space-around;
list-style: none;
animation: move 5s linear infinite;
white-space: nowrap;
}
.marquee>ul li {
font-size: 4vw;
color: white;
margin: 5vw;
position: relative;
}
.marquee__item_vegan,
.marquee__item_gluten,
.marquee__item_natural {
--bg: linear-gradient(white, white);
/* just for demo */
}
.marquee>ul li::before {
content: '';
width: 4vw;
height: 4vw;
left: -4vw;
display: inline-block;
position: absolute;
background-image: var(--bg);
background-repeat: no-repeat;
background-size: 80% 80%;
background-position: bottom left;
}
#keyframes move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
<div class="marquee">
<ul>
<li class="marquee__item_vegan">VEGAN</li>
<li class="marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item_natural">NATURAL</li>
<li class="marquee__item_vegan">VEGAN</li>
<li class="marquee__item_gluten">GLUTENFREI</li>
<li class="marquee__item_natural">NATURAL</li>
</ul>
</div>
Notes: you may want to reinstate some JS to calculate the timing - depends on your use case.
The spurious list items have been removed. The imgs which are really just visual clues are added as before pseudo elements. The empty items are not needed. (It's best to keep formatting and content separate, especially for those using assistive technology such as screen readers).

Toggling preventDefault() On mouseover and mouseleave of Parent Container

I have some containers that have child containers inside which contain links.
What happens is when the user hovers over the parent container to show the child, the links are disabled for the first 2 seconds. If a user moves the mouse away before clicking the link this behaviour is reset with a 'hasBeenHovered' variable that changes from true to false inside the mouseleave event.
The two issues I'm facing are:
a) I can't get it to work solely on the parent being hovered - it loops through all of them and shows them all;
b) On mobile is there anyway of returning the opacity to 1 and disabling the links again by re-tapping (so the re-tap effectively works as the mouseleave event?). If this is very complex to do I may just have it so it stays visible until the parent container hits the top of the viewport.
Although the code sandbox is below it says "Uncaught TypeError: allowLinks is not a function", yet on CodePen the demo works?
Many thanks in advance for any help.
Emily
CodePen: https://codepen.io/emilychews/pen/rNjXMee
var parent = document.querySelectorAll(".parent");
var child = document.querySelectorAll(".child");
var link = document.querySelectorAll(".link");
var hasBeenHovered = false;
var listener = function (e) {
e.preventDefault();
};
// prevent default on all specific links
link.forEach(function (item) {
item.addEventListener("click", listener);
});
// mouseover that changes opacity to 1 and removes prevent default on links
parent.forEach(function (item) {
item.addEventListener("mouseover", function (event) {
child.forEach(function (item) {
item.style.opacity = "1";
item.style.transition = "opacity .5s";
});
// remove prevent Default
if (hasBeenHovered === false) {
function allowLinks() {
link.forEach(function (item) {
item.removeEventListener("click", listener);
hasBeenHovered = true;
});
}
}
setTimeout(function () {
allowLinks();
}, 2000);
}, false );
});
// mouseleave event re-adds opacity: 0 and re-adds prevent default
parent.forEach(function (item) {
item.addEventListener("mouseleave", function (event) {
child.forEach(function (item) {
item.style.opacity = "0";
item.style.transition = "opacity .5s";
});
// re-add prevent Default
if (hasBeenHovered === true) {
link.forEach(function (item) {
item.addEventListener("click", listener);
hasBeenHovered = false;
});
}
}, false );
});
body {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100vh;
}
.parent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 20rem;
height: 20rem;
background: lightblue;
margin: 1rem;
}
.child {
opacity: 0; /* hides child container*/
padding: 1rem;
background: red;
}
a {
color: #fff;
display: block;
margin: 1rem 0;
}
<div class="parent">
<div class="child">
<a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
<a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
</div>
</div>
<div class="parent">
<div class="child">
<a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
<a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
</div>
</div>
Trying to keep most of your original code logic, I've modified quite a bit of it but this should be what your are aiming for. For the mobile part I'd recommend a flag for the touch handler, but do note it'll get a bit more complicated since mobile also responds to the onclick handlers.
The links will not work in the snippet due to StackOverflow security so added a console log but will work if you copy to CodePen
const parents = document.querySelectorAll(".parent");
parents.forEach(parent => {
const children = parent.querySelectorAll(".child");
const links = parent.querySelectorAll(".link");
let timeoutId = null; // track the timeout so we can clear it
let enableLinks = false; // should we allow links?
links.forEach(link =>
link.addEventListener("click", evt => {
if (!enableLinks) {
evt.preventDefault(); // hold them hostage
} else {
console.log('StackOverflow prevents links'); // just a placeholder for SO snippet
}
}, true)
);
parent.addEventListener("mouseover", function() {
enableLinks = false; // ensure links are disabled at first
children.forEach(child => child.style.opacity = "1"); // let the children be seen
if (!timeoutId) // make sure there isn't already a timeout
timeoutId = setTimeout(() => enableLinks = true, 2000); // enable links after the 2s
}, false);
parent.addEventListener("mouseleave", function(event) {
children.forEach(child => child.style.opacity = "0"); // hide your children
clearTimeout(timeoutId); // remove the timeout so it can't overlap
timeoutId = null; // clear timeout id
enableLinks = false; // turn off links
}, false);
});
body {
margin: 0;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100vh;
}
.parent {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 20rem;
height: 20rem;
background: lightblue;
margin: 1rem;
}
.child {
opacity: 0;
/* hides child container*/
padding: 1rem;
background: red;
transition: opacity .5s;
}
a {
color: #fff;
display: block;
margin: 1rem 0;
}
<div class="parent">
<div class="child">
<a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
<a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
</div>
</div>
<div class="parent">
<div class="child">
<a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
<a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
</div>
</div>

Function releases after page is loaded not after click

I'm trying to make a game of little rabbit's farm. My level of programming is a beginner.
Why is addRabbit() code starts to work after the page is loaded? I wrote it to work after click to "Buy Rabbit" button
Why rabbits are not shown at the page near the "Sell Rabbit" and "Buy Rabbit" buttons?
I know that I have a lot of issues here as far as I'm a beginner. Could I ask you to mention any of them?
// VARIABLES
// variables for modal of chosing rabbits
const chooseModal = document.querySelector(".choose-modal");
const selectRabbitBtn = document.querySelector(".choose-rabbit-btn");
const rabbitSelects = document.querySelectorAll("input[type=radio]");
let chosenRabbitUrl = "img/rabbit1.png";
// start screen
const startScreenDiv = document.querySelector(".story-modal");
const rabbit = document.querySelector("img.rabbit");
const buyRabbitBtn = document.querySelector(".buy-btn");
// EVENT LISTENERS
selectRabbitBtn.addEventListener("click", chooseTheRabbit);
// FUNCTIONS
function chooseTheRabbit(e){
e.preventDefault();
for (let rabbit of rabbitSelects) {
if (rabbit.checked) {
chosenRabbitUrl = `img/${rabbit.id}.png`;
break;
}
}
chooseModal.style.display = "none";
startScreen();
}
function startScreen() {
startScreenDiv.style.display = "block";
rabbit.src = chosenRabbitUrl;
}
class RabbitGame {
constructor() {
this.rabbitsCount = parseInt(document.querySelector(".rabbits-count").innerText, 10);
this.rabbitsCountSpan = document.querySelector(".rabbits-count");
this.rabbitsShowDiv = document.querySelector(".rabbits-count-show");
this.coinsCount = parseInt(document.querySelector(".coins-count").innerText, 10);
this.coinsCountSpan = document.querySelector(".coins-count");
this.sellRabbitBtn = document.querySelector(".sell-btn");
this.myRabbits = [{age: 0, src: chosenRabbitUrl, width: 50}];
};
// function, that shows rabbits on the page, that the owner have
// adult rabbits should be bigger, little rabbits smaller
showRabbits() {
// rabbit.src = chosenRabbitUrl
this.myRabbits.forEach((rabbit) => {
this.rabbitsShowDiv.innerHTML += `<img src="${rabbit.src}" width="${rabbit.width}">`});
};
// function that adds a rabbit, if the owner doesn't have any coin, rabbits eat him
addRabbit() {
console.log("Hello");
if (this.coinsCount > 1) {
// remove 1 coin
this.coinsCount -= 1
console.log(this.coinsCount);
// show 1 coin less
this.coinsCountSpan.innerText = this.coinsCount
// add 1 for rabbits age
// if the rabbit is older than 4, make him bigger on screen
this.myRabbits.forEach((rabbit) => {
rabbit.age +=1;
if (rabbit.age > 3) {
rabbit.width = 70
}
})
// add 1 more rabbit to array
this.myRabbits.push({age: 0, src: chosenRabbitUrl, width: 50});
// show 1 more rabbit
this.rabbitsShowDiv.innerHTML += `<img src="${rabbit.src}" width="${rabbit.width}">`
}
};
// функция, которая продает кролика, если он взрослый
// если нет взрослых кроликов, выводит предупреждение, что нет взрослых кроликов
}
const rabbitGame = new RabbitGame();
rabbitGame.showRabbits;
buyRabbitBtn.addEventListener("click", rabbitGame.addRabbit());
/* GENERAL */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: sans-serif;
}
h3 {
background-color: rgb(108, 165, 55);
width: 400px;
height: 30px;
padding: 5px;
color: white;
text-align: center;
}
button {
padding: 5px 20px;
background-color: rgb(194, 89, 89);
color: white;
text-transform: uppercase;
border: none;
font-weight: bold;
}
button:hover {
background-color: rgb(172, 79, 84);
}
/* END OF GENERAL */
/* CHOOSE RABBIT MODAL & RULES MODAL */
.choose-modal,
.rules-modal {
display: flex;
justify-content: center;
margin-top: 20px;
}
.choose-modal > div {
display: flex;
flex-direction: column;
}
input[type="radio"] {
opacity: 0;
}
label > img:hover {
border-bottom: 1px solid rgb(199, 199, 199);
}
ul {
list-style: none;
margin: 10px;
}
li {
margin-bottom: 5px;
}
.button-div {
display: flex;
justify-content: center;
}
/* END OF CHOOSE RABBIT MODAL & RULES MODAL */
/* START SCREEN */
/* .story-modal {
display: none;
} */
.story-modal {
background-image: url("img/neighbour.jpg");
background-size: cover;
height: 100vh;
position: relative;
}
img.rabbit {
position: absolute;
height: 40%;
top: 60%;
left: 50%;
overflow: hidden;
}
.story-modal > h3 {
position: absolute;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
top: 5%;
}
.img-overlay {
width: 100%;
height: 100%;
background: rgba(61, 61, 61, 0.3);
}
.story-modal > button {
position: absolute;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
top: 13%;
}
/* END OF START SCREEN */
/* MAIN GAME */
.main-game {
display: flex;
}
/* END OF MAIN GAME */
<div class="choose-modal">
<div>
<div class="choose-modal-header">
<h3>Please, choose the rabbit</h3>
</div>
<div>
<label for="rabbit1"><input type="radio" id="rabbit1" name="rabbits" value="rabbit1"><img src="img/rabbit1.png" width="100" alt="rabbit1"></label>
<label for="rabbit2"><input type="radio" id="rabbit2" name="rabbits" value="rabbit2"><img src="img/rabbit2.png" width="100" alt="rabbit2"></label>
<label for="rabbit3"><input type="radio" id="rabbit3" name="rabbits" value="rabbit3"><img src="img/rabbit3.png" width="80" alt="rabbit3"></label>
</div>
<div class="button-div">
<button type="submit" value="Choose" class="choose-rabbit-btn">Choose</button>
</div>
</div>
</div>
<div class="story-modal">
<div class="img-overlay"></div>
<h3>Your pretty neighbour gave you a rabbit</h3>
<img alt="rabbit" src="#" class="rabbit">
<button>Rules</button>
</div>
<div class="rules-modal">
<div>
<h3>Rules of game</h3>
<div>
<ul>
<li>Buy grass to feed the rabbits</li>
<li>Sell adult rabbits</li>
<li>Buy new little rabbits</li>
</ul>
</div>
<div class="button-div">
<button>Play</button>
</div>
</div>
</div>
<div class="main-game">
<div class=" navbar">
<p><img src="img/coin.png" height="20"> Coins <span class="coins-count">10</span></p>
<p class="name-of-gamer">Anonymous</p>
<p><img src="img/rabbit.png" height="20"> Rabbits <span class="rabbits-count">3</span></p>
</div>
<div class="rabbits-count-show"></div>
<div>
<button class="sell-btn">Sell Rabbit</button>
<button class="buy-btn">Buy Rabbit</button>
</div>
</div>
Changing the line
buyRabbitBtn.addEventListener("click", rabbitGame.addRabbit());
to
buyRabbitBtn.addEventListener("click", rabbitGame.addRabbit);
(simply removing those parenthesis) should work. When you add the parenthesis, a function gets called, and that's not what you want. When the button is clicked the eventlistener call the your function itself. as of your second problem you have to add parenthesis to the function to get called thus executing the code inside it.
NOTE: Do not post unnecessary files to a question. Try find the location that error could possibly occur. BTW, CSS has no contributing factor to a error in js code. sometimes html does. I know as a beginner it's hard to find the areas causing the error but it gets easier over time.

Changing images with right or left arrow key

I've built this gallery
https://jsfiddle.net/ramamamagagaulala/do4yLxcz/
let images = document.querySelectorAll('.work-item');
let best = document.querySelector('.work-modal');
let main = document.querySelector('.work-modal__item');
console.log(images)
let closeButton = document.getElementById("closee");
images.forEach(function(ref) {
ref.addEventListener('click', function(){
let newImage = this.getElementsByTagName('img')[0].src;
best.classList.add('work-modal--show');
main.style.backgroundImage = `url( ${newImage} )`;
})
})
closeButton.addEventListener('click', function() {
best.classList.remove('work-modal--show');
});
basically, it works like this:
you click an item.
JavaScript checks what IMG this item contains.
a modal window opens up.
then the IMG that is associated with the item, is going to be displayed as the background image of this modal.
So far so good, however, I would like to build a function so I can press the arrow keys on my keyboard and the next image is going to be displayed.
What I've tried is to select the IMG of the nextSibling while clicking. Then I have used this variable to set up the background image of the modal window. But this only worked once.
Any ideas what to try next?
I would suggest have list of images urls in an array in .js file, and then you show one modal, click right/left and just change img src value to next/previous array element, untill get to either end of array.
There are three things we need to do for this problem
Storing the image source in an array
Keep track of the position of the image index
Add an event listener to track the keypress for next & prev button on your keyboard
let images = document.querySelectorAll('.work-item');
let best = document.querySelector('.work-modal');
let main = document.querySelector('.work-modal__item');
let closeButton = document.getElementById("closee");
let currentIndex = -1;
let imgSrc = [];
images.forEach(function(ref,index) {
imgSrc.push(ref.children[0].getAttribute("src"));
ref.addEventListener('click', function(){
let newImage = this.getElementsByTagName('img')[0].src;
best.classList.add('work-modal--show');
main.style.backgroundImage = `url( ${newImage} )`;
currentIndex = index
});
})
closeButton.addEventListener('click', function() {
best.classList.remove('work-modal--show');
});
let doc = document.getElementById("work");
window.addEventListener("keydown", event => {
if(event.keyCode === 39){
// next event
if(currentIndex < imgSrc.length -1 ){
main.style.backgroundImage = `url( ${imgSrc[currentIndex+1]} )`;
currentIndex=currentIndex+1;
} else {
alert("Reached last image")
}
} else if(event.keyCode === 37){
// prev event
if(currentIndex > 0){
main.style.backgroundImage = `url( ${imgSrc[currentIndex-1]} )`;
currentIndex=currentIndex-1;
} else {
alert("Reached first image")
}
}
});
.work-container{
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 1rem;
}
img {
width: 250px;
}
.work-item__img{
width: 100%;
height: 100%;
display: block;
transition: all 1s linear;
opacity: 1;
object-fit: cover;
transform: scale(1.1);
}
/* modal */
.work-modal{
display: none;
}
.work-modal--show{
position: fixed;
background: rgba(0,0,0,0.5);
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 999;
display: grid;
justify-content: center;
align-items: center;
}
.work-modal__item{
height: 70vh;
width: 80vw;
border:0.5rem solid var(--yellow);
border-radius: 0.4rem;
}
#media screen and (min-width:768px){
.work-modal__item{
height: 80vh;
width: 60vw;
}
}
.work-modal__close{
position: fixed;
font-size: 3rem;
color: var(--brightYellow);
bottom: 5%;
right: 5%;
transition: color 0.5s linear;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.work-modal__close:hover{
color: red;
}
<section class="work section-padding" id="work">
<div class="work-container">
<div class="work-item item-1">
<img src="https://images.pexels.com/photos/2683138/pexels-photo-2683138.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="" class="work-item__img">
</div>
<div class="work-item item-2">
<img src="https://images.pexels.com/photos/2736220/pexels-photo-2736220.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="" class="work-item__img">
</div>
<div class="work-item item-3">
<img src="https://images.pexels.com/photos/2928178/pexels-photo-2928178.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500" alt="" class="work-item__img">
</div>
</div>
</section>
<div class="work-modal">
<div class="work-modal__item"></div>
<div class="work-modal__close">
<i id="closee" class="fas fa-window-close">close</i>
</div>
</div>
JS Fiddle
https://jsfiddle.net/aamin89/b5wp3kez/1/

z-index not working on Safari when manipulating it using Javascript

I am making a website that has multiple layers to it, which is brought back and forth by manipulating the z-index in Javascript through event reactions (like onclick). I have a navigation bar which has a large z-index value compared to every other element as I would like it to be at the very front regardless of anything. However, when run on Safari, the nav bar disappears from the get go, while it works fine on Google Chrome and FireFox.
I have included the css code and javascript code that dictates this role:
JAVASCRIPT:
//Global variables representing DOM elements
var introTitleElem = document.getElementById('introduction-title');
var resumeElem = document.getElementById('resume-container');
var introElem = document.getElementById('intro-content');
var eduElem = document.getElementById('edu-content');
//Layer tracker (for transition effect)
var prev = introElem;
var prevButton = "";
//Function that actually changes the layers
function changeLayer(layer, button) {
if (layer === prev) return;
introTitleElem.style.opacity = "0";
prev.style.zIndex = "40";
layer.style.zIndex = "50";
layer.style.cssText = "opacity: 1; transition: opacity 0.5s";
prev.style.zIndex = "5";
prev.style.opacity = "0";
prev = layer;
if (prevButton !== "") prevButton.style.textDecoration = "none";
button.style.textDecoration = "underline";
prevButton = button;
}
//Manages events triggered by name button toggle
function revealResume() {
introTitleElem.style.zIndex = "0";
resumeElem.style.zIndex = "10";
resumeElem.style.opacity = "1";
introElem.style.opacity = "1";
resumeElem.style.transition = "opacity 0.7s";
introElem.style.transition = "opacity 0.7s";
}
document.getElementById("name-title").addEventListener("click", revealResume);
//Manage z-index of different components of the resume and reveal them accordingly
$('#nav-education').click(function () {
onEducation = true;
changeLayer(eduElem, this);
});
CSS (SASS):
/*NAVIGATION STYLING*/
#fixed-nav {
align-self: flex-start;
overflow-x: hidden;
float: right;
z-index: 9999 !important;
display: flex;
margin: 1em;
li {
margin: 0.6em;
font: {
family: $font-plex-sans-condensed;
size: 0.8em;
}
text-align: center;
color: $lightest-grey;
transition: color 0.3s;
&:hover {
cursor: pointer;
color: $dark-grey;
transition: color 0.3s;
}
}
}
/*OVERALL DIV FORMATTING*/
.format-div {
width: 100vw;
height: 100vh;
display: flex;
position: absolute;
justify-content: center;
align-items: center;
flex-direction: column;
opacity: 0;
}
/*EDUCATION CONTENT STYLING*/
#edu-content {
background-color: $red-1;
}
HTML:
<div id="resume-container">
<ul id="fixed-nav">
<li id="nav-education">education.</li>
<li id="nav-experiences">experiences.</li>
<li id="nav-skills">skills.</li>
<li id="nav-projects">projects.</li>
<li id="nav-additional">additional.</li>
<li id="nav-contact">contact.</li>
</ul>
<div id="intro-content" class="format-div">
<h1 class="type-effect">
<h1>I'm Daniel <b>(Sang Don)</b> Joo</h1>
<span class="blinking-cursor">|</span>
</h1>
</div>
<div id="edu-content" class="format-div">
<h1>education</h1>
</div>
Sorry about the large amount of code but I'm really unsure of where this problem is rooted. Cheers!
it has to be the position feature of the elements so that it can work
wrong example ( not working )
.className { z-index: 99999 !important; }
correct example ( it work )
.className { position: 'relative'; z-index: 99999 !important; }
.className { position: 'absolute'; z-index: 99999 !important; }
etc..
good luck :)

Categories

Resources