let div = document.createElement('div');
let ul = document.createElement('ul');
const data = {};
const el = document.getElementById("name");
sortName = 0;
sortCapital = 0;
sortPopulation = 0;
sortArea = 0;
function sortByArea(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortArea == 0){
data.sort((a, b) => {
if (a.area > b.area) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.area < b.area) return 1;
else return -1
});
return data;
}
function sortByPopulation(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortPopulation == 0){
data.sort((a, b) => {
if (a.population > b.population) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.population < b.population) return 1;
else return -1
});
return data;
}
function sortByCapital(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortCapital == 0){
data.sort((a, b) => {
if (a.capital > b.capital) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.capital < b.capital) return 1;
else return -1
});
return data;
}
function sortByName(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortName == 0){
data.sort((a, b) => {
if (a.name > b.name) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.name < b.name) return 1;
else return -1
});
return data;
}
document.getElementById('area').addEventListener('click', () => f(4));
document.getElementById('population').addEventListener('click', () => f(3));
document.getElementById('capital').addEventListener('click', () => f(2));
document.getElementById('name').addEventListener('click', () => f(1));
div.appendChild(ul);
async function f(e) {
//fetching and sorting data by regions and subregions
const res = await fetch("https://restcountries.com/v3.1/all");
var data = await res.json();
const container = document.getElementById('container');
const accordion = document.createElement('div');
const olWrapper = document.getElementById('listWrapper');
const subRegionWrapper = document.getElementById('subRegionWrapper');
switch (e) {
case 1:
data = sortByName(data);
sortName += 1;
sortName %= 2;
case 2:
data = sortByCapital(data);
sortCapital += 1;
sortCapital %= 2;
case 3:
data = sortByPopulation(data);
sortPopulation += 1;
sortPopulation %= 2;
case 4:
data = sortByArea(data);
sortArea += 1;
sortArea %= 2;
}
data.sort((a, b) => {
if (a.region > b.region) return 1;
else if (a.region < b.region) return -1
else {
if (a.subregion > b.subregion) return 1;
else return -1;
}
});
const subRegions = data.reduce((r, a) => {
r[a.subregion] = r[a.subregion] || [];
r[a.subregion].push(a);
return r;
}, {});
const dropdownValues = Object.entries(subRegions);
dropdownValues.forEach(subRegion => {
const accordionWrapper = document.createElement('div');
const panel = document.createElement('div');
panel.classList.add('panel');
accordionWrapper.classList.add('accordion');
const totalArea = subRegion[1].reduce((acc, curr) => acc + curr.area, 0);
const totalPopulation = subRegion[1].reduce((acc, curr) => acc + curr.population, 0);
const li = createSubregion(subRegion[0], totalPopulation, totalArea);
accordionWrapper.appendChild(li);
accordion.appendChild(accordionWrapper);
subRegion[1].forEach(item => {
const subLi = createCountry(item.name.common, item.capital, item.area, item.population);
const subOl = document.createElement('ol');
subOl.appendChild(subLi);
panel.appendChild(subOl);
accordion.appendChild(panel);
});
accordionWrapper.addEventListener('click', function () {
this.classList.toggle("active");
const panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
});
container.appendChild(accordion);
}
function createSubregion(name, population, area) {
var li = document.createElement("li");
li.setAttribute("class", "subregion");
var header = document.createElement("div");
header.setAttribute("class", "subregion-header disp-flex");
var nameDiv = document.createElement("div");
var nameh2 = document.createElement("h2");
nameh2.innerText = name;
nameDiv.appendChild(nameh2);
header.append(nameDiv);
var emptyDiv = document.createElement("div");
header.appendChild(emptyDiv);
var populationDiv = document.createElement("div");
var populationh2 = document.createElement("h3");
populationh2.innerText = population;
populationDiv.appendChild(populationh2);
header.append(populationDiv);
var areaDiv = document.createElement("div");
var areah2 = document.createElement("h3");
areah2.innerText = area;
areaDiv.appendChild(areah2);
header.append(areaDiv);
li.appendChild(header);
return li;
}
function createCountry(name, capital, area, population) {
var country = document.createElement("li");
country.setAttribute("class", "country disp-flex")
var namediv = document.createElement("div");
var nameh4 = document.createElement("h4");
nameh4.innerText = name;
namediv.appendChild(nameh4);
country.appendChild(namediv);
var capitaldiv = document.createElement("div");
var capitalh4 = document.createElement("h4");
capitalh4.innerText = capital;
capitaldiv.appendChild(capitalh4);
country.appendChild(capitaldiv);
var popdiv = document.createElement("div");
var poph4 = document.createElement("h4");
poph4.innerText = population;
popdiv.appendChild(poph4);
country.appendChild(popdiv);
var areadiv = document.createElement("div");
var areah4 = document.createElement("h4");
areah4.innerText = area;
areadiv.appendChild(areah4);
country.appendChild(areadiv);
return country;
}
f(0);
*{
transition: 500ms;
}
body {
margin: 0 15%;
min-height: 100vh;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: aliceblue;
font-family: 'Open Sans', Arial;
font-size: 18px;
}
#name {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
#capital {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
#population {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
#area {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
header {
margin: 0 10%;
display: flex;
justify-content: space-between;
padding: 22px 0;
color: rgb(5, 5, 5);
}
ul {
list-style: none;
list-style-type: none;
outline: 2px solid #ddd;
padding: 1rem 2rem;
border-radius: 0.5rem;
list-style-position: inside;
color: blue;
}
ul ol {
color: rgb(197, 105, 18);
list-style: none;
list-style-type: none;
font-size: .9em;
margin: 0.4rem 0;
}
.country {
display: flex;
justify-content: space-between;
}
.disp-flex {
display: flex;
justify-content: space-between;
}
.disp-flex>div {
width: 23%;
padding: 15px 0px;
}
.subregion-header>div:nth-child(1) {
position: relative;
left: 30px;
}
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
margin: 15px 2px;
}
.accordion li {
list-style-type: none;
}
.active,
.accordion:hover {
background-color: #ccc;
}
.panel {
margin-left: 5%;
display: none;
background-color: white;
overflow: hidden;
}
#name:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
#capital:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
#population:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
#area:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="container">
<header>
<div id="name">
<h1>Name</h1>
</div>
<div id="capital">
<h1>Capital</h1>
</div>
<div id="population">
<h1>Population</h1>
</div>
<div id="area">
<h1>Area</h1>
</div>
</header>
<div id="container"></div>
<div id="subRegionWrapper"> </div>
<div id="listWrapper"></div>
<script src="script.js"></script>
</main>
</body>
</html>
I made a little data-list containing some information about countries (names, capitals, areas and population), all the countries are grouped by sub-regions. In the header section I have four positions describing each column (name, capital, area and population). I want to add functionality of sorting, so that after clicking on position in the header section, the data would sort by that position. For example after clicking once on name, data should sort by name(asc or desc) and then after clicking once more on name, data should sort inversely to previous sort. I would like to allow sorting by couple columns at same time, so for example after clicking on capital and area, the data should be sorted by capitals and area. Here is my code:
I tried to do that sorting in different ways (adding extra function, passing parameter to main function etc.) but unfortunately nothing worked.I am really struggling with that, I will be extremely grateful for any help.
Your code is actually working, but you are not removing the old table. So the new sorted entries are appended to the already existing ones. You should clear the table some way, for example:
const container = document.getElementById('container');
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
Put this where you get the container in your function f(e) and it should be working
Related
I want to change the paragraph text when the next arrow is clicked. I can change it once but if I want to change it to a third option, it's not working. Can someone explain why this is the case?
I have made a Codepen with the issue: Conditional statement problem
<div class="tutNavigation">
<div class="flexNavigation">
<div id="back"><i id="arrow-left"><-</i></div>
<div class="tutorialText">
<p></p>
</div>
<div id="next"><i id="arrow-right">-></i></div>
</div>
</div>
let text = document.querySelector("p");
text.textContent = "text1";
let backLeft = document.getElementById("back");
let arrowBack = document.getElementById("arrow-left");
let nextRight = document.getElementById("next");
let arrowNext = document.getElementById("arrow-right");
if ((text.textContent = "text1")) {
arrowBack.classList.add("hidden");
nextRight.addEventListener("click", () => {
arrowBack.classList.remove("hidden");
text.textContent = "text2";
});
backLeft.addEventListener("click", () => {
arrowBack.classList.add("hidden");
text.textContent = "text1";
});
} else if ((text.textcontent = "text2")) {
nextRight.addEventListener("click", () => {
text.textContent = "text3";
});
backLeft.addEventListener("click", () => {
text.textContent = "text2";
});
} else {
text.textContent = "none";
}
Conditions need to be added in the event listener and based on the conditions, hide or show elements
let text = document.querySelector("p");
text.textContent = "text1";
let backLeft = document.getElementById("back");
let arrowBack = document.getElementById("arrow-left");
let nextRight = document.getElementById("next");
let arrowNext = document.getElementById("arrow-right");
arrowBack.classList.add("hidden");
nextRight.addEventListener("click", () => {
arrowBack.classList.remove("hidden");
if (text.textContent === "text1") {
text.textContent = "text2";
} else if (text.textContent === "text2") {
text.textContent = "text3";
}
});
backLeft.addEventListener("click", () => {
if (text.textContent === "text3") {
text.textContent = "text2";
} else if (text.textContent === "text2") {
text.textContent = "text1";
arrowBack.classList.add("hidden");
}
});
body {
background-color: black;
}
.tutNavigation {
display: flex;
padding: 3rem 2rem;
box-shadow: 0px -4px 4px rgba(0, 0, 0, 0.25);
flex-direction: column;
flex-wrap: nowrap;
align-content: center;
gap: 1rem;
}
.tutNavigation.flexNavigation {
display: flex;
align-items: center;
justify-content: space-evenly;
flex-wrap: nowrap;
gap: 1rem;
flex-direction: row;
}
.flexNavigation.tutorialText {
text-align: center;
max-width: 500px;
}
p {
color: white;
font-size: 16px;
line-height: 1.3rem;
}
#back,
#next {
padding: 1rem;
cursor: pointer;
border-radius: 5px;
width: 72px;
}
i {
font-size: 40px;
color: white;
}
.hidden {
display: none;
}
<div class="tutNavigation">
<div class="flexNavigation">
<div id="back"><i id="arrow-left"><-</i></div>
<div class="tutorialText">
<p></p>
</div>
<div id="next"><i id="arrow-right">-></i></div>
</div>
</div>
You should pull out the eventListerens form the conditions!
Beacouse you want to do something if you clicking on the arrows!
And inside the eventListeners you should write the logic.
const text = document.querySelector("p");
const possiblities = ["text1", "text2", "text3", "text4"];
let index = 0;
let backLeft = document.getElementById("back");
let arrowBack = document.getElementById("arrow-left");
let nextRight = document.getElementById("next");
let arrowNext = document.getElementById("arrow-right");
const checkAndHideArrows = () => {
if (index >= possiblities.length - 1) {
nextRight.classList.add("hidden");
arrowNext.classList.add("hidden");
console.log("hide right");
} else {
nextRight.classList.remove("hidden")
arrowNext.classList.remove("hidden")
console.log("show right");
}
if (index <= 0) {
backLeft.classList.add("hidden");
arrowBack.classList.add("hidden");
console.log("hide left");
} else {
backLeft.classList.remove("hidden");
arrowBack.classList.remove("hidden");
console.log("show left");
}
}
const setText = () => {
text.textContent = possiblities[index];
console.log(possiblities[index]);
checkAndHideArrows();
};
const next = () => {
if (index++ >= possiblities.length - 1) {
index = possiblities.length - 1;
}
};
const prev = () => {
if (index-- <= 0) {
index = 0;
}
};
setText();
nextRight.addEventListener("click", () => {
next();
console.log(index);
setText();
});
backLeft.addEventListener("click", () => {
prev();
console.log(index);
setText();
});
.hidden {
color: transparent;
}
<div class="tutNavigation">
<div class="flexNavigation">
<div id="back"><i id="arrow-left"><-</i></div>
<div class="tutorialText">
<p></p>
</div>
<div id="next"><i id="arrow-right">-></i></div>
</div>
</div>
I have a little data list containing information about countries which are grouped by subregions. I want to add functionality of filtering data (only capitals and names of countries) by expression typed by user. For example if user types 'ab' in name filter, only countries containing 'ab' in their names should show up.
let div = document.createElement('div');
let ul = document.createElement('ul');
const data = {};
const el = document.getElementById("name");
sortName = 0;
sortCapital = 0;
sortPopulation = 0;
sortArea = 0;
function sortByArea(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortArea == 0){
data.sort((a, b) => {
if (a.area > b.area) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.area < b.area) return 1;
else return -1
});
return data;
}
function sortByPopulation(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortPopulation == 0){
data.sort((a, b) => {
if (a.population > b.population) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.population < b.population) return 1;
else return -1
});
return data;
}
function sortByCapital(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortCapital == 0){
data.sort((a, b) => {
if (a.capital > b.capital) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.capital < b.capital) return 1;
else return -1
});
return data;
}
function sortByName(data) {
var child = container.lastElementChild;
while (child) {
container.removeChild(child);
child = container.lastElementChild;
}
if(sortName == 0){
data.sort((a, b) => {
if (a.name > b.name) return 1;
else return -1
});
return data;
}
data.sort((a, b) => {
if (a.name < b.name) return 1;
else return -1
});
return data;
}
document.getElementById('area').addEventListener('click', () => f(4));
document.getElementById('population').addEventListener('click', () => f(3));
document.getElementById('capital').addEventListener('click', () => f(2));
document.getElementById('name').addEventListener('click', () => f(1));
div.appendChild(ul);
async function f(e) {
//fetching and sorting data by regions and subregions
const res = await fetch("https://restcountries.com/v3.1/all");
var data = await res.json();
const container = document.getElementById('container');
const accordion = document.createElement('div');
const olWrapper = document.getElementById('listWrapper');
const subRegionWrapper = document.getElementById('subRegionWrapper');
switch (e) {
case 1:
data = sortByName(data);
sortName += 1;
sortName %= 2;
case 2:
data = sortByCapital(data);
sortCapital += 1;
sortCapital %= 2;
case 3:
data = sortByPopulation(data);
sortPopulation += 1;
sortPopulation %= 2;
case 4:
data = sortByArea(data);
sortArea += 1;
sortArea %= 2;
}
data.sort((a, b) => {
if (a.region > b.region) return 1;
else if (a.region < b.region) return -1
else {
if (a.subregion > b.subregion) return 1;
else return -1;
}
});
const subRegions = data.reduce((r, a) => {
r[a.subregion] = r[a.subregion] || [];
r[a.subregion].push(a);
return r;
}, {});
const dropdownValues = Object.entries(subRegions);
dropdownValues.forEach(subRegion => {
const accordionWrapper = document.createElement('div');
const panel = document.createElement('div');
panel.classList.add('panel');
accordionWrapper.classList.add('accordion');
const totalArea = subRegion[1].reduce((acc, curr) => acc + curr.area, 0);
const totalPopulation = subRegion[1].reduce((acc, curr) => acc + curr.population, 0);
const li = createSubregion(subRegion[0], totalPopulation, totalArea);
accordionWrapper.appendChild(li);
accordion.appendChild(accordionWrapper);
subRegion[1].forEach(item => {
const subLi = createCountry(item.name.common, item.capital, item.area, item.population);
const subOl = document.createElement('ol');
subOl.appendChild(subLi);
panel.appendChild(subOl);
accordion.appendChild(panel);
});
accordionWrapper.addEventListener('click', function () {
this.classList.toggle("active");
const panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
});
container.appendChild(accordion);
}
function createSubregion(name, population, area) {
var li = document.createElement("li");
li.setAttribute("class", "subregion");
var header = document.createElement("div");
header.setAttribute("class", "subregion-header disp-flex");
var nameDiv = document.createElement("div");
var nameh2 = document.createElement("h2");
nameh2.innerText = name;
nameDiv.appendChild(nameh2);
header.append(nameDiv);
var emptyDiv = document.createElement("div");
header.appendChild(emptyDiv);
var populationDiv = document.createElement("div");
var populationh2 = document.createElement("h3");
populationh2.innerText = population;
populationDiv.appendChild(populationh2);
header.append(populationDiv);
var areaDiv = document.createElement("div");
var areah2 = document.createElement("h3");
areah2.innerText = area;
areaDiv.appendChild(areah2);
header.append(areaDiv);
li.appendChild(header);
return li;
}
function createCountry(name, capital, area, population) {
var country = document.createElement("li");
country.setAttribute("class", "country disp-flex")
var namediv = document.createElement("div");
var nameh4 = document.createElement("h4");
nameh4.innerText = name;
namediv.appendChild(nameh4);
country.appendChild(namediv);
var capitaldiv = document.createElement("div");
var capitalh4 = document.createElement("h4");
capitalh4.innerText = capital;
capitaldiv.appendChild(capitalh4);
country.appendChild(capitaldiv);
var popdiv = document.createElement("div");
var poph4 = document.createElement("h4");
poph4.innerText = population;
popdiv.appendChild(poph4);
country.appendChild(popdiv);
var areadiv = document.createElement("div");
var areah4 = document.createElement("h4");
areah4.innerText = area;
areadiv.appendChild(areah4);
country.appendChild(areadiv);
return country;
}
f(0);
*{
transition: 500ms;
}
body {
margin: 0 15%;
min-height: 100vh;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: aliceblue;
font-family: 'Open Sans', Arial;
font-size: 18px;
}
#name {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
#capital {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
#population {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
#area {
padding: 0 20px;
background-color: rgb(219, 219, 219);
}
header {
margin: 0 10%;
display: flex;
justify-content: space-between;
padding: 22px 0;
color: rgb(5, 5, 5);
}
ul {
list-style: none;
list-style-type: none;
outline: 2px solid #ddd;
padding: 1rem 2rem;
border-radius: 0.5rem;
list-style-position: inside;
color: blue;
}
ul ol {
color: rgb(197, 105, 18);
list-style: none;
list-style-type: none;
font-size: .9em;
margin: 0.4rem 0;
}
.country {
display: flex;
justify-content: space-between;
}
.disp-flex {
display: flex;
justify-content: space-between;
}
.disp-flex>div {
width: 23%;
padding: 15px 0px;
}
.subregion-header>div:nth-child(1) {
position: relative;
left: 30px;
}
.accordion {
background-color: #eee;
color: #444;
cursor: pointer;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
margin: 15px 2px;
}
.accordion li {
list-style-type: none;
}
.active,
.accordion:hover {
background-color: #ccc;
}
.panel {
margin-left: 5%;
display: none;
background-color: white;
overflow: hidden;
}
#name:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
#capital:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
#population:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
#area:hover {
background-color: rgb(114, 114, 114);
cursor: pointer;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="container">
<header>
<div id="name">
<h1>Name</h1>
</div>
<div id="capital">
<h1>Capital</h1>
</div>
<div id="population">
<h1>Population</h1>
</div>
<div id="area">
<h1>Area</h1>
</div>
</header>
<div id="container"></div>
<div id="subRegionWrapper"> </div>
<div id="listWrapper"></div>
<script src="script.js"></script>
</main>
</body>
</html>
How can I filter data in such way?
I have made a tic tac toe using JS however it has some problems My code is:
let x = [];
let o = [];
let xTurn = false;
let winPat = [
['00', '10', '20'],
['01', '11', '21'],
['02', '12', '22'],
['00', '01', '02'],
['10', '11', '12'],
['20', '21', '22'],
['00', '11', '22'],
['02', '11', '20']
];
const tableR = document.getElementsByClassName('tableR');
const button = document.getElementById('btn')
for (let i = 0; i <= 2; i++) {
for (let j = 0; j <= 2; j++) {
let newElement = document.createElement('td');
tableR.item(i).appendChild(newElement);
newElement.addEventListener('click', () => {
if (elementIsEmpty(newElement)) {
const img = document.createElement('img');
if (xTurn) {
img.src = 'https://www.google.com/url?sa=i&url=https%3A%2F%2Fcommons.wikimedia.org%2Fwiki%2FFile%3ARed_X.svg&psig=AOvVaw1B-fppvlN2x5oSCGllnXGc&ust=1634565535566000&source=images&cd=vfe&ved=0CAkQjRxqFwoTCMjbg6XN0fMCFQAAAAAdAAAAABAD';
x.push(String(i) + String(j))
} else {
img.src = 'https://www.google.com/imgres?imgurl=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2Fd%2Fd0%2FLetter_o.svg%2F407px-Letter_o.svg.png&imgrefurl=https%3A%2F%2Fcommons.wikimedia.org%2Fwiki%2FFile%3ALetter_o.svg&tbnid=p-B77Lz3DDttwM&vet=12ahUKEwizlYTWzdHzAhXMg9gFHTSaBJkQMygAegUIARDaAQ..i&docid=K2HPZsIMOu4d5M&w=407&h=768&q=pixture%20of%20o&ved=2ahUKEwizlYTWzdHzAhXMg9gFHTSaBJkQMygAegUIARDaAQ';
o.push(String(i) + String(j))
}
newElement.append(img)
checkWinOrDraw(xTurn)
xTurn = !xTurn
}
})
}
}
const td = document.getElementsByTagName('td')
button.addEventListener('click', () => {
reset();
});
function elementIsEmpty(el) {
return (/^(\s| )*$/.test(el.innerHTML));
}
function checkWinOrDraw(xTurn) {
for (let i = 0; i < winPat.length; i++) {
if (xTurn) {
if (winPat[i].every(element => x.includes(element))) {
alert('X wins')
reset()
return
}
} else {
if (winPat[i].every(element => o.includes(element))) {
alert('O wins')
reset()
return
}
}
}
for (let item of td) {
if (elementIsEmpty(item))
return
}
alert('Draw')
reset()
}
function reset() {
x = [];
o = [];
for (let item of td) {
item.textContent = ''
}
}
body {
margin: 0px;
background-color: #3c4552;
color: aliceblue;
text-align: center;
}
header {
height: 75px;
background-color: #1f1e1c;
padding: 20px;
font-size: large;
}
table {
border-collapse: collapse;
margin: 40px auto;
}
td {
border: 7px solid black;
height: 121px;
width: 121px;
cursor: pointer;
}
button {
background-color: #1f1e1c;
color: white;
width: 25%;
height: 50px;
font-size: larger;
border: black solid 2px;
border-radius: 7px;
cursor: pointer;
}
img {
display: block;
width: 100%;
height: 100%;
}
<header>
<h1>TicTacToe</h1>
</header>
<main>
<table>
<tr class="tableR"></tr>
<tr class="tableR"></tr>
<tr class="tableR"></tr>
</table>
</main>
<footer>
<button id="btn">RESET</button>
</footer>
my problem is that when the game ends, say X wins, the image is not rendered on the screen, and alert() is called. I googled a lot and found no solutions.
details and clarity:
the image is not rendered on screen at the end of the game. try on your computer and see. alert() is called before the image is present in-game which results in a bad user experience. hence I need a solution.
Here your append() function is not able to complete image load & your checkWinOrDraw() function got called that's why this is happening.
Please try with the below code i am calling the checkWinOrDraw() once image gets loaded or failed.
for (let i = 0; i <= 2; i++) {
for (let j = 0; j <= 2; j++) {
let newElement = document.createElement('td');
tableR.item(i).appendChild(newElement);
newElement.addEventListener('click', () => {
if (elementIsEmpty(newElement)) {
const img = document.createElement('img');
if (xTurn) {
img.src = 'https://dummyimage.com/20x20/3a4ca6/fff';
x.push(String(i) + String(j))
} else {
img.src = 'https://dummyimage.com/30x30/fff/fff';
o.push(String(i) + String(j))
}
console.log(xTurn)
xTurn = !xTurn
img.addEventListener('load', checkWinOrDraw)
img.addEventListener('error', checkWinOrDraw)
newElement.appendChild(img)
}
})
}
}
function checkWinOrDraw(e,xTurn) {
//console.log('checking',xTurn)
for (let i = 0; i < winPat.length; i++) {
if (xTurn) {
if (winPat[i].every(element => x.includes(element))) {
alert('X wins')
reset()
return
}
} else {
if (winPat[i].every(element => o.includes(element))) {
alert('O wins')
reset()
return
}
}
}
for (let item of td) {
if (elementIsEmpty(item))
return
}
alert('Draw')
reset()
}
i'm new on Javascript and i'm trying to create a web component, precisely some kind of list view. The goal would be it can expand and collapse when I click an item, and I'd like it to do with transitions.
I already put the transition on the template, but for some reason, it doesn't work, just grow or collapse instantly without the animation.
Rarely, if I do a const listView = document.querySelector(".list-view") and then listView.collapse() it works.
The summary code is:
class ListItem extends HTMLElement {
constructor(items = []) {
super();
//Template and its variables
const listItemTemplate = document.createElement("template");
listItemTemplate.innerHTML = `
<style>
.listitem{
//some other properties
transition: height 0.3s cubic-bezier(0.65, 0, 0.35, 1);
height: ${this.initialHeight};
}
//Other styles...
</style>
changedItem(itemSelected){
//More stuff
this.refreshDOMItems();
this.collsapse();
}
`;
expand() {
let height = 10;
Array.from(this.itemsHTML).forEach((item) => {
height += item.clientHeight;
});
this.listItem.style.height = height / 16 + "rem";
}
collapse() {
this.listItem.style.height = this.initialHeight;
}
Edit: Here is a Codepen.
https://codepen.io/salvabarg/pen/zYqJNMj
What i am doing wrong? Hope the code is not much caotic. Thank you in advance!
You are overriding the height transition with transition: background .5s ease; on .listitem:hover, so removing this line solves the problem:
.listitem:hover{
/*transition: background .5s ease;*/
background: rgba(255,255,255,.65);
}
class ListItem extends HTMLElement {
constructor(items = []) {
super();
//Template and its variables
this.initialHeight = "3.75rem";
this.initialBorderRadius = stringToNumber(this.initialHeight) / 2 + "rem";
const listItemTemplate = document.createElement("template");
listItemTemplate.innerHTML = `
<style>
ul{
box-sizing: border-box;
margin: 0px;
padding: 0px;
list-style: none;
}
.ul{
display: flex;
align-items: center;
flex-direction: column;
height: 3.75rem;
}
.listitem{
font-family: 'Nunito', sans-serif;
display: inline-block;
overflow: hidden;
box-sizing: border-box;
background-color: white;
border-radius: ${this.initialBorderRadius};
padding: 0px 1rem;
cursor: pointer;
box-shadow: 0px 0px 1.25rem rgba(4,25,106,.14);
transition: height 0.3s cubic-bezier(0.65, 0, 0.35, 1);
height: ${this.initialHeight};
}
li.item{
box-sizing: border-box;
min-height: ${this.initialHeight};
display: flex;
align-items: center;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
.img{
width: 2rem;
margin-right: 0.625rem;
border-radius: 50%;
}
.name{
font-size: 1.125rem;
font-weight: 700;
color: var(--autores);
margin-right: 0.6rem;
}
.listitem:hover{
/*transition: background .5s ease;*/
background: rgba(255,255,255,.65);
}</style>
<div class="listitem">
<ul class="ul">
</ul>
</div>
`;
//Constructor
this.attachShadow({
mode: "open"
});
this.shadowRoot.appendChild(listItemTemplate.content.cloneNode(true));
this.items = items;
this.listItem = this.shadowRoot.querySelector(".listitem");
this.itemsHTML = this.shadowRoot
.querySelector(".listitem")
.querySelector(".ul").children;
const ul = this.shadowRoot.querySelector(".listitem").querySelector(".ul");
this.ul = ul;
}
connectedCallback() {
//Do
//Carga de items por defecto
const item = {
name: "Item",
avatar: "images/users.svg",
selected: false
};
const item_two = {
name: "Item 2",
avatar: "images/users.svg",
selected: false
};
const item_three = {
name: "Item 3",
avatar: "images/users.svg",
selected: false
};
this.addItem(item);
this.addItem(item_two);
this.addItem(item_three);
this.refreshDOMItems();
//event listeners for each item;
const itemClick = this.shadowRoot.querySelector(".listitem");
itemClick.addEventListener("click", (event) => {
event.preventDefault();
let targetStr = "";
const trgtCls = event.target.classList;
if (
trgtCls.contains("name") ||
trgtCls.contains("img") ||
trgtCls.contains("item")
) {
if (trgtCls.contains("name")) {
targetStr = event.target.innerText;
}
if (trgtCls.contains("img")) {
targetStr = event.target.nextElementSibling.innerText;
}
if (trgtCls.contains("item")) {
targetStr = event.target.querySelector(".name").innerText;
}
}
if (targetStr === this.items[0].name) {
this.expand();
} else {
this.changedItem(targetStr);
}
});
}
addItem(item = Object) {
this.items.push(item);
this.items.forEach((item) => {
item.selected = false;
});
this.items[0].selected = true;
console.log(item.selected);
}
refreshDOMItems() {
removeChildNodes(this.ul);
this.items.forEach((item) => {
const itemTemplate = document.createElement("template");
itemTemplate.innerHTML = `
<li class="item">
<svg viewBox="0 0 512 512" width="100" title="user-alt" class="img">
<path d="M256 288c79.5 0 144-64.5 144-144S335.5 0 256 0 112 64.5 112 144s64.5 144 144 144zm128 32h-55.1c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16H128C57.3 320 0 377.3 0 448v16c0 26.5 21.5 48 48 48h416c26.5 0 48-21.5 48-48v-16c0-70.7-57.3-128-128-128z" />
</svg>
<p class="name">${item.name}</p>
</li>
`;
this.ul.appendChild(itemTemplate.content.cloneNode(true));
});
}
getItems() {
return this.items;
}
changedItem(itemSelected) {
let arr = Array.from(this.items);
this.items.forEach(function(item, index) {
if (item.name == itemSelected) {
arr = moveElementArray(arr, index, 0);
}
});
this.items = arr;
this.items.forEach((item) => {
item.selected = false;
});
this.items[0].selected = true;
this.refreshDOMItems();
this.collapse();
}
selected() {
let selected;
this.items.forEach((item) => {
if (item.selected === true) {
selected = item;
}
});
return selected;
}
value() {
let selected;
this.items.forEach((item) => {
if (item.selected === true) {
selected = item;
}
});
return selected.name;
}
expand() {
let height = 10;
Array.from(this.itemsHTML).forEach((item) => {
height += item.clientHeight;
});
this.listItem.style.height = height / 16 + "rem";
}
collapse() {
this.listItem.style.height = this.initialHeight;
}
}
window.customElements.define("c-list-item", ListItem);
const lsim = document.querySelector(".list");
function removeChildNodes(element = HTMLElement) {
while (element.childElementCount > 0) {
element.removeChild(element.firstChild);
}
}
function stringToNumber(string = String) {
let newNumber = "";
let afterComma = "";
let comma = false;
Array.from(string).forEach((char) => {
if (char === "." || char === ",") {
comma = true;
}
if (comma === false) {
if (Number(char)) {
newNumber += Number(char);
}
} else {
if (Number(char)) {
afterComma += Number(char);
}
}
});
if (afterComma != "") {
newNumber += "." + afterComma;
}
return Number(newNumber);
}
function moveElementArray(array = Array, from = Number, to = Number) {
const fromArr = array[from];
if (from > to) {
for (let index = from; index >= to; index--) {
array[index] = array[index - 1];
if (index == to) {
array[index] = fromArr;
}
}
} else {
for (let index = from; index <= to; index++) {
array[index] = array[index + 1];
if (index == to) {
array[index] = fromArr;
}
}
}
return array;
}
function replaceElementArray(array = Array, from = Number, to = Number) {
const fromArr = array[from];
const toArr = array[to];
array[from] = toArr;
array[to] = fromArr;
return array;
}
<body style="background-color: #f0f0f0;">
<c-list-item class="list"></c-list-item>
</body>
This Javascript function allows for multiple file uploads and includes a file size limit, however it is missing a maximum number of files allowed to be uploaded.
The function handleFile(e) has the file type and size arguments passed through it but do not know where to introduce a limit on the allowable number of files to be uploaded.
const fInputs = document.querySelectorAll('.btcd-f-input>div>input')
function getFileSize(size) {
let _size = size
let unt = ['Bytes', 'KB', 'MB', 'GB'],
i = 0; while (_size > 900) { _size /= 1024; i++; }
return (Math.round(_size * 100) / 100) + ' ' + unt[i];
}
function delItem(el) {
fileList = { files: [] }
let fInp = el.parentNode.parentNode.parentNode.querySelector('input[type="file"]')
for (let i = 0; i < fInp.files.length; i++) {
fileList.files.push(fInp.files[i])
}
fileList.files.splice(el.getAttribute('data-index'), 1)
fInp.files = createFileList(...fileList.files)
if (fInp.files.length > 0) {
el.parentNode.parentNode.parentNode.querySelector('.btcd-f-title').innerHTML = `${fInp.files.length} File Selected`
} else {
el.parentNode.parentNode.parentNode.querySelector('.btcd-f-title').innerHTML = 'No File Chosen'
}
el.parentNode.remove()
}
function fade(element) {
let op = 1; // initial opacity
let timer = setInterval(function () {
if (op <= 0.1) {
clearInterval(timer);
element.style.display = 'none';
}
element.style.opacity = op;
element.style.filter = 'alpha(opacity=' + op * 100 + ")";
op -= op * 0.1;
}, 50);
}
function unfade(element) {
let op = 0.01; // initial opacity
element.style.opacity = op;
element.style.display = 'flex';
let timer = setInterval(function () {
if (op >= 1) {
clearInterval(timer);
}
element.style.opacity = op;
element.style.filter = 'alpha(opacity=' + op * 100 + ")";
op += op * 0.1;
}, 13);
}
function get_browser() {
let ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if (/trident/i.test(M[1])) {
tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
return { name: 'IE', version: (tem[1] || '') };
}
if (M[1] === 'Chrome') {
tem = ua.match(/\bOPR|Edge\/(\d+)/)
if (tem != null) { return { name: 'Opera', version: tem[1] }; }
}
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]); }
return {
name: M[0],
version: M[1]
};
}
for (let inp of fInputs) {
inp.parentNode.querySelector('.btcd-inpBtn>img').src = ''
inp.addEventListener('mousedown', function (e) { setPrevData(e) })
inp.addEventListener('change', function (e) { handleFile(e) })
}
let fileList = { files: [] }
let fName = null
let mxSiz = null
function setPrevData(e) {
if (e.target.hasAttribute('multiple') && fName !== e.target.name) {
console.log('multiple')
fName = e.target.name
fileList = fileList = { files: [] }
if (e.target.files.length > 0) {
for (let i = 0; i < e.target.files.length; i += 1) {
console.log(e.target.files[i])
fileList.files.push(e.target.files[i])
}
}
}
}
function handleFile(e) {
let err = []
const fLen = e.target.files.length;
mxSiz = e.target.parentNode.querySelector('.f-max')
mxSiz = mxSiz != null && (Number(mxSiz.innerHTML.replace(/\D/g, '')) * Math.pow(1024, 2))
if (e.target.hasAttribute('multiple')) {
for (let i = 0; i < fLen; i += 1) {
fileList.files.push(e.target.files[i])
}
} else {
fileList.files.push(e.target.files[0])
}
//type validate
if (e.target.hasAttribute('accept')) {
let tmpf = []
let type = new RegExp(e.target.getAttribute('accept').split(",").join("$|") + '$', 'gi')
for (let i = 0; i < fileList.files.length; i += 1) {
if (fileList.files[i].name.match(type)) {
tmpf.push(fileList.files[i])
} else {
err.push('Wrong File Type Selected')
}
}
fileList.files = tmpf
}
// size validate
if (mxSiz > 0) {
let tmpf = []
for (let i = 0; i < fileList.files.length; i += 1) {
if (fileList.files[i].size < mxSiz) {
tmpf.push(fileList.files[i])
mxSiz -= fileList.files[i].size
} else {
console.log('rejected', i, fileList.files[i].size)
err.push('Max Upload Size Exceeded')
}
}
fileList.files = tmpf
}
if (e.target.hasAttribute('multiple')) {
e.target.files = createFileList(...fileList.files)
} else {
e.target.files = createFileList(fileList.files[fileList.files.length - 1])
fileList = { files: [] }
}
// set File list view
if (e.target.files.length > 0) {
e.target.parentNode.querySelector('.btcd-f-title').innerHTML = e.target.files.length + ' File Selected'
e.target.parentNode.parentNode.querySelector('.btcd-files').innerHTML = ''
for (let i = 0; i < e.target.files.length; i += 1) {
let img = null
if (e.target.files[i].type.match(/image-*/)) {
img = window.URL.createObjectURL(e.target.files[i])
}
else {
img = ''
}
e.target.parentNode.parentNode.querySelector('.btcd-files').insertAdjacentHTML('beforeend', `<div>
<img src="${img}" alt="img" title="${e.target.files[i].name}">
<div>
<span title="${e.target.files[i].name}">${e.target.files[i].name}</span>
<br/>
<small>${getFileSize(e.target.files[i].size)}</small>
</div>
<button type="button" onclick="delItem(this)" data-index="${i}" title="Remove This File"><span>×</span></button>
</div>`)
}
}
// set eror
if (err.length > 0) {
for (let i = 0; i < err.length; i += 1) {
e.target.parentNode.parentNode.querySelector('.btcd-files').insertAdjacentHTML('afterbegin', `
<div style="background: #fff2f2;color: darkred;display:none" class="btcd-f-err">
<img src="" alt="img">
<span>${err[i]}</span>
</div>`)
}
const errNods = e.target.parentNode.parentNode.querySelectorAll('.btcd-files>.btcd-f-err')
for (let i = 0; i < errNods.length; i += 1) {
unfade(errNods[i])
setTimeout(() => { fade(errNods[i]) }, 3000);
setTimeout(() => { errNods[i].remove() }, 4000);
}
err = []
}
}
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: Arial, Helvetica, sans-serif;
}
.btcd-f-input {
display: inline-block;
width: 340px;
position: relative;
overflow: hidden;
}
.btcd-f-input>div>input::-webkit-file-upload-button {
cursor: pointer;
}
.btcd-f-wrp {
cursor: pointer;
}
.btcd-f-wrp>small {
color: gray;
}
.btcd-f-wrp>button {
cursor: pointer;
background: #f3f3f3;
padding: 5px;
display: inline-block;
border-radius: 9px;
border: none;
margin-right: 8px;
height: 35px;
}
.btcd-f-wrp>button>img {
width: 24px;
}
.btcd-f-wrp>button>span,
.btcd-f-wrp>span,
.btcd-f-wrp>small {
vertical-align: super;
}
.btcd-f-input>.btcd-f-wrp>input {
z-index: 100;
width: 100%;
position: absolute;
opacity: 0;
left: 0;
height: 37px;
cursor: pointer;
}
.btcd-f-wrp:hover {
background: #fafafa;
border-radius: 10px;
}
.btcd-files>div {
display: flex;
align-items: center;
background: #f8f8f8;
border-radius: 10px;
margin-left: 30px;
width: 91%;
margin-top: 10px;
height: 40px;
}
.btcd-files>div>div {
display: inline-block;
width: 73%;
}
.btcd-files>div>div>small {
color: gray;
}
.btcd-files>div>img {
width: 40px;
height: 40px;
margin-right: 10px;
border-radius: 10px;
}
.btcd-files>div>div>span {
display: inline-block;
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.btcd-files>div>button {
background: #e8e8e8;
border: none;
border-radius: 50px;
width: 25px;
height: 25px;
font-size: 20px;
margin-right: 6px;
padding: 0;
}
.btcd-files>div>button:hover {
background: #bbbbbb;
}
<div class="btcd-f-input">
<small>Multiple Upload</small>
<div class="btcd-f-wrp">
<button class="btcd-inpBtn" type="button"> <img src="" alt=""> <span> Attach File</span></button>
<span class="btcd-f-title">No File Chosen</span>
<small class="f-max">(Max 1 MB)</small>
<input multiple type="file" name="snd_multiple" id="">
</div>
<div class="btcd-files">
</div>
</div>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
<script src="https://unpkg.com/create-file-list#1.0.1/dist/create-file-list.min.js"></script>
In the function handleFile before type validate:
let maxFileNum = 10; // Maximum number of files
if (fileList.files.length > maxFileNum){
err.push("Too many files selected");
}