text is not shown in mobile chrome - nextjs - javascript

In my nextjs project everything works fine in desktop but in mobile (chorme) text is displayed sometimes and sometimes text is invisible. Here are the images Desktop Mobile-text-shown Mobile-text-not-shown
/* index.js */
import Head from "next/head";
import React from "react";
import MainContainer from "../components/Maincontainer";
import BookHoliday from "../components/BookHoliday";
export default function Home() {
return (
<>
<Head>
<title>Tourister</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<MainContainer />
<BookHoliday/>
<div className="test">
<h2>Just For Test</h2>
<p>
lorem400
</p>
</div>
</>
);
}
/* MainContainer.js */
import React, { useEffect, useRef } from "react";
import { FaAngleLeft, FaAngleRight } from "react-icons/fa";
import Navbar from "./Navbar";
import Header from "./Header";
import SideBar from "./SideBar";
import Image from "next/legacy/image";
function showAnimation() {
var count = 2;
let myInterval = null;
function runInterval() {
myInterval = setInterval(() => {
if (count === 3) {
document.getElementById("bg-3").style.opacity = "0";
document.getElementById("circle3").classList.remove("circle-ani");
document
.getElementById("small-circle3")
.classList.remove("small-circle-ani");
document.getElementById("bg-1").style.opacity = "1";
document.getElementById("circle1").classList.add("circle-ani");
document
.getElementById("small-circle1")
.classList.add("small-circle-ani");
count = 1;
} else if (count === 2) {
document.getElementById("bg-2").style.opacity = "0";
document.getElementById("circle2").classList.remove("circle-ani");
document
.getElementById("small-circle2")
.classList.remove("small-circle-ani");
document.getElementById("bg-3").style.opacity = "1";
document.getElementById("circle3").classList.add("circle-ani");
document
.getElementById("small-circle3")
.classList.add("small-circle-ani");
count = 3;
} else if (count === 1) {
document.getElementById("bg-1").style.opacity = "0";
document.getElementById("circle1").classList.remove("circle-ani");
document
.getElementById("small-circle1")
.classList.remove("small-circle-ani");
document.getElementById("bg-2").style.opacity = "1";
document.getElementById("circle2").classList.add("circle-ani");
document
.getElementById("small-circle2")
.classList.add("small-circle-ani");
count = 2;
}
}, 3000);
}
runInterval();
document.getElementById("circle1").addEventListener("click", () => {
changeBg("bg-1");
});
document.getElementById("circle2").addEventListener("click", () => {
changeBg("bg-2");
});
document.getElementById("circle3").addEventListener("click", () => {
changeBg("bg-3");
});
document.getElementById("left").addEventListener("click", () => {
changeBg("left");
});
document.getElementById("right").addEventListener("click", () => {
changeBg("right");
});
function changeBg(data) {
if (data === "bg-1") {
clearInterval(myInterval);
document.getElementById(`bg-${count}`).style.opacity = "0";
document.getElementById(`circle${count}`).classList.remove("circle-ani");
document
.getElementById(`small-circle${count}`)
.classList.remove("small-circle-ani");
document.getElementById("bg-1").style.opacity = "1";
document.getElementById("circle1").classList.add("circle-ani");
document
.getElementById("small-circle1")
.classList.add("small-circle-ani");
count = 1;
setTimeout(() => {
runInterval();
});
}
if (data === "bg-2") {
clearInterval(myInterval);
document.getElementById(`bg-${count}`).style.opacity = "0";
document.getElementById(`circle${count}`).classList.remove("circle-ani");
document
.getElementById(`small-circle${count}`)
.classList.remove("small-circle-ani");
document.getElementById("bg-2").style.opacity = "1";
document.getElementById("circle2").classList.add("circle-ani");
document
.getElementById("small-circle2")
.classList.add("small-circle-ani");
count = 2;
setTimeout(() => {
runInterval();
});
}
if (data === "bg-3") {
clearInterval(myInterval);
document.getElementById(`bg-${count}`).style.opacity = "0";
document.getElementById(`circle${count}`).classList.remove("circle-ani");
document
.getElementById(`small-circle${count}`)
.classList.remove("small-circle-ani");
document.getElementById("bg-3").style.opacity = "1";
document.getElementById("circle3").classList.add("circle-ani");
document
.getElementById("small-circle3")
.classList.add("small-circle-ani");
count = 3;
setTimeout(() => {
runInterval();
});
}
if (data === "left") {
clearInterval(myInterval);
if (count === 3) {
document.getElementById("bg-3").style.opacity = "0";
document.getElementById("circle3").classList.remove("circle-ani");
document
.getElementById("small-circle3")
.classList.remove("small-circle-ani");
document.getElementById("bg-2").style.opacity = "1";
document.getElementById("circle2").classList.add("circle-ani");
document
.getElementById("small-circle2")
.classList.add("small-circle-ani");
count = 2;
setTimeout(() => {
runInterval();
});
} else if (count === 2) {
document.getElementById("bg-2").style.opacity = "0";
document.getElementById("circle2").classList.remove("circle-ani");
document
.getElementById("small-circle2")
.classList.remove("small-circle-ani");
document.getElementById("bg-1").style.opacity = "1";
document.getElementById("circle1").classList.add("circle-ani");
document
.getElementById("small-circle1")
.classList.add("small-circle-ani");
count = 1;
setTimeout(() => {
runInterval();
});
} else if (count === 1) {
document.getElementById("bg-1").style.opacity = "0";
document.getElementById("circle1").classList.remove("circle-ani");
document
.getElementById("small-circle1")
.classList.remove("small-circle-ani");
document.getElementById("bg-3").style.opacity = "1";
document.getElementById("circle3").classList.add("circle-ani");
document
.getElementById("small-circle3")
.classList.add("small-circle-ani");
count = 3;
setTimeout(() => {
runInterval();
});
}
}
if (data === "right") {
clearInterval(myInterval);
if (count === 3) {
document.getElementById("bg-3").style.opacity = "0";
document.getElementById("circle3").classList.remove("circle-ani");
document
.getElementById("small-circle3")
.classList.remove("small-circle-ani");
document.getElementById("bg-1").style.opacity = "1";
document.getElementById("circle1").classList.add("circle-ani");
document
.getElementById("small-circle1")
.classList.add("small-circle-ani");
count = 1;
setTimeout(() => {
runInterval();
});
} else if (count === 2) {
document.getElementById("bg-2").style.opacity = "0";
document.getElementById("circle2").classList.remove("circle-ani");
document
.getElementById("small-circle2")
.classList.remove("small-circle-ani");
document.getElementById("bg-3").style.opacity = "1";
document.getElementById("circle3").classList.add("circle-ani");
document
.getElementById("small-circle3")
.classList.add("small-circle-ani");
count = 3;
setTimeout(() => {
runInterval();
});
} else if (count === 1) {
document.getElementById("bg-1").style.opacity = "0";
document.getElementById("circle1").classList.remove("circle-ani");
document
.getElementById("small-circle1")
.classList.remove("small-circle-ani");
document.getElementById("bg-2").style.opacity = "1";
document.getElementById("circle2").classList.add("circle-ani");
document
.getElementById("small-circle2")
.classList.add("small-circle-ani");
count = 2;
setTimeout(() => {
runInterval();
});
}
}
}
}
const Maincontainer = () => {
const dataFetchedRef = useRef(false);
useEffect(() => {
if (dataFetchedRef.current) return;
dataFetchedRef.current = true;
showAnimation();
}, []);
return (
<div id="nav-head">
<Image
layout="fill"
objectFit="cover"
src="/london.jpg"
className="bg"
id="bg-1"
/>
<Image
layout="fill"
objectFit="cover"
src="/rome.jpg"
className="bg"
id="bg-2"
/>
<Image
layout="fill"
objectFit="cover"
src="/paris.jpg"
className="bg"
id="bg-3"
/>
<div className="nav-head-container">
<SideBar />
<Navbar />
<Header />
<div className="circle-list">
<div className="circle" id="circle1">
<div className="small-circle" id="small-circle1"></div>
</div>
<div className="circle circle-ani" id="circle2">
<div
className="small-circle small-circle-ani"
id="small-circle2"
></div>
</div>
<div className="circle" id="circle3">
<div className="small-circle" id="small-circle3"></div>
</div>
</div>
<FaAngleLeft
className="left-arrow"
id="left"
color="#fff"
fontSize={20}
/>
<FaAngleRight
className="right-arrow"
id="right"
color="#fff"
fontSize={20}
/>
</div>
</div>
);
};
export default Maincontainer;
/* BookHoliday.js */
import React, { useState } from "react";
import Datetime from "react-datetime";
import "react-datetime/css/react-datetime.css";
import { AiFillCalendar, AiOutlineSearch } from "react-icons/ai";
import moment from "moment";
const BookHoliday = () => {
var yesterday = moment().subtract(1, "day");
const [arrivalDate, setArrivalDate] = useState(null);
const [departureDate, setDepartureDate] = useState(null);
var Arrivalvalid = function (current) {
return current.isAfter(yesterday);
};
var Departurevalid = function (current) {
if (arrivalDate !== null) {
return current >= arrivalDate;
} else {
return current.isAfter(yesterday);
}
};
return (
<div className="book-holiday">
<h2>Book Holiday Now!</h2>
<form className="form-container">
<div className="input-container">
<input
type="search"
name="search"
id="search"
placeholder="Search Your Destination"
/>
<div className="icon" id="search-icon">
{" "}
<AiOutlineSearch color="#bd7457" fontSize={20} />
</div>
</div>
<div className="input-container">
<Datetime
timeFormat={false}
dateFormat="DD MMM YYYY"
isValidDate={Arrivalvalid}
value={arrivalDate}
onChange={(e) => {
setArrivalDate(e);
if (departureDate !== null && e > departureDate) {
setDepartureDate(e);
}
}}
inputProps={{ placeholder: "Arrival", readOnly: true }}
/>{" "}
<div className="icon pointer" id="a-icon">
{" "}
<AiFillCalendar color="#bd7457" fontSize={20} />
</div>
</div>
<div className="input-container">
<Datetime
timeFormat={false}
dateFormat="DD MMM YYYY"
isValidDate={Departurevalid}
value={departureDate}
onChange={(e) => {
setDepartureDate(e);
}}
inputProps={{ placeholder: "Departure", readOnly: true }}
/>{" "}
<div className="icon pointer" id="d-icon">
{" "}
<AiFillCalendar color="#bd7457" fontSize={20} />
</div>
</div>
<button id="search-btn">Search</button>
</form>
</div>
);
};
export default BookHoliday;
/* globals.css */
#import url("https://fonts.googleapis.com/css2?family=Ubuntu&display=swap");
#import url("https://fonts.googleapis.com/css2?family=Montserrat&display=swap");
#import url("https://fonts.googleapis.com/css2?family=Lobster&family=Ubuntu&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
font-family: "Montserrat";
}
#nav-head {
position: relative;
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.nav-head-container {
position: absolute;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
width: 100%;
height: 100%;
}
#bg-1,
#bg-3 {
opacity: 0;
}
.bg {
transition: 0.6s;
}
.left-arrow {
position: absolute;
left: 10px;
top: 50%;
border: 1px solid white;
border-radius: 4px;
cursor: pointer;
}
.right-arrow {
position: absolute;
right: 10px;
top: 50%;
border: 1px solid white;
border-radius: 4px;
cursor: pointer;
}
.circle-list {
position: absolute;
bottom: 10px;
left: 0;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.circle {
width: 20px;
height: 20px;
border-radius: 20px;
background-color: gray;
opacity: 0.5;
margin: 0px 10px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.circle-ani {
border: 2px solid white;
background-color: transparent;
opacity: 1;
}
.small-circle {
width: 6px;
height: 6px;
border-radius: 6px;
}
.small-circle-ani {
background-color: white;
}
.navbar {
height: 75px;
display: flex;
justify-content: center;
align-items: center;
}
.logo {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.navbar .navlink {
display: flex;
flex: 4;
justify-content: space-evenly;
align-items: center;
}
.link {
text-decoration: none;
color: white;
transition: 0.6s;
}
.navlink li {
list-style: none;
}
.cta {
display: flex;
justify-content: center;
align-items: center;
flex: 1;
}
.cta-button {
border: none;
background-color: transparent;
color: white;
cursor: pointer;
padding: 10px;
border-bottom: 1px solid white;
transition: 0.6s;
}
#ham {
position: relative;
display: none;
height: 24px;
width: 24px;
margin-left: 10px;
cursor: pointer;
}
#header {
width: 100%;
height: calc(100vh - 75px);
display: flex;
color: white;
flex-direction: column;
justify-content: center;
}
#header h2 {
font-size: 4rem;
padding: 0px 10% 10px;
}
#header p {
padding: 0px 10% 20px;
line-height: 25px;
}
#header-btn {
padding: 10px 20px;
background-color: #bd7457;
color: white;
border: 1px solid #bd7457;
width: 180px;
margin-left: 10%;
border-radius: 6px;
transition: 0.6s;
}
#sidebar {
position: fixed;
bottom: 0;
left: 0;
width: 200px;
height: 100%;
background-color: black;
color: white;
z-index: 5;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
transform: translateX(-200px);
transition: 0.6s;
}
#cross {
position: absolute;
right: 10px;
top: 10px;
width: 14px;
height: 14px;
cursor: pointer;
}
.sidelink {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
width: 100%;
height: 226px;
}
.sidelink .link {
font-size: 1.4rem;
}
.side-cta {
height: 56px;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
#side-button {
padding: 10px 20px;
border: none;
background-color: transparent;
border: 1px solid white;
border-radius: 6px;
cursor: pointer;
color: white;
transition: 0.6s;
}
.book-holiday {
background-color: rgb(245, 245, 245);
padding: 30px 0px;
max-height: 9999px;
}
.book-holiday h2 {
font-size: 2rem;
text-align: center;
margin-bottom: 20px;
}
.form-control,
#search {
width: 250px;
padding: 10px 20px;
outline: none;
}
.form-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
#search-btn {
background-color: #bd7457;
border: 1px solid #bd7457;
color: white;
padding: 10px 20px;
transition: 0.6s;
margin: 10px 0px 20px;
}
.input-container {
display: flex;
position: relative;
margin: 10px 0px;
}
.icon {
position: absolute;
right: 0px;
top: 0px;
padding: 7px 10px;
}
.test {
background-color: rgb(255, 235, 205);
padding: 30px 0px;
max-height: 9999px;
}
.test h2 {
margin-bottom: 20px;
text-align: center;
font-size: 2rem;
}
/* This is for react date-time library */
.rdtDay {
color: #bd7457 !important;
}
.rdtDisabled {
color: #999999 !important;
}
.rdtOld {
color: #999999 !important;
}
.rdtNew {
color: black !important;
}
.rdtActive,
.rdtActive:hover {
background-color: #bd7457 !important;
color: white !important;
}
.rdtPicker td.rdtToday:before {
border-bottom-color: #bd7457 !important;
}
#media (hover: hover) {
.cta-button:hover {
border-color: #bd7457;
color: #bd7457;
}
.navlink li .link:hover {
color: #bd7457;
}
#side-button:hover {
color: #bd7457;
border-color: #bd7457;
}
#header-btn:hover,
#search-btn:hover {
border: 1px solid #bd7457;
background-color: transparent;
color: #bd7457;
}
}
#media (hover: none) {
.cta-button:active {
border-bottom: none;
color: #bd7457;
}
.navlink li .link:active {
color: #bd7457;
}
#side-button:active {
color: #bd7457;
border-color: #bd7457;
}
#header-btn:active,
#search-btn:active {
border: 1px solid #bd7457;
color: #bd7457;
background-color: transparent;
}
}
#media (max-width: 768px) {
.logo {
justify-content: flex-end;
margin-right: 30px;
}
#ham {
display: flex;
}
.navbar .navlink,
.cta {
display: none;
}
.navbar {
height: 40px;
align-items: flex-end;
}
#header {
height: calc(100vh -40px);
}
#header h2 {
font-size: 3rem;
}
}
#media (max-width: 540px) {
#header h2 {
font-size: 2.6rem;
}
}
#media (max-height: 414px) {
.sidelink .link {
font-size: 1.2rem;
}
#header h2 {
font-size: 2.4rem;
padding-bottom: 5px;
}
#header p {
padding-bottom: 10px;
}
}
I have tried almost everything, earlier I thought it was because of changing the background images using setInterval or due to react-date-time library, so I made a static background image, removed that date-time library and even removed the background image, but it didn't solve the problem. I am able to click the text when it is invisible, but when i refresh the page it appears.

