Javascript audio object not playing on mobile devices - javascript

I made this audio player but the codesandbox does not work in mobile devices. It works perfectly fine in desktop.
I get 'Could not get the stack frame of error' error when i try to play song from my phone.
Audio player
The player gets audio files data in a javascript object and they get built into a list. The audios start playing when list item is clicked. The code looks like this
$(function () {
$("#time-progress").progress({ percent: 0 });
let audio = new Audio();
const songs = [
{
artist: "Yaru Makaveli & Yada Sads",
image: "https://i.ytimg.com/vi/rI2vJENDvmY/maxresdefault.jpg",
title: "Cypher Weyn",
song: "cypherweyn2.mp3"
},
{
artist: "Abebe Araya",
image: "https://i.ytimg.com/vi/mp_cR7pVEcw/maxresdefault.jpg",
title: "Natsnet",
song: "natsnet.mp3"
},
{
artist: "Shewit & Semere",
image: "https://i.ytimg.com/vi/ucolLdVzRyg/maxresdefault.jpg",
title: "Betey",
song: "betey.mp3"
},
{
artist: "Q Rap M.O.DB",
image: "https://i.scdn.co/image/ab6761610000e5eb749c5e25b3d167fd3008914b",
title: "Waero",
song: "waero.mp3"
},
{
artist: "Yaru Makaveli & Yada Sads",
image: "https://i.ytimg.com/vi/rI2vJENDvmY/maxresdefault.jpg",
title: "Tealime",
song: "tealime.mp3"
},
{
artist: "Eden Gebreselassie",
image:
"https://is4-ssl.mzstatic.com/image/thumb/Music116/v4/00/4a/14/004a1407-eaed-45d3-048d-12dae76b7d3f/artwork.jpg/375x375bb.jpg",
title: "Goblel",
song: "goblel.mp3"
},
{
artist: "Amanuel Yemane",
image: "https://i.ytimg.com/vi/iukjMznrHcI/maxresdefault.jpg",
title: "Adi Latni",
song: "adilatni.mp3"
},
{
artist: "Tmnit Welday",
image: "https://i.ytimg.com/vi/MqVT5GdW6hQ/maxresdefault.jpg",
title: "Segar",
song: "segar.mp3"
}
];
let list_of_songs = songs
.map(function (song, index) {
return ` <div class="item" data-src="${song.song}"" data-title="${song.title}" data-artist="${song.artist}" data-index=${index} data-image=${song.image}>
<img class="ui avatar image" src="${song.image}"">
<div class="content">
<div id="equalizer">
<div id="bar1"></div>
<div id="bar2"></div>
<div id="bar3"></div>
<div id="bar4"></div>
</div>
<i class="icon button-overlay circle outline"></i>
<span class="header">${song.title}</span>
<div class="description">${song.artist}</div>
</div>
</div>`;
})
.join("");
let play = document.querySelector("#play");
let currentSong = 0;
document.getElementById("song-list").innerHTML = list_of_songs;
audio = new Audio(`./music/${songs[0].song}`);
let icons = document.querySelectorAll(".icon");
$(document).on("click", ".item", function () {
let { src, artist, title, image, index } = this.dataset;
currentSong = Number(index);
let list_items = document.querySelectorAll(".item");
list_items.forEach((e) => {
e.classList.remove("active");
});
this.classList.add("active");
let newaudio = new Audio(`./music/${src}`);
if (audio.currentTime > 0 && !audio.paused && audio.src == newaudio.src) {
play.classList.remove("pause");
play.classList.add("play");
audio.pause();
this.querySelector(".button-overlay").classList.add("play");
this.querySelector(".button-overlay").classList.remove("pause");
} else if (
audio.currentTime > 0 &&
audio.paused &&
audio.src == newaudio.src
) {
play.classList.remove("play");
play.classList.add("pause");
this.querySelector(".button-overlay").classList.add("pause");
this.querySelector(".button-overlay").classList.remove("play");
audio.play();
} else {
this.querySelector(".button-overlay").classList.add("pause");
this.querySelector(".button-overlay").classList.remove("play");
playSong(src, artist, title, image);
}
});
audio.addEventListener("timeupdate", function (e) {
let currentTime = audio.currentTime;
let duration = audio.duration;
let minutes = Math.floor(currentTime / 60);
minutes = minutes >= 10 ? minutes : "0" + minutes;
let seconds = Math.floor(currentTime % 60);
seconds = seconds >= 10 ? seconds : "0" + seconds;
document.querySelector("#timer").innerText = `${minutes}:${seconds}`;
//progress
let status = Math.floor((currentTime / duration) * 100);
$("#time-progress").progress({ percent: status });
});
let artist_img = document.querySelector(".artist-image");
let song_title = document.querySelector("#title");
icons.forEach((icon) => {
icon.addEventListener("click", (e) => {
let type = e.target.dataset.type;
let image, src, artist, title;
var list_items = document.querySelectorAll(".item");
switch (type) {
case "play":
if (audio.currentTime > 0) {
play.classList.remove("play");
play.classList.add("pause");
play.dataset.type = "pause";
audio.play();
} else {
currentSong = 0;
let item = document.querySelector(".item");
item.classList.add("active");
item.querySelector(".icon").classList.add("pause");
document
.querySelector(".item")
.querySelector(".button-overlay")
.classList.remove("play");
const { song, artist, title, image } = songs[0];
playSong(song, title, artist, image);
}
break;
case "pause":
audio.pause();
artist_img.classList.remove("rotate-image");
e.target.classList.remove("pause");
e.target.classList.add("play");
e.target.dataset.type = "play";
break;
case "prev":
currentSong =
currentSong - 1 < 0 ? songs.length - 1 : currentSong - 1;
list_items.forEach((e) => {
e.classList.remove("active");
});
list_items[currentSong].classList.add("active");
var off =
$(".item.active").offset().top - $("#song-list").offset().top;
$("#song-list").scrollTop(off);
image = songs[currentSong].image;
src = songs[currentSong].song;
artist = songs[currentSong].artist;
title = songs[currentSong].title;
playSong(src, title, artist, image);
break;
case "next":
currentSong =
currentSong + 1 > songs.length - 1 ? 0 : currentSong + 1;
list_items.forEach((e, index) => {
e.classList.remove("active");
});
list_items[currentSong].classList.add("active");
var off =
$(".item.active").offset().top - $("#song-list").offset().top;
$("#song-list").scrollTop(off);
image = songs[currentSong].image;
src = songs[currentSong].song;
artist = songs[currentSong].artist;
title = songs[currentSong].title;
playSong(src, title, artist, image);
break;
default:
break;
}
});
});
function playSong(src, artist, title, image) {
document.querySelector(
".artist-img-bg"
).style.backgroundImage = `url(${image})`;
audio.src = `./music/${src}`;
artist_img.src = `${image}`;
artist_img.classList.add("rotate-image");
song_title.innerText = `${artist} - ${title}`;
document.querySelector("#title").classList.add("song-title");
play.classList.remove("play");
play.classList.add("pause");
play.dataset.type = "pause";
audio.play();
}
$(document).on("click", ".button-overlay", function () {
if (audio.currentTime > 0) {
this.classList.remove("play");
this.classList.add("pause");
audio.play();
}
});
});
body {
margin: 0;
padding: 0;
z-index: 101;
}
.container {
padding: 15px;
margin: 0 auto;
width: 100%;
background: linear-gradient(#042a45, #7295ae);
display: flex;
height: 100vh;
align-items: center;
justify-content: center;
z-index: 100;
}
.music-player {
width: 400px;
height: auto;
background: linear-gradient(to top,#042a45, #7295ae);
border-radius: 1em;
display: flex;
align-items: center;
flex-direction: column;
padding: 10px;
}
#top-img-container {
width: 100%;
min-height: 200px;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
.artist-img-bg {
filter: blur(3px);
-webkit-filter: blur(3px);
background-repeat: no-repeat;
background-size: cover;
background-position: center;
position: absolute;
z-index: 1;
width: 100%;
min-height: 200px;
}
.artist-image {
margin-top: 20px ;
max-height: 300px;
width: 150px;height: 150px;max-width: 300px;
border-radius: 50%;
box-shadow: 0 0 10px 10px #154275;
position: absolute;
z-index: 999;
}
.rotate-image {
animation:rotate 100s infinite;
animation-timing-function: linear;
}
#keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(365deg);
}
}
.controls {
width: 100%;
margin-bottom: 2em;
margin-top: 1em;display: flex;
justify-content:space-around;
}
.icon {
color:aliceblue;
font-size: 1.5em !important;
cursor: pointer;
transition: transform .2s ease-in-out;
}
.icon:hover {
transform: scale(1.2);
}
.song-title-container {
position: relative;
overflow: hidden;
width: 90%;
margin: 10px auto 0;
}
.song-title {
margin: 10px 0 15px;
text-align: center;
color: #ffffff;
font-weight: 700;animation: song_title 7s infinite ease-in-out 3s;
animation-timing-function: linear;
position: relative;
}
#keyframes song_title {
0% {
left:0%;
} 49% {
left: -100%;
visibility: hidden;
} 50% {
left: 200%;
visibility: hidden;
}
51% {
left: 100%;
visibility: visible;
}
100% {
visibility: visible;
left: 0%
}
}
#timer, #song-list .header, #song-list .description {
color: white;
}
#playlist-title {
color: white;
width: 100%;
margin-left: 20px;
}
#song-list {
height: 200px;overflow-y: scroll;width: 95%;
background-color: #16415f;
border-radius: 1em;
}
#song-list .item {
cursor: pointer;
padding: 10px;
position: relative;
}
.item.active {
background-color: #38596e;
box-shadow: 0px 2px 10px #07141d;
}
.list-image {
position: relative;
display: inline;
}
.content .icon {
position: absolute;
left: 20px;
top: 20px;
visibility: hidden;
}
.item.active .content .button-overlay {
visibility: visible;
}
#song-list .avatar {
width: 3em;
height: 3em;
}
#song-list .item:hover {
background-color: #3a5a6f;
}
#equalizer {
position: absolute;
right: 5px;top: 50%;transform: translateY(-50%);width: 40px;
max-height: 20px;
visibility: hidden;
display: flex;
justify-content: space-evenly;
}
.item.active .content #equalizer {
visibility: visible;
}
#bar1, #bar2, #bar3, #bar4 {
background-color: #1aa303;
width: 2px;
}
#bar1 {
animation: bar1 1.2s linear infinite;
}
#bar2 {
animation: bar2 0.9s linear infinite;
}
#bar3 {
animation: bar2 1.4s linear infinite;
}
#bar4 {
animation: bar4 1s linear infinite;
}
#keyframes bar1 {
0% {
height: 5px;
}
50% {
height: 10px;
}
100% {
height: 5px;
}
}#keyframes bar2 {
0% {
height: 10px;
}
50% {
height: 5px;
}
100% {
height: 10px;
}
}
#keyframes bar3 {
0% {
height: 5px;
}
50% {
height: 10px;
}
100% {
height: 5px;
}
}
#keyframes bar4 {
0% {
height: 10px;
}
50% {
height:5px;
}
100% {
height: 10px;
}
}
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Music Player</title>
<link rel="stylesheet" href="./main.css" />
<link
href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"
rel="stylesheet"
/>
</head>
<body>
<div class="ui-container container">
<div class="music-player">
<div id="top-img-container">
<div class="artist-img-bg"></div>
<img
src="https://i.pinimg.com/originals/1d/3a/26/1d3a2651f9ad02fb12aae08f618a2847.png"
class="artist-image"
alt="on and on"
/>
</div>
<div class="song-title-container">
<h3 id="title"></h3>
<div id="time-progress" class="ui tiny progress">
<div style="min-width: 1%" class="bar"></div>
<div id="timer" class="label">-00:00-</div>
</div>
</div>
<div class="controls">
<i data-type="prev" id="prev" class="icon backward"></i>
<i data-type="play" id="play" class="icon play circle outline"></i>
<i data-type="next" id="pause" class="icon forward"></i>
</div>
<h3 id="playlist-title">Playlist - Best of 2021</h3>
<div id="song-list" class="ui middle aligned divided list"></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js"></script>
<script type="text/javascript" src="./main.js"></script>
</body>
</html>
It works fine in desktop browsers but it does not work in mobile devices. What am I doin wrong?

