I am working on a project which:
Asks the user to choose images from their device
The selected images would show up in the image slider
const slider = document.querySelector('.slider');
// load user chosen files
fileInp.addEventListener('input', (e) =>
{
const files = e.target.files;
for (let file of files) {
const img = new Image();
const reader = new FileReader();
reader.addEventListener('load', (e) =>
{
img.src = e.target.result;
slider.appendChild(img);
img.classList.add('slide');
})
reader.readAsDataURL(file);
}
})
// slider
const slides = document.querySelectorAll('.slide');
let counter = 0;
slides.forEach((slide, index) =>
{
slide.style.left =
`${index * 100}%`
})
nextBtn.onclick = () => {
counter++;
move();
}
prevBtn.onclick = () => {
counter--;
move();
}
function move() {
if (counter <= 0) {
counter = slides.length - 1;
}
if (counter === slides.length) {
counter = 0;
}
slides.forEach((slide, index) => {
slide.style.transform =
`translateX(-${counter * 100}%)`
})
}
#import url("https://fonts.googleapis.com/css2?family=Nunito:wght#400;900");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Nunito", sans-serif;
}
body {
display: grid;
place-items: center;
height: 100vh;
}
.box {
display: flex;
flex-direction: column;
padding: 1rem;
gap: 1rem;
}
.slider {
min-width: 300px;
min-height: 70px;
display: flex;
overflow: hidden;
position: relative;
}
.slide {
position: absolute;
height: 100%;
width: 100%;
object-fit: cover;
object-position: center;
transition: transform 250ms ease;
}
h1 {
margin-bottom: 0.25rem;
}
.controls {
display: flex;
gap: 0.5rem;
width: 100%;
align-items: center;
}
.btn {
background-color: #333;
border: 0;
padding: 0.5rem 0.75em;
width: 12ch;
text-transform: uppercase;
letter-spacing: 1px;
color: #fff;
}
.ph {
background-color: #08b;
width: max-content;
}
.btn:hover {
border: 2px solid #000;
border-radius: .25rem;
}
<div class="box">
<h1>Gallery</h1>
<div class="slider"></div>
<div class="controls">
<button class="btn" id="prevBtn">previous</button>
<input hidden type="file" id="fileInp" multiple />
<!-- Just a placeholder to easily style the file input -->
<button onclick="fileInp.click()" class="ph btn">choose files</button>
<button class="btn" id="nextBtn">next</button>
</div>
</div>
i am adding a snippet so you can understand it clearly,
When using static HTML tags, the slider works properly but when I generate images from file input using JavaScript it gives unwanted results. Where am I going wrong?
it because on page load const slides = document.querySelectorAll('.slide'); is empty, move it to file load event
const slider = document.querySelector('.slider');
let slides = [];
// load user chosen files
fileInp.addEventListener('input', (e) => {
const files = e.target.files;
for (let file of files) {
const img = new Image();
const reader = new FileReader();
reader.addEventListener('load', (e) => {
img.src = e.target.result;
slider.appendChild(img);
img.classList.add('slide');
slides = document.querySelectorAll('.slide');
slides.forEach((slide, index) => {
slide.style.left =
`${index * 100}%`
})
})
reader.readAsDataURL(file);
}
})
// slider
let counter = 0;
nextBtn.onclick = () => {
counter++;
move();
}
prevBtn.onclick = () => {
counter--;
move();
}
function move() {
if (counter <= 0) {
counter = slides.length - 1;
}
if (counter === slides.length) {
counter = 0;
}
slides.forEach((slide, index) => {
slide.style.transform =
`translateX(-${counter * 100}%)`
})
}
.slide {
position: absolute;
height: 100%;
width: 100%;
top: 110px;
object-fit: cover;
object-position: center;
transition: transform 250ms ease;
}
h1 {
margin-bottom: 0.25rem;
}
.controls {
display: flex;
gap: 0.5rem;
width: 100%;
align-items: center;
}
.btn {
background-color: #333;
border: 0;
padding: 0.5rem 0.75em;
width: 12ch;
text-transform: uppercase;
letter-spacing: 1px;
color: #fff;
}
.ph {
background-color: #08b;
width: max-content;
}
.btn:hover {
background-color: #222;
}
<div class="box">
<h1>Gallery</h1>
<div class="slider"></div>
<div class="controls">
<button class="btn" id="prevBtn">previous</button>
<input hidden type="file" id="fileInp" multiple />
<!-- Just a placeholder to easily style the file input -->
<button onclick="fileInp.click()" class="ph btn">choose files</button>
<button class="btn" id="nextBtn">next</button>
</div>
Related
I wanted to make a 3d rotating carousel like in the stack snippet, but required is a view from inside like in the image below. How can i achieve this?
window.addEventListener('load', () => {
var carousels = document.querySelectorAll('.carousel');
for (var i = 0; i < carousels.length; i++) {
carousel(carousels[i]);
}
});
function carousel(root) {
var
figure = root.querySelector('figure'),
nav = root.querySelector('nav'),
images = figure.children,
n = images.length,
gap = root.dataset.gap || 0,
bfc = 'bfc' in root.dataset,
theta = 2 * Math.PI / n,
currImage = 0;
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));
window.addEventListener('resize', () => {
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width))
});
setupNavigation();
function setupCarousel(n, s) {
var apothem = s / (2 * Math.tan(Math.PI / n));
figure.style.transformOrigin = `50% 50% ${- apothem}px`;
for (var i = 0; i < n; i++) {
images[i].style.padding = `${gap}px`;
}
for (i = 1; i < n; i++) {
images[i].style.transformOrigin = `50% 50% ${- apothem}px`;
images[i].style.transform = `rotateY(${i * theta}rad)`;
}
if (bfc) {
for (i = 0; i < n; i++) {
images[i].style.backfaceVisibility = 'hidden';
}
}
rotateCarousel(currImage);
}
function setupNavigation() {
nav.addEventListener('click', onClick, true);
function onClick(e) {
e.stopPropagation();
var t = e.target;
if (t.tagName.toUpperCase() != 'BUTTON') {
return;
}
if (t.classList.contains('next')) {
currImage++;
}
else {
currImage--;
}
rotateCarousel(currImage);
}
}
function rotateCarousel(imageIndex) {
figure.style.transform = `rotateY(${imageIndex * -theta}rad)`;
}
}
body {
margin: 0;
font-family: 'Roboto', sans-serif;
font-size: 16px;
}
h1 {
text-align: center;
margin-bottom: 1.5em;
}
h2 {
text-align: center;
color: #555;
margin-bottom: 0;
}
.carousel {
padding: 20px;
perspective: 500px;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
}
.carousel>* {
flex: 0 0 auto;
}
.carousel figure {
margin: 0;
width: 40%;
transform-style: preserve-3d;
transition: transform 0.5s;
}
.carousel figure img {
width: 100%;
box-sizing: border-box;
padding: 0 0px;
}
.carousel figure img:not(:first-of-type) {
position: absolute;
left: 0;
top: 0;
}
.carousel nav {
display: flex;
justify-content: center;
margin: 20px 0 0;
}
.carousel nav button {
flex: 0 0 auto;
margin: 0 5px;
cursor: pointer;
color: #333;
background: none;
border: 1px solid;
letter-spacing: 1px;
padding: 5px 10px;
}
<h2>Eight images, with 80px gap</h2>
<div class="carousel" data-gap="80">
<figure>
<img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="">
<img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="">
<img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="">
<img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="">
<img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="">
<img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="">
<img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="">
<img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="">
</figure>
<nav>
<button class="nav prev">Prev</button>
<button class="nav next">Next</button>
</nav>
</div>
The carousel needs to be rotated and translated in the opposite direction to make it an inner rotation view.
The transform-origin sets the center of the carousel. The first two numbers are x and y, which just center the carousel on the screen. The third number is z which determines whether the carousel is moved towards the screen or away from the screen. The negative sign on apothem is removed to pull it out of the screen so the center becomes closer to the camera and achieves the inner carousel. backface-visibility needs to be always set to hidden because there might be images behind the camera now. Then to fix the rotation direction with the next button, * -theta is changed to positive.
window.addEventListener('load', () => {
var carousels = document.querySelectorAll('.carousel');
for (var i = 0; i < carousels.length; i++) {
carousel(carousels[i]);
}
});
function carousel(root) {
var
figure = root.querySelector('figure'),
nav = root.querySelector('nav'),
images = figure.children,
n = images.length,
gap = root.dataset.gap || 0,
theta = 2 * Math.PI / n,
currImage = 0;
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width));
window.addEventListener('resize', () => {
setupCarousel(n, parseFloat(getComputedStyle(images[0]).width))
});
setupNavigation();
function setupCarousel(n, s) {
var apothem = s / (2 * Math.tan(Math.PI / n));
figure.style.transformOrigin = `50% 50% ${apothem}px`;
for (var i = 0; i < n; i++) {
images[i].style.padding = `${gap}px`;
}
for (i = 1; i < n; i++) {
images[i].style.transformOrigin = `50% 50% ${apothem}px`;
images[i].style.transform = `rotateY(${i * theta}rad)`;
}
rotateCarousel(currImage);
}
function setupNavigation() {
nav.addEventListener('click', onClick, true);
function onClick(e) {
e.stopPropagation();
var t = e.target;
if (t.tagName.toUpperCase() != 'BUTTON') {
return;
}
if (t.classList.contains('next')) {
currImage++;
}
else {
currImage--;
}
rotateCarousel(currImage);
}
}
function rotateCarousel(imageIndex) {
figure.style.transform = `rotateY(${imageIndex * theta}rad)`;
}
}
body {
margin: 0;
font-family: 'Roboto', sans-serif;
font-size: 16px;
}
h1 {
text-align: center;
margin-bottom: 1.5em;
}
h2 {
text-align: center;
color: #555;
margin-bottom: 0;
}
.carousel {
padding: 20px;
perspective: 500px;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
}
.carousel>* {
flex: 0 0 auto;
}
.carousel figure {
margin: 0;
width: 40%;
transform-style: preserve-3d;
transition: transform 0.5s;
}
.carousel figure img {
width: 100%;
box-sizing: border-box;
padding: 0 0px;
backface-visibility: hidden;
}
.carousel figure img:not(:first-of-type) {
position: absolute;
left: 0;
top: 0;
}
.carousel nav {
display: flex;
justify-content: center;
margin: 20px 0 0;
}
.carousel nav button {
flex: 0 0 auto;
margin: 0 5px;
cursor: pointer;
color: #333;
background: none;
border: 1px solid;
letter-spacing: 1px;
padding: 5px 10px;
}
<h2>Eight images, with 80px gap</h2>
<div class="carousel" data-gap="80">
<figure>
<img src="https://source.unsplash.com/VkwRmha1_tI/800x533" alt="">
<img src="https://source.unsplash.com/EbuaKnSm8Zw/800x533" alt="">
<img src="https://source.unsplash.com/kG38b7CFzTY/800x533" alt="">
<img src="https://source.unsplash.com/nvzvOPQW0gc/800x533" alt="">
<img src="https://source.unsplash.com/mCg0ZgD7BgU/800x533" alt="">
<img src="https://source.unsplash.com/1FWICvPQdkY/800x533" alt="">
<img src="https://source.unsplash.com/bjhrzvzZeq4/800x533" alt="">
<img src="https://source.unsplash.com/7mUXaBBrhoA/800x533" alt="">
</figure>
<nav>
<button class="nav prev">Prev</button>
<button class="nav next">Next</button>
</nav>
</div>
I saved all dom-elements in an object. I want that my Script is able to check every time when there is some input if all input fields are empty anymore. If yes, the button should still be disabled, otherwise the forward button is enabled and the user can click to the next page.
Ways like like using querySelectorAll input field etc. are working but I want that this is working with using the object.
The "areTruthy" variable is directly true if one input field is not empty anymore but it should only be true if all input fields are true. Where is my code wrong?
window.onload = () => {
forward = document.getElementById("forward");
input = {
caseNumber: {
month: document.getElementById("month"),
year: document.getElementById("year"),
},
clientsInformation: {
gender: document.getElementById("gender"),
inpName: document.getElementById("inpName"),
},
adress: {
street: document.getElementById("street"),
houseNumber: document.getElementById("house-number"),
postCode: document.getElementById("postCode"),
city: document.getElementById("city"),
receiver: document.getElementById("receiver"),
},
};
addEventListener("input", () => {
for (parts in input) {
const areTruthy = Object.values(input[parts]).every((value) => value != "");
if (areTruthy) {
forward.style.backgroundColor = "rgb(77,55,120)";
forward.style.color = "white";
forward.disabled = false;
forward.style.transition = "1s ease";
forward.addEventListener("click", () => {
window.open("case.html");
});
} else {
forward.style.backgroundColor = "rgb(191,191,191)";
forward.style.color = "black";
forward.disabled = true;
}
}
});
};
<body>
<h3>Fill in all fields</h3>
<div id = "wrapper">
<div id = "caseNumber">
<label id = "caseLabel"></label>
<input id = "month" placeholder="Zahlenfolge">
<input id = "year" placeholder = "Jahr">
</div>
<div id = "name">
<label for = "name">Name</label>
<select id = "gender">
<option>Herr</option>
<option>Frau</option>
</select>
<input id = "inpName" placeholder = "Name">
</div>
<div id = "adress">
<label for = "adress">Adresse</label>
<div id = "adressWrapper1">
<input placeholder = "Straße" id = "street" >
<input placeholder = "Hausnummer" id = "house-number">
<input placeholder = "Postleitzahl" id = "postCode" >
</div>
<div id = "adressWrapper2">
<input placeholder = "Stadt" id = "city" >
<input placeholder = "Adressant" id = "receiver" >
</div>
</div>
</div>
</div>
<div class = "button-bar">
<div class = "nav-inner" id = "backward"><a class = "nav-inner" href= "#http://127.0.0.1:5500/pages/client.html" ></a> < Zurück</div>
<div class = "nav-inner" id = "forward"> Weiter ></div>
</div>
</body>
</html>
.navbar {
display: flex;
list-style: none;
background-color: rgb(77, 55, 120);
margin: 0;
position: fixed;
width: 100%;
gap: 4rem;
height: 50px;
text-align: center;
line-height: 45px;
left: 0;
top: 0;
}
.nav-text {
text-decoration: none;
color: white;
width: auto;
cursor: pointer;
font-size: 18px;
padding-bottom: 5px;
}
#wrapper {
margin-top: 10rem;
margin-left: 5rem;
display: sticky;
}
#caseNumber {
display: block;
}
input::placeholder {
font-size: 1rem;
text-align: center;
line-height: 19rem;
}
.button-bar {
position: fixed;
bottom: 0;
width: 100%;
display: flex;
margin: 0;
left: 0;
}
.nav-inner {
cursor: pointer;
width: 50%;
text-align: center;
line-height: 83px;
}
#backward {
background-color: rgb(101, 93, 93);
color: white;
}
#forward {
background-color: rgb(191, 191, 191);
}
h3 {
left: 20vh;
position: absolute;
font-size: 24px;
margin-top: 0.5rem;
}
#caseNumber {
position: absolute;
left: 20vh;
margin-top: 6.5rem;
text-align: left;
}
#name {
position: absolute;
left: 20vh;
margin-top: 12.5rem;
}
label {
display: inline-block;
text-align: left;
width: 140px;
font-size: 20px;
}
input,
select {
width: 30vh;
height: 5vh;
font-size: 19px;
border-radius: 5px;
border-style: solid;
border: 1px solid;
}
#adress {
position: absolute;
left: 20vh;
margin-top: 18.5rem;
max-width: 40rem;
display: block;
}
#adressWrapper1 {
display: flex;
gap:5vh;
flex-direction: column;
left: 18vh;
position: absolute;
top:0rem
}
#adressWrapper2 {
display: flex;
gap:5vh;
flex-direction: column;
position: absolute;
left: 50vh;
top:0rem
}
You are not checking input values. Also you are only checking every on child per parent object key, not on all children together so you need to lift if outside for
addEventListener("input", () => {
let allFilled = true;
for (parts in input) {
const anyEmpty = Object.values(input[parts]).some(el => el.value == "");
if (anyEmpty) {
allFilled = false;
break;
}
}
if (allFilled) {
forward.style.backgroundColor = "rgb(77,55,120)";
forward.style.color = "white";
forward.disabled = false;
forward.style.transition = "1s ease";
forward.addEventListener("click", () => {
window.open("case.html");
});
} else {
forward.style.backgroundColor = "rgb(191,191,191)";
forward.style.color = "black";
forward.disabled = true;
}
});
};
If you want to see what is exactly happening you can use this to log
const anyEmpty = Object.values(input[parts]).some((el) => {
console.log(el, el.value, el.value == "");
return el.value == "";
});
I'm using an API to create an anime quiz. I'm also using Math.random() to create four choices for the user to click on. But I'm facing two problems. Firstly when the user is presented with the first set of 4 choices, there's a possibility that two are identical. I'd like all four choices to be distinct from each other. Secondly regardless of the user getting the right answer or not I'd like another set of four distinct questions to be generated. I tried to come up with something but it quickly turned into spaghetti code.
const animeApi = "https://anime-facts-rest-api.herokuapp.com/api/v1";
const intro = document.querySelector(".intro");
const anime_picture = document.querySelector(".anime_picture img");
const anime = document.querySelector(".anime");
const questions = Array.from(document.querySelector(".question").children)
const question1 = document.querySelector(".question1");
const question2 = document.querySelector(".question2");
const question3 = document.querySelector(".question3");
const question4 = document.querySelector(".question4");
const question5 = document.querySelector(".question5");
const randomNum1 = Math.floor((Math.random()* 13));
const randomNum2 = Math.floor((Math.random()* 13));
const randomNum3 = Math.floor((Math.random()* 13));
const randomNum4 = Math.floor((Math.random()* 13));
let [counter, score] = [0,0]
let data;
fetch(animeApi)
.then(res => res.json())
.then(response => {
// response is an object but we need the array in property data
console.log(response)
data = response.data;
console.log(data.length)
for (let {anime_img} of data) {
console.log(anime_img)
}
// alternative
//data.forEach(item => console.log(item));
});
intro.addEventListener("click", () => {
intro.classList.add("hide");
anime.classList.remove("hide");
anime.classList.add("show")
quiz()
});
function quiz() {
anime_picture.src = data[counter].anime_img;
question1.innerHTML = data[randomNum1].anime_name;
question2.innerHTML = data[randomNum2].anime_name;
question3.innerHTML = data[randomNum3].anime_name;
question4.innerHTML = data[randomNum4].anime_name;
}
for(var i = 0; i < questions.length; i++) {
questions[i].addEventListener("click", userAnswer)
}
function userAnswer(e) {
let target = e.target.innerHTML
if(target === data[counter].anime_name) {
console.log("correct");
score++
} else {
console.log("incorrect");
}
update();
}
function update () {
if(counter < data.length) {
counter++;
quiz();
}
}
body {
position: relative;
display: flex;
justify-content: center;
align-content: center;
}
.intro {
height: 300px;
width: 300px;
border: 1px blue solid;
position: absolute;
left: 25%;
text-align: center;
}
.hide {
visibility: hidden;
}
.show {
visibility: visible;
}
.anime {
height: 800px;
width: 800px;
border: 1px red solid;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.anime_picture {
height: 400px;
width: 400px;
position: absolute;
}
.question {
height: 100px;
width: 100%;
border: 1px blue solid;
bottom: 0;
position: absolute;
display: flex;
justify-content: space-around;
align-items: center;
}
.question > div {
height: 80px;
width: auto;
border: 1px black solid;
background-color: #ddd;
}
img {
height: 100%;
width: 100%;
object-fit: cover;
}
header {
width: 100%;
height: 100px;
border: 1px black solid;
position: absolute;
top: 0;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="index.css">
<title>anime page</title>
</head>
<body>
<div class="intro">
welcome to the anime website
</div>
<div class="anime hide">
<div class="anime_picture">
<img src="" alt="">
</div>
<div class="question">
<div class="question1"></div>
<div class="question2"></div>
<div class="question3"></div>
<div class="question4"></div>
</div>
<header>anime Quiz</header>
</div>
</body>
<script src="index.js" type="text/javascript"></script>
</html>
I little refactored your code and fixed bugs. And now you can get 4 random answers but with guarantee that there is correct answer. Also now the number of answers depends on count of div with class answer:
const animeApi = 'https://anime-facts-rest-api.herokuapp.com/api/v1';
const intro = document.querySelector('.intro');
const anime_picture = document.querySelector('.anime_picture img');
const anime = document.querySelector('.anime');
const answers = [...document.querySelectorAll('.answer')];
let data = [];
let [counter, score] = [0, 0];
fetch(animeApi)
.then(res => res.json())
.then(response => data = response.data);
const getRandomNumber = () => Math.floor(Math.random() * data.length);
intro.addEventListener('click', () => {
intro.classList.add('hide');
anime.classList.remove('hide');
anime.classList.add('show');
quiz();
});
const getUniqueAnswersNumbers = (count) => {
const answersNumbers = new Set();
while(!answersNumbers.has(counter)) {
answersNumbers.clear();
while (answersNumbers.size < count) answersNumbers.add(getRandomNumber());
}
return [...answersNumbers];
}
function quiz() {
anime_picture.src = data[counter].anime_img;
const answersNumbers = getUniqueAnswersNumbers(answers.length);
answers.forEach((answer, i) => answer.innerHTML = data[answersNumbers[i]].anime_name);
}
answers.forEach(answer => answer.addEventListener('click', userAnswer));
function userAnswer(e) {
const target = e.target.innerHTML;
if(target === data[counter].anime_name) {
console.log('correct');
++score;
} else {
console.log('incorrect');
}
update();
}
function update () {
if((counter + 1) === data.length) return;
++counter;
quiz();
}
body {
position: relative;
display: flex;
justify-content: center;
align-content: center;
}
.intro {
height: 300px;
width: 300px;
border: 1px blue solid;
position: absolute;
left: 25%;
text-align: center;
}
.hide {
visibility: hidden;
}
.show {
visibility: visible;
}
.anime {
height: 800px;
width: 800px;
border: 1px red solid;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.anime_picture {
height: 400px;
width: 400px;
position: absolute;
}
.answers {
height: 100px;
width: 100%;
border: 1px blue solid;
bottom: 0;
position: absolute;
display: flex;
justify-content: space-around;
align-items: center;
}
.answers > div {
height: 80px;
width: auto;
border: 1px black solid;
background-color: #ddd;
}
img {
height: 100%;
width: 100%;
object-fit: cover;
}
header {
width: 100%;
height: 100px;
border: 1px black solid;
position: absolute;
top: 0;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="index.css">
<title>anime page</title>
</head>
<body>
<div class="intro">
welcome to the anime website
</div>
<div class="anime hide">
<div class="anime_picture">
<img src="" alt="">
</div>
<div class="answers">
<div class="answer answer1"></div>
<div class="answer answer2"></div>
<div class="answer answer3"></div>
<div class="answer answer4"></div>
</div>
<header>anime Quiz</header>
</div>
</body>
<script src="index.js" type="text/javascript"></script>
</html>
The function that assembles the randomly chosen question could also remove that question from its array using the splice array method (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)
The removed element could be added to a new array to keep track of used questions.
This deals with preventing repeats both in a set of four and in subsequent sets of four.
In the snippet, I've used an array of numbers in proxy for the questions but the logic is the same.
Note the console logs the question array afer each addition, showing the used elements have been removed.
const questions = [1,2,3,4,5,6,7,8,9,10,11,12];
const usedQuestions = [];
const outputField = document.getElementById("output");
function next4() {
let qPanel = document.createElement('p');
for(let i=0; i<4; i++) {
if (questions.length > 0) {
let index = Math.floor(Math.random()*questions.length);
qPanel.innerHTML = qPanel.innerHTML + `question ${questions[index]}, `;
usedQuestions.push(questions[index]);
questions.splice(index,1);
} // end if array has length;
} // next i question;
outputField.appendChild(qPanel)
console.log(questions)
} // end function next4;
<button onclick="next4()">next set</button>
<div id="output"></output>
The 'full page' link may be needed to see the page and the console.
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;
}
}
I've been struggling with this problem 3 entire days. Any help would be appreciated!
I have a button 'ADD MY PHOTO' and when clicked, it comes a popup with the option to upload a picture or more. So, when I click 'Select Files' button or I drag & drop a picture or more, it will preview the pictures on the right side.
What I need help with is: when I upload a picture or 2, I want on the right side of every picture to display a textarea where the user can write something (like a caption). Also, after the pictures and captures are displayed I need the option to remove one or all of them. Here is a picture:
Here is the CodePen code: https://codepen.io/anon/pen/VEQMwm
Thanks in advance for help.
Also, here is the code:
// ---------- THIS IS FOR THE POPUP ---------- //
function CustomAlert() {
this.performCustomAlert = function (dialog) {
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var dialogoverlay = document.getElementById('dialogoverlay');
var dialogbox = document.getElementById('dialogbox');
dialogoverlay.style.display = "block";
dialogoverlay.style.height = windowHeight + "px";
dialogbox.style.display = "block";
}
this.ok = function () {
document.getElementById('dialogbox').style.display = "none";
document.getElementById('dialogoverlay').style.display = "none";
}
}
var newAlert = new CustomAlert();
// ------------- TABS ----------------- //
function openTab(evt, tabName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " active";
}
document.getElementById("defaultOpen").click();
// ---------------- UPLOAD --------------------------//
// ************************ Drag and drop ***************** //
let dropArea = document.getElementById("drop-area")
// Prevent default drag behaviors
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
document.body.addEventListener(eventName, preventDefaults, false)
})
// Highlight drop area when item is dragged over it
;['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false)
})
;['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false)
})
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false)
function preventDefaults (e) {
e.preventDefault()
e.stopPropagation()
}
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('active')
}
function handleDrop(e) {
var dt = e.dataTransfer
var files = dt.files
handleFiles(files)
}
let uploadProgress = []
let progressBar = document.getElementById('progress-bar')
function initializeProgress(numFiles) {
progressBar.value = 0
uploadProgress = []
for(let i = numFiles; i > 0; i--) {
uploadProgress.push(0)
}
}
function updateProgress(fileNumber, percent) {
uploadProgress[fileNumber] = percent
let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
console.debug('update', fileNumber, percent, total)
progressBar.value = total
}
function handleFiles(files) {
files = [...files]
initializeProgress(files.length)
files.forEach(uploadFile)
files.forEach(previewFile)
}
function previewFile(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function() {
let img = document.createElement('img')
img.src = reader.result
document.getElementById('gallery').appendChild(img)
}
}
function uploadFile(file, i) {
var xhr = new XMLHttpRequest()
var formData = new FormData()
xhr.open('POST', true)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener("progress", function(e) {
updateProgress(i, (e.loaded * 100.0 / e.total) || 100)
})
xhr.addEventListener('readystatechange', function(e) {
if (xhr.readyState == 4 && xhr.status == 200) {
updateProgress(i, 100) // <- Add this
}
else if (xhr.readyState == 4 && xhr.status != 200) {
// Error. Inform the user
}
})
formData.append('upload_preset', 'ujpu6gyk')
formData.append('file', file)
xhr.send(formData)
}
.add-photo{
width: 18%;
background-color: #00a100;
color: #fff;
padding: 11px 13px;
border: 3px solid #00a100;
-webkit-transition: 0.3s ease;
transition: 0.3s ease;
cursor: pointer;
text-align: center;
font-size: 13px;
font-weight: 550;
border-radius: 1px;
margin-left: 41%;
}
* {
box-sizing: border-box;
}
#dialogoverlay {
display: none;
opacity: 0.5;
/*so that user can see through it*/
position: fixed;
top: 0px;
left: 0px;
background: black;
z-index: 10;
height: 100%;
width: 100%;
}
#dialogbox {
display: none;
position: fixed;
background: #ffffff;
border-radius: 1px;
border: 0.5px solid #ccc;
z-index: 10;
left: 25%;
top: 20%;
width: 50%;
height: 400px;
-webkit-animation: fadeEffect 0.3s;
animation: fadeEffect 0.3s;
}
#close-popup {
float: right;
background-color: red;
color: #474747;
font-size: 15px;
}
.header{
position: absolute;
width: 100.2%;
background-color: white;
height: 11%;
top: 5.4%;
left: 50%;
transform: translate(-50%, -50%);
}
.content-centre{
width: 99%;
height: 77%;
margin-left: 3px;
margin-top: 46px;
}
#content-leftside{
width: 65%;
height: 100%;
float: left;
}
.tab {
overflow: hidden;
}
.tab button {
width: 33.3%;
height: 14%;
background-color: #acacac;
float: left;
color: white;
outline: none;
cursor: pointer;
padding: 6px;
transition: 0.3s;
border-right: 1px solid #fff;
}
.tab button:hover {
background-color: #474747;
}
.tab button.active {
background-color: #474747;
}
.tabcontent {
display: none;
padding: 6px 12px;
}
#content-rightside{
width: 35%;
height: 100%;
float: right;
background-color: #ffffff;
border-left: 1px solid #dddddd;
}
#right-topbar{
width: 100%;
height: 9%;
background-color: #474747;
color: #fff;
padding: 5px;
text-align: center;
transition: 0.3s;
}
.footer{
position: absolute;
width: 100.2%;
background-color: #474747;
height: 11%;
bottom: -5.6%;
left: 50%;
/* top: calc(50% - 50px); */
transform: translate(-50%, -50%);
}
/*------------------- UPLOAD AREA -----------------------*/
#drop-area {
border: 2px dashed #ccc;
border-radius: 8px;
width: 98%;
margin: 24px auto;
padding: 15px;
}
#progress-bar{
display: none;
}
#gallery {
margin-top: 5%;
}
#gallery img {
width: 55px;
height: 50px;
margin-bottom: 10px;
margin-left: 10px
}
.button {
display: inline-block;
padding: 10px;
background: #00a100;
color: #fff;
cursor: pointer;
border-radius: 5px;
}
#fileElem {
display: none;
}
#upload-button{
font-size: 40px;
color: #00a100;
<button class="add-photo" onclick="newAlert.performCustomAlert()">ADD MY PHOTO</button>
<div class="popup-upload">
<div id="dialogoverlay"></div>
<!--------------- SELECT MEDIA BOX ---------------->
<div id="dialogbox">
<!--------------- HEADER OF THE BOX ---------------->
<div class="header">
<!--------------- CLOSE POPUP ---------------->
<button id="close-popup" onclick="newAlert.ok()"><i class="fa fa-times" style="margin-top: 8px; margin-right: 7px;"></i></button>
<div class="select-media">
<i class="fa fa-camera" id="select-camera"></i>
<h2 id="select-media">SELECT YOUR MEDIA</h2>
</div>
</div>
<!--------------- CONTENT OF THE BOX ---------------->
<div class="content-centre">
<!--------------- LEFT CONTENT ---------------->
<div id="content-leftside">
<div class="tab">
<button class="tablinks" id="defaultOpen" onclick="openTab(event, 'Desktop')"><span class="fa fa-desktop"></span> Desktop</button>
<button class="tablinks" onclick="openTab(event, 'Facebook')"><span class="fa fa-facebook"></span> Facebook</button>
<button class="tablinks" onclick="openTab(event, 'Instagram')"><span class="fa fa-instagram"></span> Instagram</button>
</div>
<div id="Desktop" class="tabcontent">
<div id="drop-area">
<form class="my-form">
<span class="fa fa-cloud-upload" id="upload-button"></span>
<p id="drag-text">Drag & Drop Your <br> Photos or Videos <br> To Upload</p>
<input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
<label class="button" for="fileElem">or Select Files</label>
</form>
</div>
</div>
<div id="Facebook" class="tabcontent">
<h3>Facebook</h3>
</div>
<div id="Instagram" class="tabcontent">
<h3>Instagram</h3>
</div>
</div>
<!--------------- RIGHT CONTENT ---------------->
<div id="content-rightside">
<!--------------- RIGHT TOPBAR ---------------->
<div id="right-topbar">
<h1>Selected Media</h1>
</div>
<progress id="progress-bar" max=100 value=0></progress>
<div id="gallery"/></div>
</div>
</div>
<div class="footer">
</div>
</div>
</div>
</div>
Look into below code, I made some changes on previewFile() function.
I hope, by looking below code you can get idea.
// ---------- THIS IS FOR THE POPUP ---------- //
function CustomAlert() {
this.performCustomAlert = function(dialog) {
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
var dialogoverlay = document.getElementById('dialogoverlay');
var dialogbox = document.getElementById('dialogbox');
dialogoverlay.style.display = "block";
dialogoverlay.style.height = windowHeight + "px";
dialogbox.style.display = "block";
}
this.ok = function() {
document.getElementById('dialogbox').style.display = "none";
document.getElementById('dialogoverlay').style.display = "none";
}
}
var newAlert = new CustomAlert();
// ------------- TABS ----------------- //
function openTab(evt, tabName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " active";
}
document.getElementById("defaultOpen").click();
// ---------------- UPLOAD --------------------------//
// ************************ Drag and drop ***************** //
let dropArea = document.getElementById("drop-area")
// Prevent default drag behaviors
;
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
document.body.addEventListener(eventName, preventDefaults, false)
})
// Highlight drop area when item is dragged over it
;
['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false)
})
;
['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false)
})
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false)
function preventDefaults(e) {
e.preventDefault()
e.stopPropagation()
}
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('active')
}
function handleDrop(e) {
var dt = e.dataTransfer
var files = dt.files
handleFiles(files)
}
let uploadProgress = []
let progressBar = document.getElementById('progress-bar')
function initializeProgress(numFiles) {
progressBar.value = 0
uploadProgress = []
for (let i = numFiles; i > 0; i--) {
uploadProgress.push(0)
}
}
function updateProgress(fileNumber, percent) {
uploadProgress[fileNumber] = percent
let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
console.debug('update', fileNumber, percent, total)
progressBar.value = total
}
function handleFiles(files) {
files = [...files]
initializeProgress(files.length)
files.forEach(uploadFile)
files.forEach(previewFile)
}
function previewFile(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function() {
let img = document.createElement('img')
img.src = reader.result
var mainDiv = document.createElement("div")
mainDiv.className = "box"
mainDiv.appendChild(img)
var textbx = document.createElement("textarea")
textbx.placeholder ="Caption..."
mainDiv.appendChild(textbx)
var btn = document.createElement("button")
var tx = document.createTextNode("X");
btn.onclick = function() {
$(this).closest(".box").remove()
}
btn.appendChild(tx);
mainDiv.appendChild(btn)
document.getElementById('gallery').appendChild(mainDiv)
}
}
function uploadFile(file, i) {
var xhr = new XMLHttpRequest()
var formData = new FormData()
xhr.open('POST', true)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener("progress", function(e) {
updateProgress(i, (e.loaded * 100.0 / e.total) || 100)
})
xhr.addEventListener('readystatechange', function(e) {
if (xhr.readyState == 4 && xhr.status == 200) {
updateProgress(i, 100) // <- Add this
} else if (xhr.readyState == 4 && xhr.status != 200) {
// Error. Inform the user
}
})
formData.append('upload_preset', 'ujpu6gyk')
formData.append('file', file)
xhr.send(formData)
}
.add-photo {
width: 18%;
background-color: #00a100;
color: #fff;
padding: 11px 13px;
border: 3px solid #00a100;
-webkit-transition: 0.3s ease;
transition: 0.3s ease;
cursor: pointer;
text-align: center;
font-size: 13px;
font-weight: 550;
border-radius: 1px;
margin-left: 41%;
}
* {
box-sizing: border-box;
}
#dialogoverlay {
display: none;
opacity: 0.5;
/*so that user can see through it*/
position: fixed;
top: 0px;
left: 0px;
background: black;
z-index: 10;
height: 100%;
width: 100%;
}
#dialogbox {
display: none;
position: fixed;
background: #ffffff;
border-radius: 1px;
border: 0.5px solid #ccc;
z-index: 10;
left: 25%;
top: 20%;
width: 50%;
height: 400px;
-webkit-animation: fadeEffect 0.3s;
animation: fadeEffect 0.3s;
}
#close-popup {
float: right;
background-color: red;
color: #474747;
font-size: 15px;
}
.header {
position: absolute;
width: 100.2%;
background-color: white;
height: 11%;
top: 5.4%;
left: 50%;
transform: translate(-50%, -50%);
}
.content-centre {
width: 99%;
height: 77%;
margin-left: 3px;
margin-top: 46px;
}
#content-leftside {
width: 65%;
height: 100%;
float: left;
}
.tab {
overflow: hidden;
}
.tab button {
width: 33.3%;
height: 14%;
background-color: #acacac;
float: left;
color: white;
outline: none;
cursor: pointer;
padding: 6px;
transition: 0.3s;
border-right: 1px solid #fff;
}
.tab button:hover {
background-color: #474747;
}
.tab button.active {
background-color: #474747;
}
.tabcontent {
display: none;
padding: 6px 12px;
}
#content-rightside {
width: 35%;
height: 100%;
float: right;
background-color: #ffffff;
border-left: 1px solid #dddddd;
}
#right-topbar {
width: 100%;
height: 9%;
background-color: #474747;
color: #fff;
padding: 5px;
text-align: center;
transition: 0.3s;
}
.footer {
position: absolute;
width: 100.2%;
background-color: #474747;
height: 11%;
bottom: -5.6%;
left: 50%;
/* top: calc(50% - 50px); */
transform: translate(-50%, -50%);
}
/*------------------- UPLOAD AREA -----------------------*/
#drop-area {
border: 2px dashed #ccc;
border-radius: 8px;
width: 98%;
margin: 24px auto;
padding: 15px;
}
#progress-bar {
display: none;
}
#gallery {
margin-top: 5%;
}
#gallery img {
width: 55px;
height: 50px;
margin-bottom: 10px;
margin-left: 10px
}
.button {
display: inline-block;
padding: 10px;
background: #00a100;
color: #fff;
cursor: pointer;
border-radius: 5px;
}
#fileElem {
display: none;
}
#upload-button {
font-size: 40px;
color: #00a100;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="add-photo" onclick="newAlert.performCustomAlert()">ADD MY PHOTO</button>
<div class="popup-upload">
<div id="dialogoverlay"></div>
<!--------------- SELECT MEDIA BOX ---------------->
<div id="dialogbox">
<!--------------- HEADER OF THE BOX ---------------->
<div class="header">
<!--------------- CLOSE POPUP ---------------->
<button id="close-popup" onclick="newAlert.ok()"><i class="fa fa-times" style="margin-top: 8px; margin-right: 7px;"></i></button>
<div class="select-media">
<i class="fa fa-camera" id="select-camera"></i>
<h2 id="select-media">SELECT YOUR MEDIA</h2>
</div>
</div>
<!--------------- CONTENT OF THE BOX ---------------->
<div class="content-centre">
<!--------------- LEFT CONTENT ---------------->
<div id="content-leftside">
<div class="tab">
<button class="tablinks" id="defaultOpen" onclick="openTab(event, 'Desktop')"><span class="fa fa-desktop"></span> Desktop</button>
<button class="tablinks" onclick="openTab(event, 'Facebook')"><span class="fa fa-facebook"></span> Facebook</button>
<button class="tablinks" onclick="openTab(event, 'Instagram')"><span class="fa fa-instagram"></span> Instagram</button>
</div>
<div id="Desktop" class="tabcontent">
<div id="drop-area">
<form class="my-form">
<span class="fa fa-cloud-upload" id="upload-button"></span>
<p id="drag-text">Drag & Drop Your <br> Photos or Videos <br> To Upload</p>
<input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
<label class="button" for="fileElem">or Select Files</label>
</form>
</div>
</div>
<div id="Facebook" class="tabcontent">
<h3>Facebook</h3>
</div>
<div id="Instagram" class="tabcontent">
<h3>Instagram</h3>
</div>
</div>
<!--------------- RIGHT CONTENT ---------------->
<div id="content-rightside">
<!--------------- RIGHT TOPBAR ---------------->
<div id="right-topbar">
<h1>Selected Media</h1>
</div>
<progress id="progress-bar" max=100 value=0></progress>
<div id="gallery" /></div>
</div>
</div>
<div class="footer">
</div>
</div>
</div>
</div>
just replace previewFile function with this.
function previewFile(file) {
let reader = new FileReader()
reader.readAsDataURL(file);
reader.onloadend = function() {
var gallleryDiv=document.getElementById('gallery');
var wrapperDiv = document.createElement("div");
let img = document.createElement('img');
img.src = reader.result;
wrapperDiv.className = "wrapperDiv";
wrapperDiv.appendChild(img)
var textbx = document.createElement("textarea");
wrapperDiv.appendChild(textbx);
var btn = document.createElement("button");
var tx = document.createTextNode("X");
btn.onclick = function() {$(this).closest(".wrapperDiv").remove()}
btn.appendChild(tx);
wrapperDiv.appendChild(btn);
gallleryDiv.appendChild(wrapperDiv);
}
}