Related

I return nothing ondrop how to fix it?

When iam dropping my div with an image inside i get nothing and after i am trying to get it ID i get null of course. But how can i get info from a div with image and append row with it?
Code here or check codepen:
https://codepen.io/13reathcode/pen/NWBmZpb
'use strict';
let queuedImagesArray = [],
queuedForm = document.querySelector('#queued-form'),
queuedDiv = document.querySelector('.queued-div'),
inputDiv = document.querySelector('.input-div'),
input = document.querySelector('.input-div input');
const colors = ['#FF7F7F', '#FFBF7F', '#FFDF7F', '#BFFF7F', '#7FFF7F', '#7FBFFF', '#7F7FFF'],
rows = document.querySelectorAll('.content__row'),
cards = document.querySelectorAll('.content__card'),
addCard = document.getElementById('addCard');
// Queued Images
const onDragStart = (event) => {
console.log('Dragging');
event.dataTransfer.setData('id', event.target.id);
setTimeout(() => {
event.target.style.visibility = 'hidden';
}, 100);
};
const onDragEnd = (event) => {
console.log('Ended dragging');
event.target.style.visibility = 'visible';
};
const displayQueuedImages = () => {
let images = '';
queuedImagesArray.forEach((image, index) => {
images += `
<div class="image" draggable="true" id="${(Date.now() + '').slice(-10) + index}">
<img width='100' height='100' style="pointerEvents:none;" id="${index}" ondragstart="onDragStart" ondragend="onDragEnd"
src="${URL.createObjectURL(image)}" alt="image" />
<span style="color:black;font-size:2rem" onclick="deleteQueuedImage(${index})">×</span>
</div>
`;
});
queuedDiv.innerHTML = images;
};
const deleteQueuedImage = (index) => {
queuedImagesArray.splice(index, 1);
displayQueuedImages();
};
input.addEventListener('change', () => {
const files = input.files;
for (let i = 0; i < files.length; i++) {
queuedImagesArray.push(files[i]);
}
queuedForm.reset();
displayQueuedImages();
});
inputDiv.addEventListener('drop', (e) => {
e.preventDefault();
const files = e.dataTransfer.files;
for (let i = 0; i < files.length; i++) {
if (!files[i].type.match('image')) return;
if (queuedImagesArray.every((image) => image.name !== files[i].name))
queuedImagesArray.push(files[i]);
}
displayQueuedImages();
});
const onDrag = (event) => {
event.preventDefault();
};
// Problem here
const onDrop = (event) => {
event.preventDefault();
const draggedCardId = event.dataTransfer.getData('id'); // nothing
const draggedCard = document.getElementById(draggedCardId); // null
event.target.appendChild(draggedCard);
};
rows.forEach((row, index) => {
const label = row.querySelector('.content__label');
label.style.backgroundColor = colors[index];
row.ondragover = onDrag;
row.ondrop = onDrop;
});
<main>
<section class="section" id="section--1">
<div class="section__title">
<h2 class="section__description">Tier list app</h2>
<h3 class="section__text">Start dragging to move cards</h3>
</div>
<div class="content" id="content">
<div class="content__row">
<div class="content__label">S (The best)</div>
</div>
<div class="content__row">
<div class="content__label">A (Great)</div>
</div>
<div class="content__row">
<div class="content__label">B (Good)</div>
</div>
<div class="content__row">
<div class="content__label">C (Mediocre)</div>
</div>
<div class="content__row">
<div class="content__label">D (Bad)</div>
</div>
<div class="content__row">
<div class="content__label">E (Horrible)</div>
</div>
<!-- <div class="content__row">
<div class="content__label">F (Worst^_^)</div>
</div> -->
</div>
</section>
<form id="queued-form">
<div class="queued-div"></div>
</form>
<div class="input-div">
<p>Drag & drop images here or <span class="browse">Browse</span></p>
<input
type="file"
class="file"
multiple="multiple"
accept="image/png, image/jpeg, image/jpg"
/>
</div>
</main>
* {
margin: 0;
padding: 0;
box-sizing: inherit;
}
html {
font-size: 62.5%; // 10px = 1rem
box-sizing: border-box;
}
body {
font-family: 'Open Sans', sans-serif;
font-weight: 300;
color: #555;
line-height: 1.5;
}
.input-div {
width: 70%;
height: 200px;
border-radius: 5px;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
margin: 5rem auto;
border: 2px dotted black;
background-color: white;
position: relative;
.browse {
color: black;
font-weight: bold;
}
}
.file {
width: 100%;
height: 100%;
position: absolute;
opacity: 0;
cursor: pointer;
}
.queued-div {
width: 70%;
min-height: 200px;
display: flex;
margin: 5rem auto;
justify-content: flex-start;
flex-wrap: wrap;
gap: 0.5rem;
position: relative;
border-radius: 5px;
border: 2px dotted black;
background-color: white;
.image {
height: 10rem;
border-radius: 5px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.15);
overflow: hidden;
position: relative;
&:nth-child(4n) {
margin-right: 0;
}
img {
height: 100%;
width: 100%;
}
span {
position: absolute;
top: -4px;
right: 4px;
cursor: pointer;
font-size: 22px;
color: white;
&:hover {
opacity: 0.8;
}
}
}
}
#mixin center {
display: flex;
justify-content: center;
align-items: center;
}
// SECTIONS
.section {
padding: 1.8rem 3rem;
&__title {
max-width: 80rem;
margin: 0 auto 2rem auto;
text-align: center;
text-transform: uppercase;
}
&__description {
color: lightgreen;
font-size: 1.8rem;
}
&__text {
font-size: 2.5rem;
}
&__button {
font-size: 2rem;
text-transform: uppercase;
text-decoration: none;
padding: 1rem 2rem;
display: inline-block;
border-radius: 3rem;
position: relative;
background-color: lightgreen;
color: #fff;
border: none;
cursor: pointer;
}
}
// CONTENT
.content {
width: 70vw;
min-height: 10vh;
padding: 0rem;
box-sizing: content-box;
border: 3px solid #000;
display: flex;
flex-wrap: wrap;
flex-direction: column;
margin: 0 auto;
&__row {
width: 100%;
box-sizing: content-box;
flex-wrap: wrap;
height: 8.5rem;
background-color: #1a1a17;
display: flex;
&:not(:last-child) {
border-bottom: 2px solid #000;
}
}
&__label {
font-size: 2rem;
font-weight: 400;
height: 100%;
width: 15rem;
background-color: #555;
color: #333;
border-right: 3px solid #000;
#include center;
}
&__card {
#include center;
&:focus,
&:active {
cursor: pointer;
}
}
}
When i was adding images one by one everything was fine but when i changed button to input zone i can't get anything from new images.
There is a small issue in how you create your img elements: the ondragstart and ondragend event handlers aren't being assigned properly.
Currently, your img elements look something like:
<img id="0" ondragstart="onDragStart" ondragend="onDragEnd" src=... />
However, setting ondragstart to equal onDragStart doesn't actually invoke the function when the event is fired. Same with ondragend and onDragEnd. To do so, you have to actually invoke the functions with onDragStart(event) and onDragEnd(event).
This is because every HTML event handler attribute is implicitly wrapped in:
(event) => { /** the attribute value */ }
In other words, currently, your handlers are equivalent to:
(event) => { onDragStart }
// and
(event) => { onDragEnd }
instead of
(event) => { onDragStart(event) }
// and
(event) => { onDragEnd(event) }
which is given by
<img id="0" ondragstart="onDragStart(event)" ondragend="onDragEnd(event)" src=... />
So the fix is simply to fix the img generation code to:
images += `
<div class="image" draggable="true" id="${(Date.now() + '').slice(-10) + index}">
<img width='100' height='100' id="${index}"
ondragstart="onDragStart(event)" ondragend="onDragEnd(event)"
src="${URL.createObjectURL(image)}" alt="image" />
<span style="color: black; font-size: 2rem"
onclick="deleteQueuedImage(${index})">×</span>
</div>`;