Related

Having a tremendous amount of difficulty getting play buttons to work from inside their own container, separate from the HTML

This code I am trying to get working has the play buttons at the bottom inside their own container.
How the code works is, click on a button, and then a video should appear on the screen.
To test jsitor code, press run, not update.
https://jsitor.com/MIYiywN4HC / https://jsfiddle.net/kxhyLdr8/
<div class="playButtonContainer">
<button class="playa cover" type="button"></button>
<button class="playb cover" type="button"></button>
<button class="playc cover" type="button"></button>
<button class="playd cover" type="button"></button>
<button class="playe cover" type="button"></button>
<button class="playf cover" type="button"></button>
<button class="playg cover" type="button"></button>
<button class="playh cover" type="button"></button>
<button class="playi cover" type="button"></button>
</div>
What is weird about the above code is that after clicking on the buttons, no errors are appearing, when there should be.
By errors I mean, in the console log, I was referring to, usually errors will appear in there if something is broken. I am stuck trying to figure out how to get the broken code working.
For comparison, here is a demo code of it working with the play buttons not inside their own container.
The only difference between the demo code and the code I am working on is that in the code I am working on, the play buttons are inside their own container.
To test jsitor code, press run, not update.
https://jsitor.com/qTWoiuHp7z / https://jsfiddle.net/24xf0avp/
The only difference between the working and non working example is just the extra layer of HTML.
Nothing was touched in any other part of the code.
Would anyone be able to check and see if their able that code to work, the code with the play buttons in their own container?
No one has been able to figure out how that would be done, getting the play buttons to work inside their own container.
I was told by my instructor, the code needs to work with the play buttons inside their own container, separate from the HTML.
I have no idea how that would be done, and because there are no errors appearing in the code, that further confuses me.
I was told this:
The problem here is inside the body of the function showCover, which
adds the class active and calls the function show (which removes the
class hide) to and from the parent of the button , instead of the
.container element where it should be.
Could it be that you just need to extend your JS code so that it
reaches the inner html node?
on the button click event it probably captures the parent element
instead of the target element?
Binding of the click of the play button
So you should either wrap them together, put the play button in the
outer, and then go to the closest('.outer') then
querySelector('.video') or whatever you want to do. Or put an
attribute on the .playa button (eg data-player-id="play1") that you
can use to directly find from the click and then use this to call the
.container.play1 element
Here I created a very simple demo with one button.
<div class="playButtonContainer">
<button class="playa cover" type="button"></button>
</div>
Here is the working demo code with only 1 button.
https://jsitor.com/Qer_1Oi9Jv / https://jsfiddle.net/1bmrwuzn/
Here is the broken code with only 1 button.
https://jsitor.com/D38vgjs3g / https://jsfiddle.net/gt57k29h/
What is meant by broken is, is that it is not working or functioning the same way as the demo code I provided.
Smaller, reproducible example code:
const manageCover = (function makeManageCover() {
const config = {};
const body = document.body;
let currentPlayButton = {};
function show(el) {
el.classList.remove("hide");
}
function hide(el) {
el.classList.add("hide");
}
function hideAll(elements) {
elements.forEach(hide);
}
function resetBackground(backgroundSelector) {
const allBackgrounds = document.querySelectorAll(backgroundSelector);
function hideBackground(background) {
background.classList.add("bg1");
}
allBackgrounds.forEach(hideBackground);
}
function resetButtons(buttonSelector) {
const allButtons = document.querySelectorAll(buttonSelector);
function hideButton(button) {
button.classList.add("isOpen");
}
allButtons.forEach(hideButton);
}
function resetPage() {
resetBackground("body");
resetButtons(".outer");
}
function markAsPlayed(played) {
played.classList.add("played");
}
function showCover(playButton) {
hideAll(config.containers);
resetPage();
markAsPlayed(playButton);
const cover = playButton.parentElement;
cover.classList.add("active");
show(cover);
}
function animationEndHandler(evt) {
const animationName = evt.animationName;
if (animationName === "initial-fade") {
body.classList.remove("initial-fade");
showCover(currentPlayButton);
}
}
function coverClickHandler(evt) {
currentPlayButton = evt.currentTarget;
body.classList.add("initial-fade");
}
function addClickToButtons(playButtons) {
playButtons.forEach(function playButtonHandler(playButton) {
playButton.addEventListener("click", coverClickHandler);
});
}
function addCoverHandler(coverSelector, handler) {
const cover = document.querySelector(coverSelector);
cover.addEventListener("click", handler);
}
function init(selectors) {
config.containers = document.querySelectorAll(selectors.container);
const playButtons = document.querySelectorAll(selectors.playButton);
addClickToButtons(playButtons);
body.addEventListener("animationend", animationEndHandler);
}
return {
addCoverHandler,
init
};
}());
const manageUI = (function makeManageUI() {
const body = document.body;
const players = [];
function findPlayers() {
const allCovers = document.querySelectorAll(".cover");
const allWrappers = document.querySelectorAll(".wrap");
allCovers.forEach(function addToPlayers(cover, index) {
players.push({
"cover": cover,
"wrapper": allWrappers[index]
});
});
}
// inline arrow function, direct return
function getWrapper(cover) {
const index = players.findIndex(
(player) => player.cover === cover
);
return players[index].wrapper;
}
function resetBackground(backgroundSelector) {
const allBackgrounds = document.querySelectorAll(backgroundSelector);
function showBackground(background) {
background.classList.remove("bg1");
}
allBackgrounds.forEach(showBackground);
}
function resetCurtains(curtainSelector) {
const allCurtains = document.querySelectorAll(curtainSelector);
function showCurtain(curtain) {
curtain.classList.remove("active");
}
allCurtains.forEach(showCurtain);
}
function showAllButtons(buttonSelector) {
const allButtons = document.querySelectorAll(buttonSelector);
function showButton(button) {
button.classList.remove("hide");
}
allButtons.forEach(showButton);
}
function resetButtons(buttonSelector) {
const allButtons = document.querySelectorAll(buttonSelector);
function showButton(button) {
button.classList.remove("isOpen");
}
allButtons.forEach(showButton);
}
function animationEndHandler(evt) {
const animationName = evt.animationName;
console.log(animationName);
if (animationName === "fadingOut") {
fadeReset();
}
}
function fadeReset() {
body.classList.remove("fadingOut");
resetBackground("body");
resetCurtains(".with-curtain");
showAllButtons(".hide");
resetButtons(".outer");
}
function resetPage() {
body.classList.add("fadingOut");
}
function exitClickHandler() {
resetPage();
}
function addClickToExit(exitButtons) {
exitButtons.forEach(function addExitButtonHandler(exitButtons) {
exitButtons.addEventListener("click", exitClickHandler);
});
}
function addExitHandlers(callback) {
const resetVideo = document.querySelectorAll(".exit");
resetVideo.forEach(function resetVideoHandler(video) {
video.addEventListener("click", callback);
});
}
function init() {
findPlayers();
const exitButtons = document.querySelectorAll(".exit");
addClickToExit(exitButtons);
body.addEventListener("animationend", animationEndHandler);
}
return {
addExitHandlers,
getWrapper,
init
};
}());
const videoPlayer = (function makeVideoPlayer() {
const tag = document.createElement("script");
tag.src = "https://www.youtube.com/player_api";
const firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
function onPlayerReady(event) {
const player = event.target;
player.setVolume(100);
}
function onPlayerStateChange(event) {
const player = event.target;
return player;
}
function addPlayer(video, playerOptions) {
playerOptions.videoId = playerOptions.videoId || video.dataset.id;
playerOptions.events = playerOptions.events || {};
playerOptions.events.onReady = onPlayerReady;
playerOptions.events.onStateChange = onPlayerStateChange;
const player = new YT.Player(video, playerOptions);
return player;
}
return {
addPlayer
};
}());
const managePlayer = (function makeManagePlayer() {
const playerVars = {
autoplay: 0,
controls: 1,
disablekb: 1,
enablejsapi: 1,
fs: 0,
iv_load_policy: 3
};
const defaults = {
height: 360,
host: "https://www.youtube-nocookie.com",
playerVars,
width: 640
};
function show(el) {
el.classList.remove("hide");
}
function combinePlayerOptions(opts1 = {}, opts2 = {}) {
const combined = Object.assign({}, opts1, opts2);
Object.keys(opts1).forEach(function checkObjects(prop) {
if (typeof opts1[prop] === "object") {
combined[prop] = Object.assign({}, opts1[prop], opts2[prop]);
}
});
return combined;
}
function createPlayer(videoWrapper, playerOptions = {}) {
const video = videoWrapper.querySelector(".video");
const options = combinePlayerOptions(defaults, playerOptions);
return videoPlayer.addPlayer(video, options);
}
function playerAdder(wrapper, playerOptions) {
return function addPlayerCallback() {
initPlayer(wrapper, playerOptions);
};
}
function removePlayer(wrapper) {
wrapper.player.destroy();
delete wrapper.player;
console.log("removePlayer");
}
function removePlayerHandler(evt) {
const el = evt.target;
const container = el.closest(".container");
const wrapper = container.querySelector(".wrap");
if (wrapper.player) {
return removePlayer(wrapper);
}
}
function initPlayer(wrapper, playerOptions) {
show(wrapper);
const player = createPlayer(wrapper, playerOptions);
wrapper.player = player;
}
return {
adder: playerAdder,
removePlayerHandler
};
}());
const players = (function coverUIPlayerFacade() {
function addPlayer(coverSelector, playerOptions) {
const cover = document.querySelector(coverSelector);
const wrapper = manageUI.getWrapper(cover);
const callback = managePlayer.adder(wrapper, playerOptions);
manageCover.addCoverHandler(coverSelector, callback);
}
function init() {
manageCover.init({
container: ".container",
playButton: ".cover"
});
manageUI.init({});
manageUI.addExitHandlers(managePlayer.removePlayerHandler);
}
return {
add: addPlayer,
init
};
}());
players.init();
function onYouTubeIframeAPIReady() {
players.add(".playa", {});
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
body {
background: #353198;
}
.initial-fade {
animation: initial-fade 500ms ease forwards;
}
#keyframes initial-fade {
to {
opacity: 0;
}
}
.initial-fade,
.fadingOut {
cursor: default;
}
.initial-fade .cover,
.initial-fade .cover * {
pointer-events: none !important;
}
.container {
display: flex;
justify-content: center;
position: relative;
/*z-index: 2;*/
}
.container.active {
flex: 1 0 0;
}
/*body.*/
.bg1 {
animation: fadeInBody 5s ease 0s forwards;
animation-delay: 0s;
opacity: 0;
}
#keyframes fadeInBody {
100% {
opacity: 1;
}
}
/*body.*/
.bg1 .with-curtain:before {
content: "";
position: fixed;
/*z-index: 1;*/
top: 0;
left: 0;
right: 0;
bottom: 0;
background-size: 165px 165px;
background-image:
}
.playButtonContainer {
display: flex;
flex-wrap: wrap;
min-height: 100%;
margin: auto;
justify-content: center;
align-content: center;
width: 290px;
gap: 10px;
animation: fadeInButtons 3s ease 0s forwards;
}
#keyframes fadeInButtons {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.outer.isOpen {
width: auto;
}
.fadingOut .isOpen {
animation: fadingOut 1s;
animation-delay: 11.3s;
}
#keyframes fadingOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.inner-container {
display: none;
}
/* when container is active hide the cssPlay and show the inner container*/
.container.active .cover {
display: none;
}
.container.active .inner-container {
display: flex;
}
.container.active .inner-container.curtain {
display: block;
}
.cover {
-webkit-appearance: none;
appearance: none;
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 90px;
height: 90px;
border-radius: 50%;
cursor: pointer;
border: 9px solid blue;
background: transparent;
filter: drop-shadow(3px 3px 3px rgba(0, 0, 0, 0.7));
}
.cover::before {
content: "";
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-left: 27px solid blue;
transform: translateX(4px);
}
.cover:hover {
box-shadow: 0 0 0 5px rgba(43, 179, 20, 0.5);
}
.cover:focus {
outline: 0;
box-shadow: 0 0 0 5px rgba(0, 255, 255, 0.5);
}
.played {
border-color: green;
}
.played::before {
border-left-color: green;
}
.curtain {
flex: 1 0 0;
margin: auto;
max-width: 642px;
/*box-shadow: inset 0 -2px 0px 0px #0a0a0a;*/
border: 20px solid black;
border-radius: 3.2px;
border-color: #000 #101010 #000 #101010;
position: relative;
}
.curtain::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
background: radial-gradient(circle, transparent 0% 35%, #0a0a0a 35%);
}
.ratio-keeper {
position: relative;
height: 0;
padding-top: 56.25%;
margin: auto;
overflow: hidden;
border: 1px solid #333;
}
.video-frame {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
iframe {
user-select: none;
}
.panel-left,
.panel-right {
position: absolute;
height: 100%;
width: calc(50% + 1px);
/* rounding error fix */
top: 0%;
transition: all ease 10s;
/*background-image: url("https://picsum.photos/600");
background-size: cover;
background-repeat: no-repeat;
background-position: center;*/
overflow: hidden;
}
.panel-left {
left: 0;
/*background-color: rgb(91, 96, 106);*/
}
.panel-right {
right: 0;
/*background-color: rgb(229, 211, 211);*/
}
.panel-left::before,
.panel-right::before {
content: "";
position: absolute;
height: 100%;
width: 200%;
top: 0;
left: 0;
background-size: auto;
background-repeat: no-repeat;
background-position: 0 0;
}
.panel-right::before {
left: -100%;
}
.container.active .curtain .panel-left {
animation: curtain1-open 8s forwards 520ms;
}
#keyframes curtain1-open {
to {
transform: translateX(calc(-100% - 1px));
}
}
.container.active .curtain .panel-right {
animation: curtain2-open 8s forwards 520ms;
}
#keyframes curtain2-open {
to {
transform: translateX(calc(100% + 1px));
}
}
.fadingOut .container.active .curtain .panel-left {
animation: curtain1-close 8s ease forwards;
transform: translateX(calc(-100% - 1px));
animation-delay: 3.3s;
}
#keyframes curtain1-close {
to {
transform: translateX(0);
}
}
.fadingOut .container.active .curtain .panel-right {
animation: curtain2-close 8s ease forwards;
transform: translateX(calc(100% + 1px));
animation-delay: 3.3s;
}
#keyframes curtain2-close {
to {
transform: translateX(0);
}
}
.exit {
position: absolute;
top: auto;
bottom: -47px;
margin: auto;
right: 0;
left: 0;
width: 47px;
height: 47px;
cursor: pointer;
background: black;
border-radius: 50%;
border: 5px solid red;
animation: fadeInExit 1s forwards;
opacity: 0;
pointer-events: none;
}
.exit::before,
.exit::after {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
margin: auto;
width: 100%;
height: 5px;
background: red;
transform: rotate(45deg);
transition: all 1s ease;
}
.exit::after {
transform: rotate(-45deg);
}
#keyframes fadeInExit {
99% {
pointer-events: none;
}
100% {
pointer-events: initial;
opacity: 1;
}
}
.exit:hover::before,
.exit:hover::after,
.fadingOut .exit::before,
.fadingOut .exit::after {
background: green;
}
.fadingOut .exit {
animation: fadeOutExit 8s forwards;
pointer-events: none;
opacity: 1;
}
#keyframes fadeOutExit {
to {
opacity: 0;
}
}
.hide {
display: none;
}
<div class="outer">
<div class="container play1 with-curtain">
<div class="inner-container curtain curtain1">
<div class="ratio-keeper">
<div class="wrap">
<div class="video video-frame" data-id="CHahce95B1g"></div>
</div>
<div class="sliding-panels">
<div class="panel-left"></div>
<div class="panel-right"></div>
</div>
</div>
<button class="exit" type="button" title="Exit" aria-label="Close"></button>
</div>
</div>
</div>
<div class="playButtonContainer">
<button class="playa cover" type="button" aria-label="Open"></button>
</div>