Why does the update of this computed property fail in my Vue 3 app?

I am working on an audio player with Vue 3 and the Napster API.
Project details
I have made the vinyl spin with the help of a CSS keyframes-based animation and the isSpinning computed property.
I want the vinyl to stop spinning once the end of the current track is reached, which is why isSpinning has this "formula":
isSpinning() {
return this.isPlaying && !this.player.ended;
}
const musicApp = {
data() {
return {
player: new Audio(),
trackCount: 0,
tracks: [],
muted: false,
isPlaying: false
};
},
methods: {
async getTracks() {
try {
const response = await axios
.get(
"https://api.napster.com/v2.1/tracks/top?apikey=ZTk2YjY4MjMtMDAzYy00MTg4LWE2MjYtZDIzNjJmMmM0YTdm"
)
.catch((error) => {
console.log(error);
});
this.tracks = response;
this.tracks = response.data.tracks;
} catch (error) {
console.log(error);
}
},
nextTrack() {
if (this.trackCount < this.tracks.length - 1) {
this.trackCount++;
this.setPlayerSource();
this.playPause();
}
},
prevTrack() {
if (this.trackCount >= 1) {
this.trackCount--;
this.setPlayerSource();
this.playPause();
}
},
setPlayerSource() {
this.player.src = this.tracks[this.trackCount].previewURL;
},
playPause() {
if (this.player.paused) {
this.isPlaying = true;
this.player.play();
} else {
this.isPlaying = false;
this.player.pause();
}
},
toggleMute() {
this.player.muted = !this.player.muted;
this.muted = this.player.muted;
}
},
async created() {
await this.getTracks();
this.setPlayerSource();
},
computed: {
isSpinning() {
return this.isPlaying && !this.player.ended;
}
}
};
Vue.createApp(musicApp).mount("#audioPlayer");
html,
body {
margin: 0;
padding: 0;
font-size: 16px;
}
body * {
box-sizing: border-box;
font-family: "Montserrat", sans-serif;
}
#-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
#keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.player-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #2998ff;
background-image: linear-gradient(62deg, #2998ff 0%, #5966eb 100%);
}
#audioPlayer {
width: 300px;
height: 300px;
border-radius: 8px;
position: relative;
overflow: hidden;
background-color: #00ca81;
background-image: linear-gradient(160deg, #00ca81 0%, #ffffff 100%);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
display: flex;
flex-direction: column;
align-items: center;
}
.volume {
color: #ff0057;
opacity: 0.9;
display: inline-block;
width: 20px;
font-size: 20px;
position: absolute;
top: 5px;
right: 6px;
cursor: pointer;
}
.album {
width: 100%;
flex: 1;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.album-items {
padding: 0 10px;
text-align: center;
}
.cover {
width: 150px;
height: 150px;
margin: auto;
box-shadow: 0px 5px 12px 0px rgba(0, 0, 0, 0.17);
border-radius: 50%;
background: url("https://w7.pngwing.com/pngs/710/955/png-transparent-vinyl-record-artwork-phonograph-record-compact-disc-lp-record-disc-jockey-symbol-miscellaneous-classical-music-sound.png") center top transparent;
background-size: cover;
}
.cover.spinning {
webkit-animation: spin 6s linear infinite;
/* Safari */
animation: spin 6s linear infinite;
}
.info {
width: 100%;
padding-top: 5px;
color: #000;
opacity: 0.85;
}
.info h1 {
font-size: 11px;
margin: 5px 0 0 0;
}
.info h2 {
font-size: 10px;
margin: 3px 0 0 0;
}
.to-bottom {
width: 100%;
margin-top: auto;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.track {
background-color: #ff0057;
opacity: 0.9;
height: 3px;
width: 100%;
}
.controls {
width: 150px;
display: flex;
height: 60px;
justify-content: space-between;
align-items: center;
}
.controls .navigate {
display: flex;
box-shadow: 1px 2px 7px 2px rgba(0, 0, 0, 0.09);
width: 33px;
height: 33px;
line-height: 1;
color: #ff0057;
cursor: pointer;
background: #fff;
opacity: 0.9;
border-radius: 50%;
text-align: center;
justify-content: center;
align-items: center;
}
.controls .navigate.disabled {
pointer-events: none;
color: #606060;
background-color: #f7f7f7;
}
.controls .navigate.navigate-play {
width: 38px;
height: 38px;
}
.navigate-play .fa-play {
margin-left: 3px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://unpkg.com/axios#0.22.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/vue#next"></script>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght#300;500&display=swap" rel="stylesheet">
<div class="player-container">
<div id="audioPlayer">
<span class="volume" #click="toggleMute">
<i v-show="!muted" class="fa fa-volume-up"></i>
<i v-show="muted" class="fa fa-volume-off"></i>
</span>
<div class="album">
<div class="album-items">
<div class="cover" :class="{'spinning' : isSpinning}"></div>
<div class="info">
<h1>{{tracks[trackCount].name}}</h1>
<h2>{{tracks[trackCount].artistName}}</h2>
</div>
</div>
</div>
<div class="to-bottom">
<div class="track"></div>
<div class="controls">
<div class="navigate navigate-prev" :class="{'disabled' : trackCount == 0}" #click="prevTrack">
<i class="fa fa-step-backward"></i>
</div>
<div class="navigate navigate-play" #click="playPause">
<i v-show="!isPlaying" class="fa fa-play"></i>
<i v-show="isPlaying" class="fa fa-pause"></i>
</div>
<div class="navigate navigate-next" :class="{'disabled' : trackCount == tracks.length - 1}" #click="nextTrack">
<i class="fa fa-step-forward"></i>
</div>
</div>
</div>
</div>
</div>
The problem
But, to my surprise, the app is unaware of the fact that the value of this.player.ended has changed (or is supposed to).
What am I doing wrong?
An Audio object will not function in the same manner as normal JavaScript objects in Vue, as the internals that Vue uses to abstract away state change observation will not be maintained when the Audio object changes state. In other words, the thing that allows Vue to detect the Audio object switching from ended === false to ended === true won't work, preventing Vue from knowing that the component needs to be updated.
If you wish to observe a change in ended state, then you'll want to add a custom event listener to the object in your created hook to toggle the spinning state and simply remove the ended check:
const musicApp = {
data() {
return {
player: new Audio(),
trackCount: 0,
tracks: [],
muted: false,
isPlaying: false
};
},
methods: {
async getTracks() {
try {
const response = await axios
.get(
"https://api.napster.com/v2.1/tracks/top?apikey=ZTk2YjY4MjMtMDAzYy00MTg4LWE2MjYtZDIzNjJmMmM0YTdm"
)
.catch((error) => {
console.log(error);
});
this.tracks = response;
this.tracks = response.data.tracks;
} catch (error) {
console.log(error);
}
},
nextTrack() {
if (this.trackCount < this.tracks.length - 1) {
this.trackCount++;
this.setPlayerSource();
this.playPause();
}
},
prevTrack() {
if (this.trackCount >= 1) {
this.trackCount--;
this.setPlayerSource();
this.playPause();
}
},
setPlayerSource() {
this.player.src = this.tracks[this.trackCount].previewURL;
},
playPause() {
if (this.player.paused) {
this.isPlaying = true;
this.player.play();
} else {
this.isPlaying = false;
this.player.pause();
}
},
toggleMute() {
this.player.muted = !this.player.muted;
this.muted = this.player.muted;
}
},
async created() {
await this.getTracks();
this.setPlayerSource();
this.player.addEventListener('ended', () => {
this.isPlaying = false;
});
},
computed: {
isSpinning() {
return this.isPlaying;
}
}
};
Vue.createApp(musicApp).mount("#audioPlayer");
html,
body {
margin: 0;
padding: 0;
font-size: 16px;
}
body * {
box-sizing: border-box;
font-family: "Montserrat", sans-serif;
}
#-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
#keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.player-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #2998ff;
background-image: linear-gradient(62deg, #2998ff 0%, #5966eb 100%);
}
#audioPlayer {
width: 300px;
height: 300px;
border-radius: 8px;
position: relative;
overflow: hidden;
background-color: #00ca81;
background-image: linear-gradient(160deg, #00ca81 0%, #ffffff 100%);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
display: flex;
flex-direction: column;
align-items: center;
}
.volume {
color: #ff0057;
opacity: 0.9;
display: inline-block;
width: 20px;
font-size: 20px;
position: absolute;
top: 5px;
right: 6px;
cursor: pointer;
}
.album {
width: 100%;
flex: 1;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
.album-items {
padding: 0 10px;
text-align: center;
}
.cover {
width: 150px;
height: 150px;
margin: auto;
box-shadow: 0px 5px 12px 0px rgba(0, 0, 0, 0.17);
border-radius: 50%;
background: url("https://w7.pngwing.com/pngs/710/955/png-transparent-vinyl-record-artwork-phonograph-record-compact-disc-lp-record-disc-jockey-symbol-miscellaneous-classical-music-sound.png") center top transparent;
background-size: cover;
}
.cover.spinning {
webkit-animation: spin 6s linear infinite;
/* Safari */
animation: spin 6s linear infinite;
}
.info {
width: 100%;
padding-top: 5px;
color: #000;
opacity: 0.85;
}
.info h1 {
font-size: 11px;
margin: 5px 0 0 0;
}
.info h2 {
font-size: 10px;
margin: 3px 0 0 0;
}
.to-bottom {
width: 100%;
margin-top: auto;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.track {
background-color: #ff0057;
opacity: 0.9;
height: 3px;
width: 100%;
}
.controls {
width: 150px;
display: flex;
height: 60px;
justify-content: space-between;
align-items: center;
}
.controls .navigate {
display: flex;
box-shadow: 1px 2px 7px 2px rgba(0, 0, 0, 0.09);
width: 33px;
height: 33px;
line-height: 1;
color: #ff0057;
cursor: pointer;
background: #fff;
opacity: 0.9;
border-radius: 50%;
text-align: center;
justify-content: center;
align-items: center;
}
.controls .navigate.disabled {
pointer-events: none;
color: #606060;
background-color: #f7f7f7;
}
.controls .navigate.navigate-play {
width: 38px;
height: 38px;
}
.navigate-play .fa-play {
margin-left: 3px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<script src="https://unpkg.com/axios#0.22.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/vue#next"></script>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght#300;500&display=swap" rel="stylesheet">
<div class="player-container">
<div id="audioPlayer">
<span class="volume" #click="toggleMute">
<i v-show="!muted" class="fa fa-volume-up"></i>
<i v-show="muted" class="fa fa-volume-off"></i>
</span>
<div class="album">
<div class="album-items">
<div class="cover" :class="{'spinning' : isSpinning}"></div>
<div class="info">
<h1>{{tracks[trackCount].name}}</h1>
<h2>{{tracks[trackCount].artistName}}</h2>
</div>
</div>
</div>
<div class="to-bottom">
<div class="track"></div>
<div class="controls">
<div class="navigate navigate-prev" :class="{'disabled' : trackCount == 0}" #click="prevTrack">
<i class="fa fa-step-backward"></i>
</div>
<div class="navigate navigate-play" #click="playPause">
<i v-show="!isPlaying" class="fa fa-play"></i>
<i v-show="isPlaying" class="fa fa-pause"></i>
</div>
<div class="navigate navigate-next" :class="{'disabled' : trackCount == tracks.length - 1}" #click="nextTrack">
<i class="fa fa-step-forward"></i>
</div>
</div>
</div>
</div>
</div>
Try to add ended event instead
https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement#events
const audioElement = new Audio('car_horn.wav');
audioElement.addEventListener('ended', () => {
this.isPlaying = false
})

All values are true, although the values have been changed

I have three input fields. Once for email, password and confirm password. If the user presses a button then a method checkAll() should be executed, this checks if the defaults for the fields are correct, if not another class should be assigned to the fields and the variable should be false.
The problem is, if all values are true then the default classes should be displayed again and console.log("Everything ok"); should be output. However, if I reload the page and don't fill anything (everything should be set to false) then I still get an Everything ok in the console and all the variables are set to true, even though the method has set the values to false. Why is that?
SignUp.js
import React, { useState } from "react";
import "./SignUp.css";
function SignUp() {
const [email, setEmail] = useState(true);
const [value_email, setValue_email] = useState('')
const [password, setPassword] = useState('')
const [passwordLength, setPasswordLenth] = useState(true)
const [passwordValueEqual, setPasswordValueEqual] = useState('')
const [passwordEqual, setPasswordEqual] = useState(true)
const [emailExist, setEmailExist] = useState(false)
const checkEmail = async () => {
// eslint-disable-next-line
var reg = /^([A-Za-z0-9_\-\.])+\#([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
if (reg.test(value_email) === false) {
console.log("E-Mail is wrong");
setEmail(false);
}
else {
setEmail(true);
}
};
const checkPasswortLength = async () => {
if(password.length < 5 ) {
console.log("Passwort zu kurz");
setPasswordLenth(false);
}
else{
setPasswordLenth(true);
}
}
const checkPasswortEqual = async () => {
if(passwordValueEqual === password ) {
setPasswordEqual(true);
console.log("Passwörter sind gleich");
}
else{
console.log("Passwörter sind ungleich");
setPasswordEqual(false);
}
}
async function checkAll() {
await checkEmail();
await checkPasswortLength();
await checkPasswortEqual();
if(email === true && passwordLength === true && passwordEqual === true ) {
console.log("Everything ok");
setEmailExist(true);
}
}
return (
<div className="SignUp">
<div className="container" id="container">
<div className="form-container sign-in-container">
<form>
<h2>Registriere dich jetzt</h2>
<input type="email" className={email ? 'input-form' : 'input-form-validation-wrong'} placeholder="E-Mail" onChange={event => setValue_email(event.target.value)} />
<p className={email ? 'validation-email-right' : 'validation-email-wrong'}>E-Mail ist falsch</p>
<input type="password" id="password" className={passwordLength ? 'input-form' : 'input-form-validation-wrong'} placeholder="Passwort" onChange={event => setPassword(event.target.value)} />
<p className={passwordLength ? 'validation-password-short-right' : 'validation-password-short-wrong'}>Passwort ist zu kurz</p>
<input type="password" id="password-confirm" className={ passwordEqual ? 'input-form' : 'input-form-validation-wrong'} placeholder="Passwort bestätigen" onChange={event => setPasswordValueEqual(event.target.value)} />
<p className={passwordEqual ? 'validation-password-equal-right' : 'validation-password-equal-wrong'}>Passwörter stimmen nicht über ein</p>
{emailExist === true &&
<p className='validation-password-equal-wrong'>E-Mail gibt es bereits</p>
}
<div className='buttons-container'>
<button className="button-registration" type="button" onClick={checkAll}>Registrieren</button>
</div>
<a className="already-account" href="/login">Du hast bereits einen Account?</a>
</form>
</div>
<div className="overlay-container">
<div className="overlay">
<div className="overlay-panel overlay-right">
</div>
</div>
</div>
</div>
</div>
);
}
export default SignUp;
SignUp.css
#import url('https://fonts.googleapis.com/css?family=Montserrat:400,800');
* {
box-sizing: border-box;
}
body {
background: #fff;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
font-family: 'Montserrat', sans-serif;
height: 100vh;
}
h1 {
font-weight: bold;
margin: 0;
}
h2 {
text-align: center;
font-weight: normal;
font-size: 18px;
}
p {
font-size: 14px;
font-weight: 100;
line-height: 20px;
letter-spacing: 0.5px;
margin: 20px 0 30px;
}
span {
font-size: 12px;
}
a {
color: #333;
font-size: 14px;
text-decoration: none;
margin: 15px 0;
}
.buttons-container {
text-align: center;
display: flex;
margin-top: 35px;
}
.button-registration {
display: flex;
border-radius: 20px;
border: 1px solid #5869FF;
background-color: #5869FF;
color: #FFFFFF;
font-size: 12px;
font-weight: bold;
padding: 12px 25px;
letter-spacing: 1px;
text-transform: uppercase;
transition: transform 80ms ease-in;
}
button:active {
transform: scale(0.95);
}
button:focus {
outline: none;
}
button.ghost {
background-color: transparent;
border-color: #FFFFFF;
}
form {
background-color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 50px;
height: 100%;
text-align: center;
}
.input-form {
background-color: #e6e6e6;
border: none;
border-radius: 20px;
padding: 12px 15px;
margin: 8px 0;
width: 100%;
}
.input-form:focus {
outline: none;
box-shadow: 0px 0px 2px #5869FF;
}
.input-form-validation-wrong {
background-color: #e6e6e6;
border: none;
border-radius: 20px;
padding: 12px 15px;
margin: 8px 0;
width: 100%;
}
.input-form-validation-wrong {
background-color: #e6e6e6;
border: none;
border-radius: 20px;
padding: 12px 15px;
margin: 8px 0;
width: 100%;
box-shadow: 0px 0px 4px #ff5858;
}
.input-form-validation-wrong:focus {
outline: none;
box-shadow: 0px 0px 4px #ff5858;
}
.input-form-validation-wrong::placeholder {
color: #ff5858;
}
.input-remain {
}
.optional-buttons {
text-align: center;
display: inline-block;
font-size: 12px;
}
.already-account {
color: #5869FF;
text-decoration: underline;
font-size: 12px;
text-align: center;
}
.already-account:hover {
color: #6a79fc;
text-decoration: underline;
}
.container {
position: absolute;
top: 0px;
bottom: 0px;
left: 0px;
right: 0px;
overflow: hidden;
}
.form-container {
position: absolute;
top: 0;
height: 100%;
max-width: 40%;
}
.sign-in-container {
left: 0;
width: 50%;
z-index: 2;
}
.container.right-panel-active .sign-in-container {
transform: translateX(100%);
}
.sign-up-container {
left: 0;
width: 50%;
opacity: 0;
z-index: 1;
}
.validation-wrong {
color: #ff5858;
margin-top: -2px;
margin-bottom: -2px;
text-align: left;
}
.validation-email-wrong {
color: #ff5858;
margin-top: -2px;
margin-bottom: -2px;
text-align: left;
}
.validation-email-right {
visibility: hidden;
margin-top: -2px;
margin-bottom: -2px;
}
.validation-password-short-wrong {
color: #ff5858;
margin-top: -2px;
margin-bottom: -2px;
text-align: left;
}
.validation-password-short-right {
visibility: hidden;
margin-top: -2px;
margin-bottom: -2px;
}
.validation-password-equal-wrong {
color: #ff5858;
margin-top: -2px;
margin-bottom: -2px;
text-align: left;
}
.validation-password-equal-right {
visibility: hidden;
margin-top: -2px;
margin-bottom: -2px;
}
.validation-right {
visibility: hidden;
margin-top: -2px;
margin-bottom: -2px;
}
.container.right-panel-active .sign-up-container {
transform: translateX(100%);
opacity: 1;
z-index: 5;
animation: show 0.6s;
}
#keyframes show {
0%, 49.99% {
opacity: 0;
z-index: 1;
}
50%, 100% {
opacity: 1;
z-index: 5;
}
}
.overlay-container {
position: absolute;
top: 0;
left: 40%;
width: 60%;
height: 100%;
overflow: hidden;
transition: transform 0.6s ease-in-out;
z-index: 100;
background-image: url('../../images/wallpaper.PNG');
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
.container.right-panel-active .overlay-container{
transform: translateX(-100%);
}
.overlay {
/*background: -webkit-linear-gradient(to right top, #5869ff, #5c66ff, #6063ff, #6460ff, #685dff, #6d5bff, #715aff, #7658ff, #7c58ff, #8158ff, #8658ff, #8b58ff);*/
/*background: linear-gradient(to right top, #5869ff, #5c66ff, #6063ff, #6460ff, #685dff, #6d5bff, #715aff, #7658ff, #7c58ff, #8158ff, #8658ff, #8b58ff);*/
background-repeat: no-repeat;
background-size: cover;
background-position: 0 0;
color: #FFFFFF;
position: relative;
left: -100%;
height: 100%;
width: 200%;
transform: translateX(0);
transition: transform 0.6s ease-in-out;
}
.container.right-panel-active .overlay {
transform: translateX(50%);
}
.overlay-panel {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 40px;
text-align: center;
top: 0;
height: 100%;
width: 50%;
transform: translateX(0);
transition: transform 0.6s ease-in-out;
}
.overlay-left {
transform: translateX(-20%);
}
.container.right-panel-active .overlay-left {
transform: translateX(0);
}
.overlay-right {
right: 0;
transform: translateX(0);
}
.container.right-panel-active .overlay-right {
transform: translateX(20%);
}
.social-container {
margin: 20px 0;
}
.social-container a {
border: 1px solid #DDDDDD;
border-radius: 50%;
display: inline-flex;
justify-content: center;
align-items: center;
margin: 0 5px;
height: 40px;
width: 40px;
}
#media screen and (max-width: 960px) {
.overlay {
visibility: hidden;
}
.overlay-container {
visibility: hidden;
}
.form-container {
max-width: 100%;
min-width: 100%;
}
.optional-buttons {
display: flex;
align-items: center;
}
}
App.js
import "./styles.css";
import SignUp from "./SignUp";
export default function App() {
return (
<div className="App">
<SignUp></SignUp>
</div>
);
}
Reload Page
First Button click (the yellow mark is wrong)
Second Button click this time without the print statement everything okay
Since you are calling a function that updates a state on another function, the following code on the same function block won't use the updated value.
what you can do is return the updated value whenever you set it:
const checkEmail = async () => {
// eslint-disable-next-line
var reg = /^([A-Za-z0-9_\-\.])+\#([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
if (reg.test(value_email) === false) {
console.log("E-Mail is wrong");
setEmail(false);
return false; // add this one -------
}
else {
setEmail(true);
return true; // add this one --------
}
};
^^ do that for the other functions as well (checkPasswortLength, and checkPasswortEqual)
Then, on your checkAll:
async function checkAll() {
const emailCheck = await checkEmail();
const passwordLengthCheck = await checkPasswortLength();
const passwordEqualCheck = await checkPasswortEqual();
if(emailCheck === true && passwordLengthCheck === true && passwordEqualCheck === true ) {
console.log("Everything ok");
setEmailExist(true);
}
}
UPDATE
We can't use setstate callback functions since the setstate is called on another function, One other way is to use useEffect.
useEffect(() => {
if(email === true && passwordLength === true && passwordEqual === true ) {
console.log("Everything ok");
setEmailExist(true);
}
}, [])
async function checkAll() {
await checkEmail();
await checkPasswortLength();
await checkPasswortEqual();
// we removed the condition here
}
this way you don't have to "return" the value and will automatically notify the user everytime everything is ok.

Intersection Observer API for infinite scrolling

I am doing a twitch web app. I try to use the Intersection observer API to achieve infinite scrolling and it works. However, I notice that my navbar just disappears at all when the infinite scrolling is working. I guess the issue is caused by "DOMContentLoaded", but not sure. Thank you.
Here is my codepen link : Twitch
const clientID = '5npghe3kytuifte3z9kvwnto50mqch';
const req = new XMLHttpRequest();
function showError() {
alert('Error');
}
function getResp(url, callback) {
req.open('GET', url, true);
req.setRequestHeader('Client-ID', clientID);
req.setRequestHeader('Accept', 'application/vnd.twitchtv.v5+json');
req.send();
req.onload = function () {
if (req.status >= 200 && req.status < 400) {
let data
try {
data = JSON.parse(req.response)
} catch (err) {
showError();
return;
}
callback(data)
} else {
showError();
}
}
}
const navList = document.querySelector('.nav__list')
const streamBox = document.querySelector('.stream_box')
const streamItems = document.querySelector('.streamItems')
const langFilter = document.querySelector('.langFilter')
const langArr = ['ALL', 'EN', 'ZH', 'ES', 'FR', 'DE', 'RU', 'KO', 'JA', 'PT', 'AR'];
const urlRoot = 'https://api.twitch.tv/kraken/'
const topGameurl = `${urlRoot}games/top?limit=5`
const streamApi = `${urlRoot}streams/`
let offset = 0
document.addEventListener('DOMContentLoaded', ()=> {
const target = document.querySelector('.stream-end')
let options = {
root: null,
rootMargin: '30px', // looking entire viewport
threshold: 0.5, // if 50% of footer
}
const observer = new IntersectionObserver(handleIntersection, options)
observer.observe(target)
})
function handleIntersection(entries) {
if (entries[0].isIntersecting) {
let gameTitle = document.querySelector('.gameTitle')
let gameURLname = encodeURIComponent(gameTitle.innerHTML)
const loadmorestreamUrl = createURL(streamApi, gameURLname, offset)
offset += 100
getData(loadmorestreamUrl)
}
}
function createURL(url, game, offset) {
const streamUrl = `${url}?game=${game}&limit=20&offset=${offset}`
return streamUrl
}
getResp(topGameurl, (data) => {
const topGames = [...data.top]
const result = topGames.reduce((result, item) => {
result += `<li>${item.game.name}</li>`
return result
}, '')
navList.innerHTML = result
const gameName = encodeURIComponent(data.top[0].game.name)
const streamUrl = createURL(streamApi, gameName, offset)
getData(streamUrl)
})
navList.addEventListener('click', e => {
streamItems.innerHTML = ''
let gameTitle = document.querySelector('.gameTitle')
const gameName = e.target.innerHTML
gameTitle.innerHTML = gameName
const gameNameURL = encodeURIComponent(gameName)
const streamUrl = createURL(streamApi, gameNameURL, offset)
getData(streamUrl)
})
function getData(url) {
getResp(url, (data) => {
const dataArrs = [...data.streams]
dataArrs.map(dataArr => {
let streamItem = document.createElement('div')
streamItem.classList.add('stream')
streamItem.innerHTML = `
<p class="viewers">Viewers: ${dataArr.viewers}</p>
<img src="${dataArr.preview.large}" alt="" class="preview">
<div class="streamer">
<img src="${dataArr.channel.logo}" alt="" class="logo">
<p class="name">${dataArr.channel.name}</p>
<p class="lang">${dataArr.channel.broadcaster_language.toUpperCase()}</p>
</div>
`
streamItems.appendChild(streamItem)
})
})
}
html, body {
font-size: 16px;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background:black;
background-attachment: fixed;
}
.container {
width: 90%;
margin: 0 auto;
display: flex;
flex-direction: column;
padding: 2rem;
overflow-x: hidden;
}
#header {
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
margin-bottom: 1.2rem;
}
.navbar {
position: relative;
transform: translateX(0%);
}
.title {
margin-right: 1rem;
font-size: 2rem;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-weight: 700;
color: #741cf7;
}
.nav__list {
display: flex;
}
.nav__list li {
padding: .8rem .6rem;
cursor: pointer;
font-size: 1rem;
font-weight: 700;
}
.nav__list li:hover {
border-radius: .5rem;
background: #a87ceb;
transition: background .3s ease;
}
.nav__list li + li {
margin-left: 1rem;
}
#stream_box {
display: flex;
flex-direction: column;
border-radius: 8px;
align-items: center;
padding: 1.5rem;
color: #fff;
position: relative;
}
.game__title {
font-size: 2rem;
margin-bottom: 1rem;
font-weight: 600;
color:#741cf7;
}
.top__twenty {
font-size: 1.4rem;
margin-bottom: 1.5rem;
}
.streamItems {
display: flex;
flex-flow: row wrap;
width: 100%;
justify-content: center;
}
.lang__options {
position: absolute;
top: 2rem;
right: 7rem;
}
.lang__title {
font-size: 1.2rem;
color: #fff;
}
#language {
outline: none;
width: 3rem;
background: transparent;
color: #741cf7;
border: none;
font-size: 1rem;
}
.stream {
width: 30%;
margin: 1.5rem;
background-color: rgba(255, 255, 255, .15);
backdrop-filter: blur(5px);
cursor: pointer;
}
.preview {
width: 100%;
vertical-align: middle;
}
.viewers {
display: none;
}
.stream:hover > .viewers {
display: block;
position: absolute;
z-index: -1;
animation-name: move;
animation-duration: .4s;
animation-timing-function: ease;
animation-fill-mode: forwards;
}
#keyframes move {
from {
top: 0px;
}
to {
top: -20px;
}
}
.streamer {
display: flex;
align-items: center;
padding: .5rem;
position: relative;
color: #fff;
}
.logo {
width: 15%;
border-radius: 50%;
margin-right: .8rem;
}
.lang {
position: absolute;
right: .5rem;
bottom: .5rem;
}
.hidden {
display: none;
}
.check {
display: none;
}
.rwdSwitch {
display: none;
}
.gameTitle {
color: #fff;
}
#media screen and (max-width: 1024px) {
.lang__options {
position: absolute;
top: 7rem;
right: 50%;
transform: translateX(50%);
}
.streamItems {
flex-flow: row wrap;
width: 100%;
justify-content: center;
margin-top: 1.2rem;
}
.stream {
width: 100%;
}
.nav__list li {
text-align: center;
font-size: 1rem;
}
}
#media screen and (max-width: 576px) {
body {
overflow-x: hidden;
}
.navbar {
position: absolute;
top: 20%;
right: 50%;
transform: translateX(200%);
transition: transform .3s ease-in-out;
background-color: rgba(255, 255, 255, .15);
backdrop-filter: blur(5px);
z-index: 999;
visibility: hidden;
}
.top__twenty {
font-size: .8rem;
}
.nav__list {
flex-direction: column;
align-items: flex-end;
}
.rwdSwitch {
display: block;
cursor: pointer;
position: absolute;
right:1.5rem;
top: 2.5rem;
z-index: 999;
}
.check:checked ~ .navbar {
visibility: visible;
transform: translateX(100%);
transition: transform .3s ease-in-out;
}
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="style.css">
<title>Twitch</title>
</head>
<body>
<div class="container">
<header id="header">
<label for="check__status" class="rwdSwitch"><i class="fas fa-bars"></i></label>
<input type="checkbox" class="check" id="check__status">
<h1 class="title">Twitch Top Games</h1>
<nav class="navbar">
<ul class="nav__list"></ul>
</nav>
</header>
<div class="selections">
<label for="language" class="lang__title">Filter by Language: </label>
<select name="lang" id="language" class="langFilter"></select>
</div>
<main class="stream_box">
<p class="gameTitle"></p>
<div class="streamItems"></div>
</main>
<div class="stream-end"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/js/all.min.js"></script>
<script src="api.js"></script>
<script src="app.js"></script>
</body>
</html>
First of all please don't use XMLHttpRequest it's very old and hard to use and read. Use fetch instead.
Your problem is caused by the fact that you are creating one XMLHttpRequest. So before the first request is completed the second request starts and cancel the first one. Just move const req = new XMLHttpRequest(); to getResp.
But I would just change getResp to:
function getResp(url, callback) {
fetch(url, {
headers: {
'Client-ID': clientID,
'Accept': 'application/vnd.twitchtv.v5+json'
}
})
.then(response => response.json())
.then(callback)
.catch(showError)
}