How to show individual ALT tag and URL in responsive popup image gallery

I am pretty new here and also with Javascript but I have tried for quite some time now and can't get my head around how to have individual data in a popup image gallery.
I have tried adding it in multiple ways, and I do not really need a number (1-..) but an individual URL, also an individual description (using the alt tag). So this is the first code that offered it to me and I hope there is some geeks in this community that can give me an easy and quick "fix".
Many thanks in advance!
Here is some code to make you understand better (hopefully):
HTML:
<div class="popup">
<!-- top bar -->
<div class="top-bar">
<p class="image-name">img1.png</p>
<p class="image-description">Just an image</p>
<span class="close-btn"></span>
!-- image -->
<img src="./img/ads/1.png" class="large-image" alt="Just an image">
<!-- image-index -->
<p class="index">01</p>
</div>
<div class="gallery">
<div class="gallery-image">
<img src="./img/ads/1.png" alt="Just an image" class="image">
</div>...
CSS:
.popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
width: 80%;
max-width: 100%;
height: 90vh;
max-height: 70%;
border-radius: 20px;
background: rgba(0, 0, 0, 0.75);
display: flex;
justify-content: center;
align-items: center;
z-index: 5;
overflow: hidden;
transition: 1s;
opacity: 0;
}
.popup.active {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
.popup.active .close-btn,
.popup.active .image-name,
.popup.active .index,
.popup.active .image-description,
.popup.active .large-image,
.popup.active .arrow-btn {
opacity: 1;
transition: opacity .5s;
transition-delay: 1s;
}
.top-bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 50px;
background: #000;
color: #fff;
text-align: center;
line-height: 50px;
font-weight: 300;
}
.image-name {
opacity: 0;
}
.close-btn {
opacity: 0;
position: absolute;
top: 15px;
right: 20px;
width: 20px;
height: 20px;
border-radius: 50%;
background: #f00;
cursor: pointer;
}
JAVASCRIPT:
const popup = document.querySelector('.popup');
const closeBtn = document.querySelector('.close-btn');
const imageName = document.querySelector('.image-name');
const largeImage = document.querySelector('.large-image');
const imageDis = document.querySelector('.image-description');
const imageIndex = document.querySelector('.index');
const leftArrow = document.querySelector('.left-arrow');
const rightArrow = document.querySelector('.right-arrow');
let index = 0; // will track our current image;
images.forEach((item, i) => {
item.addEventListener('click', () => {
updateImage(i);
popup.classList.toggle('active');
});
});
const updateImage = i => {
let path = `./img/ads/${i + 1}.png`;
largeImage.src = path;
imageName.innerHTML = path;
imageIndex.innerHTML = `0${i + 1}`;
index = i;
};
closeBtn.addEventListener('click', () => {
popup.classList.toggle('active');
});
leftArrow.addEventListener('click', () => {
if (index > 0) {
updateImage(index - 1);
}
});
rightArrow.addEventListener('click', () => {
if (index < images.length - 1) { updateImage(index + 1); } });```

How do I change grid for a memory game using dropdown list, I've manage to make it work but the cards stops flipping when I change the grid?

When I first load page the game works but when I change grid the game cards don't flip. I'm not sure if it the use of onchange inside the select tag or it something else. If I change the value of a variable named value inside displayCardsDiv manual it also works fine maybe if I could find a way to update it by dropddown list or button click it will work.
Html
<div class="game-container" >
<script>displayCardsDiv();</script>
<div ></div>
</div>
</div>
<div class="game-title">
<label class="changeGrid">change Grid</label>
<select id="grid" name="grid" onchange="displayCardsDiv()">
<option value="first" id="first">2x2</option>
<option value="second" id="second">3x2</option>
<option value="third" id="third">4x3</option>
</select>
<button class="reset-btn" onclick="resetGame()">Reset</button>
</div>
<script src="./src/script.js"></script>
</body>
Javascript
"src/img/apple-eye.png",
"src/img/apple-eye.png",
"src/img/blue-nife.png",
"src/img/blue-nife.png",
"src/img/devil.png",
"src/img/devil.png",
"src/img/headless.png",
"src/img/headless.png",
"src/img/heart.png",
"src/img/heart.png",
"src/img/mommy.png",
"src/img/mommy.png",
];
class MemoryGame {
constructor() {
this.cardsArray = playCard;
}
beforeGameStart() {
this.cardChecker = null;
this.matchingCardsArray = [];
this.isLocked = true;
setTimeout(() => {
this.shuffleCards(this.cardsArray);
this.isLocked = false;
}, 500);
}
handleCardFlip(card) {
if (this.flipCard(card)) {
card.classList.add("showingCard");
if (this.cardChecker) {
this.checkForMatch(card);
} else {
this.cardChecker = card;
}
}
}
checkForMatch(card) {
if (this.findTypeOfCard(card) === this.findTypeOfCard(this.cardChecker))
this.matchCards(card, this.cardChecker);
else this.misMatchCards(card, this.cardChecker);
this.cardChecker = null;
}
matchCards(firstCard, secondCard) {
this.matchingCardsArray.push(firstCard);
this.matchingCardsArray.push(secondCard);
firstCard.classList.add("matched");
secondCard.classList.add("matched");
setTimeout(() => {
if (this.matchingCardsArray.length === this.cardsArray.length) {
alert("All cards matched");
resetGame();
}
}, 1000);
}
misMatchCards(firstCard, secondCard) {
this.isLocked = true;
setTimeout(() => {
firstCard.classList.remove("showingCard");
secondCard.classList.remove("showingCard");
this.isLocked = false;
}, 1000);
}
shuffleCards(cardsArray) {
cardsArray.forEach((card) => {
let randomPos = Math.floor(Math.random() * cardsArray.length);
card.style.order = randomPos;
});
}
findTypeOfCard(card) {
return card.getElementsByClassName("value")[0].src;
}
flipCard(card) {
return (
!this.isLocked &&
!this.matchingCardsArray.includes(card) &&
card !== this.cardChecker
);
}
}
function resetGame() {
location.reload();
}
let gameContainer = document.querySelector(".game-container");
function twoByTwoGrid() {
gameContainer.style.gridTemplateColumns = "repeat(2, auto)";
return imagesArray.slice(0, 4);
}
function threeByTwoGrid() {
gameContainer.style.gridTemplateColumns = "repeat(3, auto)";
return imagesArray.slice(0, 6);
}
function displayCardsDiv() {
let deckOfCards = [];
let displayGameCards = "";
let value = document.querySelector("#grid").value;
if (value == "first") {
deckOfCards = twoByTwoGrid();
}
if (value === "second") {
deckOfCards = threeByTwoGrid();
}
if (value !== "first" && value !== "second") {
gameContainer.style.gridTemplateColumns = "repeat(4, auto)";
deckOfCards = [...imagesArray];
}
for (let i in deckOfCards) {
displayGameCards += `<div class="card">
<div class="card-back card-face"><img class="demonsCards" src="src/img/demons.png"></div>
<div class="card-front card-face"><img class="value" src="${deckOfCards[i]}"></div>
</div>`;
}
document.getElementsByClassName("game-container")[0].innerHTML =
displayGameCards;
}
displayCardsDiv();
const playCard = document.querySelectorAll(".card");
const game = new MemoryGame(playCard);
game.beforeGameStart();
for (let i = 0; i < imagesArray.length; i++) {
playCard[i].addEventListener("click", () => game.handleCardFlip(playCard[i]));
}
module.exports = { game };
CSS
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
min-height: 100vh;
}
body {
margin: 0;
background: radial-gradient(#46bddb, #4672db);
}
h1{
background: -webkit-repeating-linear-gradient(#eee, #333);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
img {
height: 175px;
width: 125px;
}
.game-title {
color: aliceblue;
font-family: 'Noto Sans Mono', monospace;
font-weight: normal;
text-align: center;
font-size: 4em;
margin-top: 0px;
}
.game-info-container {
grid-column: 1 / -1;
display: flex;
justify-content: space-between;
}
.game-info {
font-family: 'Pacifico', cursive;
color: aliceblue;
font-size: 4em;
}
.changeGrid{
font-size: large;
}
.game-container {
margin: 50px auto;
display: grid;
grid-template-columns:repeat(4, auto);
grid-gap: 10px;
justify-content: center;
perspective: 500px;
}
.card {
position: relative;
height: 175px;
width: 125px;
cursor: pointer;
}
.card-face {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
border-radius: 12px;
border-width: 1px;
border-style: solid;
overflow: hidden;
transition: transform 500ms ease-in-out;
backface-visibility: hidden;
}
.card.showingCard .card-back {
transform: rotateY(180deg);
}
.card.showingCard .card-front {
transform: rotateY(0);
}
.card-back {
background-color:aliceblue;
border-color: aliceblue;
transform: rotateY(0);
}
.demonsCards {
align-self: flex-start;
transition: transform 100ms ease-in-out;
transform: translateY(-10px);
}
.card:active {
transform: scale(0.79);
transition: transform .1s;
}
.card-front:hover .card-value {
transform: scale(1);
}
.card-front {
background-color: #FFBB89;
border-color: #333;
transform: rotateY(-180deg);
}
#media (max-width: 600px) {
.game-container {
grid-template-columns: repeat(2, auto)
}
.game-info-container {
flex-direction: column;
align-items: center;
}
}

Translation animation when moving child from one parent to another parent

I am trying to make connect4 HTML game and I know I will be better off using canvas elements instead of a grids of divs but is it possible to make transition translate type of css animation when moving HTML elements around like this (using appendChild)
const row1 = document.getElementById("row1")
const row2 = document.getElementById("row2")
const ball = document.getElementById("TheBall")
ball.addEventListener("click", (event, element) => {
let rowNum = parseInt(ball.dataset.row)
if(rowNum==1) {
row2.appendChild(ball)
ball.dataset.row = 2
} else {
row1.appendChild(ball)
ball.dataset.row = 1
}
})
#main {
left:100px;
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
}
#main div {
margin: 50px 0;
width: 50px;
height: 50px;
}
#TheBall {
width: auto;
height: auto;
background-color: red;
border-radius: 100%;
}
<div id="main">
<div id="row1"> </div>
<div id="row2">
<div id="TheBall" data-row=2></div>
</div>
</div>
Click on the red dot to toggle position of ball
You can use animationend to check when the animation end and move the ball element between the divs
const row1 = document.getElementById("row1")
const row2 = document.getElementById("row2")
const ball = document.getElementById("TheBall")
ball.addEventListener('animationend', () => {
ball.classList.remove("animate-ball");
ball.style.animation = "";
let rowNum = parseInt(ball.dataset.row)
if (rowNum == 1) {
row2.appendChild(ball)
ball.dataset.row = 2
} else {
row1.appendChild(ball)
ball.dataset.row = 1
}
});
ball.addEventListener("click", (event, element) => {
let rowNum = parseInt(ball.dataset.row)
if (rowNum == 1) {
ball.style.animation = "MoveDown 1s linear";
} else {
ball.style.animation = "MoveUp 1s linear";
}
ball.classList.add("animate-ball");
})
#main {
left: 100px;
width: 100px;
height: 100px;
display: flex;
flex-direction: column;
}
#main div {
margin: 50px 0;
width: 50px;
height: 50px;
}
#TheBall {
position: relative;
width: auto;
height: auto;
background-color: red;
border-radius: 100%;
}
.animate-ball {
animation-iteration-count: 1;
position: absolute;
bottom: 0;
}
#keyframes MoveUp {
0% {
top: -50px;
}
100% {
top: -100px;
}
}
#keyframes MoveDown {
0% {
top: 0;
}
100% {
top: 50px;
}
}
<div id="main">
<div id="row1"> </div>
<div id="row2">
<div id="TheBall" data-row=2></div>
</div>
</div>

Java script game

I'm looking for some help with my java script game as i have just started getting into it now,
my problem is that my animation loops infinitely on autopay and reloads the page every time it meets the condition to restart,
what I want to achieve is have a start button that would start the game and the animation and score on click and if the condition for restart is met I have to press the start button again to play again
I would appreciate all the help I could get
const skate = document.getElementById("skate");
const rock = document.getElementById("rock");
const score = document.getElementById("score");
function jump() {
skate.classList.add("jump-animation");
setTimeout(() =>
skate.classList.remove("jump-animation"), 500);
}
document.addEventListener('keypress', (event) => {
if (!skate.classList.contains('jump-animation')) {
jump();
}
})
setInterval(() => {
const skateTop = parseInt(window.getComputedStyle(skate)
.getPropertyValue('top'));
const rockLeft = parseInt(window.getComputedStyle(rock)
.getPropertyValue('left'));
score.innerText++;
if (rockLeft < 0) {
rock.style.display = 'none';
} else {
rock.style.display = ''
}
if (rockLeft < 50 && rockLeft > 0 && skateTop > 150) {
location.reload();
}
}, 50);
#score { text-align: center; }
#game {
width: 600px;
height: 300px;
border: 2px solid black;
margin: auto;
background-image: url("./background.gif");
background-size: cover;
}
#skate {
height: 75px;
width: 75px;
top: 220px;
position: relative;
background-image: url("./skateboard.png");
background-size: cover;
}
#rock {
width: 50px;
height: 50px;
position: relative;
top: 175px;
left: 550px;
background-image: url("./rock.png");
background-size: cover;
animation: rock 1.33s infinite;
}
#keyframes rock {
0%{left: 550px;}
100%{left: -50px;}
}
.jump-animation {
animation: jump 0.5s;
}
#keyframes jump {
0%{top: 225px;}
50%{top: 75px;}
75%{top: 75px;}
100%{top: 225px;}
}
<div id="game">
<div id="skate"></div>
<div id="rock"></div>
</div>
<h1 id="score">0</h1>
The trick here is to have a variable at the top scope of this module to track the ID of the current animation so that the animation/interval can be turned off if the user presses the start button before the current game ends. It should also turn off once the end-game condition is met, although I'm not really sure why window.getComputedStyle() is being used to compute the variables used for that condition, but I guess it's still a work in progress.
EDIT: Somehow I didn't notice that the player and obstacle were already in the game so I added colors to them for easier debugging, at least on my end. This time I made it so that the rock will lose then immediately regain a class for its animation, but the re-addition needs to be asynchronous even if the delay is 0ms.
index.html
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="game">
<div id="skate"></div>
<div id="rock"></div>
</div>
<h1 id="score">0</h1>
</div>
<div>
<button id="new-game">New Game </button>
</div>
</body>
<script>
const skate = document.getElementById("skate");
const rock = document.getElementById("rock");
const score = document.getElementById("score");
const newGameButton = document.getElementById("new-game");
function jump() {
skate.classList.add("jump-animation");
setTimeout(() => skate.classList.remove("jump-animation"), 500);
}
document.addEventListener('keypress', (event) => {
if (!skate.classList.contains('jump-animation')) {
jump();
}
});
let animationId;
newGameButton.addEventListener('click', () => {
resetScore();
resetRockAnimation();
playScoreAnimation();
function resetScore() {
clearInterval(animationId);
score.innerText = 0;
}
function resetRockAnimation() {
rock.classList = [];
setTimeout(() => rock.classList.add('rock-animation'), 0);
}
function playScoreAnimation() {
animationId = setInterval(() => {
const skateTop = parseInt(window.getComputedStyle(skate).getPropertyValue('top'));
const rockLeft = parseInt(window.getComputedStyle(rock).getPropertyValue('left'));
score.innerText++;
if (rockLeft < 0) {
rock.style.display = 'none';
}
else {
rock.style.display = '';
}
if (rockLeft < 50 && rockLeft > 0 && skateTop > 150) {
clearInterval(animationId);
}
}, 50);
}
});
</script>
style.css
#score {
text-align: center;
}
#game {
width: 600px;
height: 300px;
border: 2px solid black;
margin: auto;
background-image: url("./background.gif");
background-size: cover;
}
#skate {
height: 75px;
width: 75px;
top: 225px;
position: relative;
background-image: url("./skateboard.png");
background-size: cover;
background-color: slategrey;
}
#rock {
width: 50px;
height: 50px;
position: relative;
top: 175px;
left: 550px;
background-image: url("./rock.png");
background-size: cover;
background-color: lightcoral ;
}
.rock-animation {
animation: rock 1.33s infinite;
}
#keyframes rock {
0% {
left: 550px;
}
100% {
left: -50px;
}
}
.jump-animation {
animation: jump 0.5s;
}
#keyframes jump {
0% {
top: 225px;
}
50% {
top: 75px;
}
75% {
top: 75px;
}
100% {
top: 225px;
}
}

Categories

Resources