Javascript focus() is not working in mobile browser on element that just entered the window from top, React functional component

I've built a search bar dropdown as part of a navbar I'm working on in React. The search bar's starting position is above the window, top: -112px. When the search button on the nav is clicked, the search bar animation begins and the the drop down goes from top: -112px to top: 0. After the animation is complete and the 'shown' class is applied to the search bar container, a useEffect watching the displayClass value triggers and focuses on the search bar input field.
This works fine on desktop Chrome and Safari, but on mobile the input field does not focus once in view for either browser. I've tried setTimeouts of every length on the focus call to no avail. Any help would be greatly appreciated.
SearchBar component:
export const SearchBar = ({
analyticsSearchBarSearchInput,
display,
history,
onClose,
}) => {
const [displayClass, setDisplayClass] = useState("");
const [searchActive, setSearchActive] = useState(false);
const prevDisplayRef = useRef();
useEffect(() => {
prevDisplayRef.current = display;
});
const prevDisplay = prevDisplayRef.current;
useEffect(() => {
if (!prevDisplay && display) {
onSearchOpen();
} else if (prevDisplay && !display) {
onSearchClose();
}
}, [display, prevDisplay]);
useEffect(() => {
if (displayClass === "shown") {
document.getElementById("search-bar-input").focus({
preventScroll: true,
});
}
}, [displayClass]);
return (
<ConditionalWrapper
condition={displayClass === "shown"}
wrapper={(children) => (
<OutsideClick onClick={onClose}>{children}</OutsideClick>
)}
>
<div className={`search-bar__container ${displayClass}`}>
<div className="search-bar__left">
<div className={`search-bar__search-icon${getSearchIconClass()}`} />
<div className="search-bar__input">
<SearchForm
onSubmit={onSearchSubmit}
searchActive={searchActive}
setSearchActive={setSearchActive}
/>
</div>
</div>
<div className="search-bar__right">
<div
aria-label="Close search"
className="search-bar__close-icon"
onClick={onClose}
onKeyDown={onClose}
role="button"
tabIndex={0}
/>
</div>
</div>
</ConditionalWrapper>
);
function getSearchIconClass() {
return searchActive ? " active" : "";
}
function onSearchClose() {
setDisplayClass("hide");
setTimeout(() => {
setDisplayClass("");
}, 300);
enableBodyScroll();
document.body.removeEventListener("touchmove", touchMoveCallback, {
passive: false,
});
}
function onSearchOpen() {
setDisplayClass("show");
setTimeout(() => {
setDisplayClass("shown");
}, 200);
disableBodyScroll();
document.body.addEventListener("touchmove", touchMoveCallback, {
passive: false,
});
}
function onSearchSubmit(usersInput) {
const hostUrl = window.location.host;
const pageNumber = 1;
const searchUrl = `${hostUrl}/search?query=${usersInput}&page=${pageNumber}`;
if (displayClass === "shown" && usersInput !== "") {
Promise.resolve(
analyticsSearchBarSearchInput(hostUrl, usersInput, searchUrl)
).then(() => {
onClose();
history.push(`/search?query=${usersInput}&page=${pageNumber}`);
});
}
}
};
const ConditionalWrapper = ({ condition, wrapper, children }) =>
condition ? wrapper(children) : children;
const touchMoveCallback = (e) => {
e.preventDefault();
};
SearchBar Form Component:
export const SearchForm = ({ onSubmit, searchActive, setSearchActive }) => {
const [search, setSearch] = useState("");
useEffect(() => {
search !== "" ? setSearchActive(true) : setSearchActive(false);
}, [search, setSearchActive]);
return (
<div className="search-bar__form-container">
<form action="#" onSubmit={onSearchSubmit} className="search-bar__form">
<input
autoComplete="off"
name="search"
className="search-bar__input"
id="search-bar-input"
onChange={handleChange}
placeholder="Search"
type="search"
value={search}
onKeyDown={onKeyDown}
/>
</form>
{searchActive && (
<div
aria-label="Search submit"
className="search-bar__submit-button"
onClick={onSearchSubmit}
onKeyDown={onKeyDown}
role="button"
tabIndex={0}
>
SEARCH>
</div>
)}
</div>
);
function handleChange(event) {
setSearch(event.target.value);
}
function onSearchSubmit() {
onSubmit(search);
setSearch("");
document.activeElement.blur();
}
function onKeyDown(event) {
if (event.keyCode === 13) {
event.preventDefault();
onSearchSubmit();
}
}
};
Styling:
.search-bar {
&__close-icon {
background: url("/images/icons/close.png") no-repeat;
background-size: contain;
cursor: pointer;
height: 26px;
transition: all 80ms ease-out;
width: 26px;
&:hover {
background: url("/images/icons/close-hover.png") no-repeat;
background-size: contain;
}
}
&__container {
align-items: center;
background-color: #ffffff;
border-bottom: 1px solid #d8d8d8;
color: #c5c5c5;
display: flex;
height: 112px;
justify-content: space-between;
left: 0;
position: fixed;
top: -112px;
width: 100%;
z-index: 1020;
&.show {
animation: searchFadeIn 200ms ease-out;
}
&.shown {
top: 0;
box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.4);
}
&.hide {
animation: searchFadeOut 300ms ease-in;
}
}
#keyframes searchFadeIn {
0% {
box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0);
}
50% {
box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.2);
}
100% {
box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.4);
top: 0px;
}
}
#keyframes searchFadeOut {
0% {
box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.4);
top: 0;
}
50% {
box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0.2);
}
100% {
box-shadow: 0 0 0 100vmax rgba(51, 51, 51, 0);
top: -75px;
}
}
&__icon {
background: url("/images/icons/search.png") no-repeat;
background-size: contain;
cursor: pointer;
height: 24px;
padding-right: 20px;
width: 24px;
}
&__input {
background: none;
border: none;
color: #333333;
flex: 1;
font-family: Setimo;
font-size: 42px;
line-height: 1.24;
letter-spacing: 2px;
max-width: 321px;
padding-right: 10px;
}
&__form {
margin: 0 20px 0 20px;
}
&__form-container {
display: flex;
}
&__left {
align-items: center;
display: flex;
padding-left: 40px;
}
&__right {
color: inherit;
font-size: 30px;
padding-right: 40px;
}
&__search-icon {
background: url("/images/icons/search-grey.png") no-repeat;
background-size: contain;
cursor: pointer;
height: 24px;
padding-right: 20px;
width: 24px;
&.active {
background: url("/images/icons/search.png") no-repeat;
background-size: contain;
}
}
&__submit-button {
align-self: flex-end;
color: #9a9a9a;
cursor: pointer;
flex: 1;
font-family: Setimo;
font-size: 11px;
font-weight: bold;
line-height: 1.64;
letter-spacing: 3.5px;
margin-bottom: 7px;
transition: all 80ms ease-out;
width: 81px;
&:hover {
color: #333333;
}
}
}
.search-icon {
&__container {
align-items: center;
cursor: pointer;
display: flex;
justify-content: space-between;
transition: all 80ms ease-out;
width: 97px;
&:hover .search-icon__text {
color: #7d7be4;
}
&:hover .search-icon__icon {
background: url("/images/icons/search-hover.png") no-repeat;
background-size: contain;
}
}
&__icon {
background: url("/images/icons/search.png") no-repeat;
background-size: contain;
cursor: pointer;
height: 20px;
width: 20px;
}
&__text {
color: #333333;
font-family: Setimo;
font-size: 11px;
font-weight: bold;
letter-spacing: 3.5px;
line-height: 1.64;
}
}
::placeholder {
color: #c5c5c5;
}
[type="search"] {
-webkit-appearance: textfield;
}
input[type="text"]::-ms-clear {
display: none;
width: 0;
height: 0;
}
input[type="text"]::-ms-reveal {
display: none;
width: 0;
height: 0;
}
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration {
display: none;
}
#media (max-width: 959px) {
.search-bar {
&__container {
height: 80px;
top: -80px;
}
&__form {
margin: 0;
}
&__input {
font-size: 32px;
max-width: 225px;
}
&__left {
padding-left: 30px;
}
&__right {
padding-right: 30px;
}
&__search-icon {
padding-right: 14px;
}
}
}
#media (max-width: 480px) {
.search-bar {
&__close-icon {
height: 20px;
width: 20px;
}
&__container {
height: 64px;
top: -64px;
}
&__form {
margin: 0;
}
&__input {
font-size: 24px;
letter-spacing: normal;
max-width: 175px;
}
&__left {
padding-left: 16px;
}
&__right {
padding-right: 16px;
}
&__search-icon {
padding-right: 10px;
}
&__submit-button {
display: none;
}
}
}

Categories

Resources