How to handle keyword boxes with spans in JavaScript / HTML / CSS - javascript

I am trying to build a variable number of keyword boxes (the number of keyword boxes is handeld by php).
For better understanding I made three hardcoded html keyword boxes.
The final result should be multiple keyword boxes which can handle multiple keywords and show them after every ENTER in a seperated inner box. If I click on the BUTTON, all keywords should be alerted.
My current try is nearly working. You are able to type in keywords, store them by pressing enter.
But the inner keyword boxes are only shown, after klicking on the keyword box again.
I would be verry grateful if anybody could help me with this problem. :)
let tags = [];
let tagContainer = document.querySelectorAll('.tag-container');
tagContainer.forEach(function(foo) {
foo.addEventListener('click', (e) => {
//console.log(e.target.tagName);
//if (e.target.tagName === 'I') {
var tagLabel = e.target.getAttribute('data-item');
var index = tags.indexOf(tagLabel);
tags = [...tags.slice(0, index), ...tags.slice(index + 1)];
foo.querySelectorAll('.tag').forEach(tag => {
tag.parentElement.removeChild(tag);
});
tags.slice().reverse().forEach(tag => {
var div = document.createElement('div');
div.setAttribute('class', 'tag');
var span = document.createElement('span');
span.innerHTML = tag;
var closeIcon = document.createElement('i');
closeIcon.innerHTML = 'close';
closeIcon.setAttribute('class', 'material-icons');
closeIcon.setAttribute('data-item', tag);
div.appendChild(span);
div.appendChild(closeIcon);
foo.prepend(div);
});
//}
})
});
let input = document.querySelectorAll('.tag-container input');
input.forEach(function(bar) {
bar.addEventListener('keyup', (e) => {
if (e.key === 'Enter') {
e.target.value.split(',').forEach(tag => {
tags.push(tag);
});
bar.querySelectorAll('.tag').forEach(tag => {
tag.parentElement.removeChild(tag);
});
tags.slice().reverse().forEach(tag => {
var div = document.createElement('div');
div.setAttribute('class', 'tag');
var span = document.createElement('span');
span.innerHTML = tag;
var closeIcon = document.createElement('i');
closeIcon.innerHTML = 'close';
closeIcon.setAttribute('class', 'material-icons');
closeIcon.setAttribute('data-item', tag);
div.appendChild(span);
div.appendChild(closeIcon);
bar.prepend(div);
});
bar.value = '';
}
bar.focus();
})
});
function clicked() {
alert(JSON.stringify(tags));
}
.container {
width: 20%;
margin: 40px;
align-self: center;
}
.tag-container {
border: 2px solid #ccc;
padding: 10px;
border-radius: 5px;
display: flex;
}
.tag-container .tag {
padding: 5px;
border: 1px solid #ccc;
display: flex;
align-items: center;
margin: 5px;
border-radius: 3px;
background: #f2f2f2;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2), inset 0 1px 1px #fff;
cursor: default;
}
.tag i {
font-size: 16px;
margin-left: 5px;
}
.tag-container input {
flex: 1;
font-size: 16px;
padding: 5px;
outline: none;
border: 0;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<table>
<td>
<div class="tag-container">
<div id="span_tag">
<span id="span_key" value=""></span>
<i class="material-icons">close</i>
</div>
<input id="input_search" value="" />
</div>
</tr>
</td>
<td><button type="button" id="bt" onclick="clicked()">Button</button>
<td/>
</table>
<table>
<td>
<div class="tag-container">
<div id="span_tag">
<span id="span_key" value=""></span>
<i class="material-icons">close</i>
</div>
<input id="input_search" value="" />
</div>
</tr>
</td>
<td><button type="button" id="bt" onclick="clicked()">Button</button>
<td/>
</table>
<table>
<td>
<div class="tag-container">
<div id="span_tag">
<span id="span_key" value=""></span>
<i class="material-icons">close</i>
</div>
<input id="input_search" value="" />
</div>
</tr>
</td>
<td><button type="button" id="bt" onclick="clicked()">Button</button>
<td/>
</table>
re

const tagContainerNodes = document.querySelectorAll(".tag-container");
tagContainerNodes.forEach((tagContainerNode) => {
const input = tagContainerNode.querySelector("input");
input.addEventListener("keyup", e => {
if (e.key === "Enter") {
createTag(e.target.value, tagContainerNode, input);
e.target.value = "";
}
});
});
function createTag(tagName, parent, before) {
var div = document.createElement("div");
div.setAttribute("class", "tag");
var span = document.createElement("span");
span.innerHTML = tagName;
div.appendChild(span);
parent.insertBefore(div, before)
}
function clicked() {
const allTagText = [];
tagContainerNodes.forEach((tagContainerNode) => {
const tagNodes = tagContainerNode.querySelectorAll(".tag");
tagNodes.forEach((tagNode) => {
allTagText.push(tagNode.textContent);
});
});
console.log(allTagText);
}
.tag-container {
border: 2px solid #ccc;
padding: 10px;
border-radius: 5px;
display: flex;
}
.tag-container .tag {
padding: 5px;
border: 1px solid #ccc;
margin: 5px;
border-radius: 3px;
background: #f2f2f2;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2), inset 0 1px 1px #fff;
}
.tag-container input {
font-size: 16px;
padding: 5px;
outline: none;
border: 0;
}
<div class="tag-container">
<input id="input_search" />
</div>
<button type="button" onclick="clicked()">Button</button>
<div class="tag-container">
<input id="input_search" />
</div>
<button type="button" onclick="clicked()">Button</button>
<div class="tag-container">
<input id="input_search" />
</div>
<button type="button" onclick="clicked()">Button</button>

Related

how to render img by filereader and push img arr to obj

What I'm trying to do is to get an image from my input type="file" and do a card with this image and other information from inputs type="text".
The problem is that whenever I store an image src data in variable and push it to array of imgArrForPush (that is array for extra pictures that admin wants to see on other page after you click on card) and it works but when I'm trying to add imgArrForPush in object to push all of data to cardArr (cardArr is arr that store all card's information in object and I want to render this data) it doesn't work, what I receive is undefined.
The error:
undefined:1 GET http://127.0.0.1:5500/undefined 404 (Not Found)
I don't think that css of the whole page play a big role in that problem so I created another folder and trying to solve this problem with this styles. But I think that css of a card would be important.
I'm also using fontawesome icons and I don't know is it ok that icons is here in code.
<div class="admin-inputs-container">
<input type="file" class="input">
<button class="admin-buttons" onclick="onGetImg()">submit</button>
<input class="admin-inputs img-name-input" type="text" placeholder="img name(must be number)" />
<input class="admin-inputs district-input" type="text" placeholder="district"/>
<input class="admin-inputs area-input" type="text" placeholder="area"/>
<input class="admin-inputs rooms-input" type="text" placeholder="number of rooms"/>
<input class="admin-inputs floor-input" type="text" placeholder="floor"/>
<input class="admin-inputs number-of-floors-input" type="text" placeholder="floors in building"/>
<input class="admin-inputs price-input" type="text" placeholder="price"/>
<input class="admin-inputs adress-input" type="text" placeholder="adreess"/>
<div class="admin-inputs-container">
<button class="admin-buttons" onclick="onGetDataFromInput()">submit</button>
</div>
<button onclick="redner()">render</button>
<div class="container"></div>
.card {
display: flex;
flex-direction: column;
width: 40%;
align-items: flex-start;
border: 4px solid #0A3D33;
margin: 0 0 30px 0;
}
.card-button {
margin: 0 20px 0 0;
font-size: 16px;
font-weight: 600;
transition: all 0.3s;
cursor: pointer;
background-color: #f4f3f0;
color: #0A3D33;
border: 2px solid #0A3D33;
border-radius: 5px;
height: 60px;
width: 150px;
}
.card-button:hover {
background-color: #0A3D33 ;
color: #f4f3f0;
}
.card-button:active {
background-color: #E7E6E3;
color: #0A3D33;
}
.card-info-container {
width: 100%;
display: flex;
flex-direction: column;
}
.card-info-sign-container {
border-top: 2px solid #0A3D33;
display: flex;
gap: 20px;
padding: 0 0 0 10px;
}
.icon-wrapper {
display: flex;
gap: 20px;
}
.card-info-price-container {
border-top: 2px solid #0A3D33;
gap: 5px;
padding: 0 0 0 10px;
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
}
.card-info-sign {
font-size: 36px;
}
.card-price {
font-size: 36px;
}
.card-img {
height: 250px;
width: 100%;
cursor: pointer;
}
.card-icon-container {
display: flex;
align-items: center;
justify-content: center;
width: 25px;
}
.fa-arrow-up,
.fa-bed,
.fa-layer-group,
.fa-location-dot {
font-size: 20px;
}
.fa-dollar-sign {
font-size: 35px;
color: #137A63;
}
.fa-location-dot {
color: #c85108;
}
.fa-layer-group {
color: #a20e0e;
}
.fa-arrow-up {
color: #318CE7;
}
.password-input {
width: 500px;
text-align: center;
height: 50px;
background-color: #f4f3f0;
border: 2px solid #0A3D33;
color: #0A3D33;
font-weight: 600;
font-size: 20px;
border-radius: 10px;
}
.wirte-a-password-sign{
font-size: 30px;
font-weight: 600;
color: #0A3D33;
}
.admin-inputs {
width: 320px;
height: 30px;
border: 2px solid #0A3D33;
border-radius: 5px;
padding: 5px;
background-color: #f4f3f0;
color: #0A3D33;
font-size: 20px;
font-weight: 600;
}
.admin-buttons {
font-weight: 600;
transition: all 0.3s;
cursor: pointer;
background-color: #0A3D33;
color: #f4f3f0;
width: 150px;
height: 40px;
border-radius: 5px;
}
.admin-buttons:hover {
border: 2px solid #0A3D33;
background-color: #f4f3f0 ;
color: #0A3D33;}
const adressInput = document.querySelector(".adress-input");
const priceInput = document.querySelector(".price-input");
const numberOfFloorsInput = document.querySelector(".number-of-floors-input");
const floorInput = document.querySelector(".floor-input");
const roomsInput = document.querySelector(".rooms-input");
const areaInput = document.querySelector(".area-input");
const districtInput = document.querySelector(".district-input");
const imgNameInput = document.querySelector(".img-name-input");
const imgInput = document.querySelector('.input');
const containerImg = document.querySelector('.container-img');
const container = document.querySelector('.container');
let uploaded_image = "";
const cardArr = [];
const imgArrForPush = [];
const onGetImg = () => {
const reader = new FileReader();
var file = imgInput.files[0];
reader.addEventListener("load", () => {
uploaded_image = reader.result;
imgArrForPush.push(uploaded_image);
})
reader.readAsDataURL(file);
imgInput.value = "";
}
const onGetDataFromInput = () => {
cardArr.push({
imgNameInput:imgNameInput.value,
inputData : {
districtInput:districtInput.value,
areaInput:areaInput.value,
roomsInput:roomsInput.value,
floorInput:floorInput.value,
numberOfFloorsInput:numberOfFloorsInput.value,
priceInput:priceInput.value,
adressInput:adressInput.value
},
images:imgArrForPush
});
imgNameInput.value = "";
districtInput.value = "";
areaInput.value = "";
roomsInput.value = "";
floorInput.value = "";
numberOfFloorsInput.value = "";
priceInput.value = "";
adressInput.value = "";
imgInput.value = "";
imgArrForPush.splice(0,imgArrForPush.length);
}
const redner = () => {
container.innerHTML = "";
cardArr.forEach(card => {
console.log(card.images);
container.innerHTML += ` <div class="card">
<img src="${card.images[card.images.length - 1]}"/>
<div class="card-info-container">
<div class="card-info-sign-container">
<div class="card-icon-container">
<i class="fa-solid fa-location-dot"></i>
</div>
<p class="card-info-sign">${card.inputData.districtInput}</p>
</div>
<div class="card-info-sign-container">
<div class="card-icon-container">
<i class="fa-solid fa-layer-group"></i>
</div>
<p class="card-info-sign">${card.inputData.areaInput} м&sup2</p>
</div>
<div class="card-info-sign-container">
<div class="card-icon-container">
<i class="fa-solid fa-bed"></i>
</div>
<p class="card-info-sign">${card.inputData.roomsInput}</p>
</div>
<div class="card-info-sign-container">
<div class="card-icon-container">
<i class="fa-solid fa-arrow-up"></i>
</div>
<p class="card-info-sign">${card.inputData.floorInput}</p>
</div>
<div class="card-info-price-container">
<div class="icon-wrapper">
<div class="card-icon-container">
<i class="fa-solid fa-dollar-sign"></i>
</div>
<p class="card-info-sign card-price">${card.inputData.priceInput}$</p>
</div>
<button class="card-button">Перейти</button>
</div>
</div>
</div>`
})
}
What I want to get is this :
For example:
cardArr = [{
imgNameInput:"the_img_name",
districtInput:"the_district",
areaInput:"250",
roomsInput:"3",
floorInput:"5",
numberOfFloorsInput:"10",
priceInput:"30000",
adressInput:"the_adress",
images:["the_img_path_1","the_img_path_2","the_img_path_3"]
}]
Thanks to the #FiddlingAway!!
const adressInput = document.querySelector(".adress-input");
const priceInput = document.querySelector(".price-input");
const numberOfFloorsInput = document.querySelector(".number-of-floors-input");
const floorInput = document.querySelector(".floor-input");
const roomsInput = document.querySelector(".rooms-input");
const areaInput = document.querySelector(".area-input");
const districtInput = document.querySelector(".district-input");
const imgNameInput = document.querySelector(".img-name-input");
const imgInput = document.querySelector('.input');
const containerImg = document.querySelector('.container-img');
const container = document.querySelector('.container');
let uploaded_image = "";
const cardArr = [];
let imgArrForPush = [];
const onGetImg = () => {
const reader = new FileReader();
var file = imgInput.files[0];
reader.addEventListener("load", () => {
uploaded_image = reader.result;
imgArrForPush.push(uploaded_image);
//imgArrForPush.push(uploaded_image);
})
reader.readAsDataURL(file);
imgInput.value = "";
}
const onGetDataFromInput = () => {
cardArr.push({
imgNameInput:imgNameInput.value,
inputData : {
districtInput:districtInput.value,
areaInput:areaInput.value,
roomsInput:roomsInput.value,
floorInput:floorInput.value,
numberOfFloorsInput:numberOfFloorsInput.value,
priceInput:priceInput.value,
adressInput:adressInput.value
},
images:imgArrForPush
});
imgNameInput.value = "";
districtInput.value = "";
areaInput.value = "";
roomsInput.value = "";
floorInput.value = "";
numberOfFloorsInput.value = "";
priceInput.value = "";
adressInput.value = "";
imgInput.value = "";
imgArrForPush = [];
}
const redner = () => {
container.innerHTML = "";
cardArr.forEach(card => {
console.log(card.images);
container.innerHTML += ` <div class="card">
<img src="${card.images[0]}"/>
<div class="card-info-container">
<div class="card-info-sign-container">
<div class="card-icon-container">
<i class="fa-solid fa-location-dot"></i>
</div>
<p class="card-info-sign">${card.inputData.districtInput}</p>
</div>
<div class="card-info-sign-container">
<div class="card-icon-container">
<i class="fa-solid fa-layer-group"></i>
</div>
<p class="card-info-sign">${card.inputData.areaInput} м&sup2</p>
</div>
<div class="card-info-sign-container">
<div class="card-icon-container">
<i class="fa-solid fa-bed"></i>
</div>
<p class="card-info-sign">${card.inputData.roomsInput}</p>
</div>
<div class="card-info-sign-container">
<div class="card-icon-container">
<i class="fa-solid fa-arrow-up"></i>
</div>
<p class="card-info-sign">${card.inputData.floorInput}</p>
</div>
<div class="card-info-price-container">
<div class="icon-wrapper">
<div class="card-icon-container">
<i class="fa-solid fa-dollar-sign"></i>
</div>
<p class="card-info-sign card-price">${card.inputData.priceInput}$</p>
</div>
<button class="card-button">Перейти</button>
</div>
</div>
</div>`
})
}

Collecting information from a step-form and displaying it in a confirmation page?

In the snippet below, I have a step-form. While submitting the content in the form and hitting next, the input values are being passed to the confirmation page at the end.
I'm trying to understand if this is the most efficient way to pass the information to the confirmation page?
const previousButton = document.getElementById("previous")
const nextButton = document.getElementById("next")
const submitButton = document.getElementById('validate')
const form = document.getElementById('stepByStepForm')
const dots = document.getElementsByClassName('progress-bar__dot')
const numberOfSteps = 5
let currentStep = 1
for(let i = 0 ; i < dots.length ; ++i){
dots[i].addEventListener('click', ()=>{
goToStep(i+1)
})
}
previousButton.onclick = goPrevious
nextButton.onclick = goNext
function goNext(e) {
e.preventDefault()
currentStep += 1
goToStep(currentStep)
}
function goPrevious(e) {
e.preventDefault()
currentStep -= 1
goToStep(currentStep)
}
function goToStep(stepNumber){
currentStep = stepNumber
let inputsToHide = document.getElementsByClassName('step')
let inputs = document.getElementsByClassName(`step${currentStep}`)
let indicators = document.getElementsByClassName('progress-bar__dot')
for(let i = indicators.length-1; i >= currentStep ; --i){
indicators[i].classList.remove('full')
}
for(let i = 0; i < currentStep; ++i){
indicators[i].classList.add('full')
}
//hide all input
for (let i = 0; i < inputsToHide.length; ++i) {
hide(inputsToHide[i])
}
//only show the right one
for (let i = 0; i < inputs.length; ++i) {
show(inputs[i])
}
//if we reached final step
if(currentStep === numberOfSteps){
enable(previousButton)
disable(nextButton)
show(submitButton)
}
//else if first step
else if(currentStep === 1){
disable(previousButton)
enable(next)
hide(submitButton)
}
else {
enable(previousButton)
enable(next)
hide(submitButton)
}
}
function enable(elem) {
elem.classList.remove("disabled");
elem.disabled = false;
}
function disable(elem) {
elem.classList.add("disabled");
elem.disabled = true;
}
function show(elem){
elem.classList.remove('hidden')
}
function hide(elem){
elem.classList.add('hidden')
}
//collect inputs
next.addEventListener('click', function() {
const fNameInput = document.getElementById('firstname').value;
document.getElementById('fNameOutput').textContent = fNameInput;
const lNameInput = document.getElementById('lastname').value;
document.getElementById('lNameOutput').textContent = lNameInput;
const emailInput = document.getElementById('mail').value;
document.getElementById('emailOutput').textContent = emailInput;
const phoneInput = document.getElementById('phone').value;
document.getElementById('phoneOutput').textContent = phoneInput;
const addressInput = document.getElementById('address').value;
document.getElementById('addressOutput').textContent = addressInput;
const countryInput = document.getElementById('country').value;
document.getElementById('countryOutput').textContent = countryInput;
const colorInput = document.getElementById('color').value;
document.getElementById('colorOutput').textContent = colorInput;
const animalInput = document.getElementById('animal').value;
document.getElementById('animalOutput').textContent = animalInput;
});
submitButton.addEventListener('click', function(e) {
e.preventDefault();
})
html {
background-color: #A2C7E5;
}
.hidden {
display: none;
}
.button {
width: 140px;
cursor: pointer;
padding: 1em;
border-radius: 0.2em;
border: none;
color: white;
font-weight: bold;
font-size: 1em;
transition: all 0.5s ease;
background-color: #ff8552;
}
.button:hover {
background-color: #ff4f06;
}
.button.disabled {
opacity: 0.5;
}
.button.disabled:hover {
background-color: #ff8552;
}
.form {
width: 20em;
margin: auto;
margin-top: 5em;
padding: 5em;
padding-top: 2em;
padding-bottom: 2em;
background-color: white;
font-family: "Raleway", sans-serif;
color: #33312E;
border-radius: 0.2em;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
}
.form .progress-bar {
width: 100%;
list-style-type: none;
display: flex;
padding: 0;
justify-content: center;
margin-bottom: 3.5em;
}
.form .progress-bar li.progress-bar__dot {
display: block;
width: 0.6em;
border-radius: 50%;
height: 0.6em;
border: 0.1em solid #ff8552;
background-color: white;
cursor: pointer;
transition: all 0.5s ease;
}
.form .progress-bar li.progress-bar__dot.full {
background-color: #ff8552;
}
.form .progress-bar li.progress-bar__connector {
display: block;
width: 5em;
border-radius: 1000em;
height: 0.1em;
background-color: #ff8552;
margin-top: 0.35em;
}
.form label {
font-weight: bold;
font-size: 1.2em;
}
.form input {
width: 100%;
padding: 1.3em;
margin-bottom: 3em;
box-sizing: border-box;
margin-top: 1em;
border: none;
border-radius: 0.5em;
background-color: #e6e6e6;
font-size: 1em;
font-family: "Raleway", sans-serif;
}
.form input:focus {
border: none;
}
.form .button-group {
width: 100%;
display: flex;
justify-content: space-between;
margin-top: 50px;
}
.form button#validate {
margin: auto;
background-color: #1A936F;
width: 12em;
}
.form button#validate:hover {
background-color: #12684e;
}
<form id="stepByStepForm" class="form">
<ul class="progress-bar">
<li class="progress-bar__dot full"></li>
<li class="progress-bar__connector"></li>
<li class="progress-bar__dot"></li>
<li class="progress-bar__connector"></li>
<li class="progress-bar__dot"></li>
<li class="progress-bar__connector"></li>
<li class="progress-bar__dot"></li>
<li class="progress-bar__connector"></li>
<li class="progress-bar__dot"></li>
</ul>
<div class="step step1">
<div class="input-group">
<label for="firstname">First name</label>
<input type="text" value="James" name="firstname" id="firstname">
</div>
<div class="input-group">
<label for="lastname">Last name</label>
<input type="text" value="Smith" name="lastname" id="lastname">
</div>
</div>
<div class="step step2 hidden">
<div class="input-group">
<label for="mail">E-mail</label>
<input type="mail" value="jamessmith#gmail.com" name="mail" id="mail">
</div>
<div class="input-group">
<label for="phone">Phone</label>
<input type="text" value="602-379-1395" name="phone" id="phone">
</div>
</div>
<div class="step step3 hidden">
<div class="input-group">
<label for="address">Address</label>
<input type="text" value="221B Baker Street" name="address" id="address">
</div>
<div class="input-group">
<label for="country">Country</label>
<input type="text" value="United States" name="country" id="country">
</div>
</div>
<div class="step step4 hidden">
<div class="input-group">
<label for="color">Favorite Color</label>
<input type="text" value="Blue" name="color" id="color">
</div>
<div class="input-group">
<label for="animal">Favorite Animal</label>
<input type="text" value="Lion" name="animal" id="animal">
</div>
</div>
<div class="step step5 hidden">
<h4>Name:</h4>
<p><strong>First Name:</strong> <span id="fNameOutput"></span></p>
<p><strong>Last Name:</strong> <span id="lNameOutput"></span></p>
<hr>
<h4>Contact:</h4>
<p><strong>Email:</strong> <span id="emailOutput"></span></p>
<p><strong>Phone:</strong> <span id="phoneOutput"></span></p>
<hr>
<h4>Location:</h4>
<p><strong>Address:</strong> <span id="addressOutput"></span></p>
<p><strong>Country:</strong> <span id="countryOutput"></span></p>
<hr>
<h4>Attributes:</h4>
<p><strong>Favorite Color:</strong> <span id="colorOutput"></span></p>
<p><strong>Favorite Animal:</strong> <span id="animalOutput"></span></p>
</div>
<div class="button-group">
<button id="previous" class="disabled button" disabled>
previous
</button>
<button id="next" class="button">
next
</button>
</div>
<div class="button-group">
<button id="validate" type="submit" class="hidden button">
Submit
</button>
</div>
</form>
There are 3 problems:
In this part next.addEventListener('click', function() {...}, it updates all texts every time the next button is pressed.
document.getElementById(ID_NAME).textContent = VALUE; is written repeatedly; It is not that bad but there are more readable ways.
The confirmation textContents are not updated when the last dot is clicked.
First, make a function to update texts. To avoid writing similar code repeatedly, you can use an Object for ids and for...of to implement updating the text contents.
function updateConfirmText() {
const map = {
firstname: 'fNameOutput',
lastname: 'lNameOutput',
mail: 'emailOutput',
phone: 'phoneOutput',
address: 'addressOutput',
country: 'countryOutput',
color: 'colorOutput',
animal: 'animalOutput'
};
for (const [input, output] of Object.entries(map)) {
document.getElementById(output).textContent = document.getElementById(input).value;
}
}
Second, call the function in the if statement that you have already written.
//if we reached final step
if (currentStep === numberOfSteps) {
enable(previousButton)
disable(nextButton)
show(submitButton)
updateConfirmText(); // Add
}
I hope it works🤞

Dropdown menu with result js

I am writing a search menu that searches for and shows on the page all matching substrings in a string on pure js. I made the search on page, but I don't know how to display the results in the search drop-down menu with the number of results like on picture use only js. My code and picture below:
const input = document.querySelector('input')
const paragraphs = document.querySelectorAll('p')
input.addEventListener('input', e => {
const searchTerm = e.target.value
const regex = new RegExp(searchTerm, 'g')
const replacement = `<span class="highlighted">${ searchTerm }</span>`
for (const p of paragraphs) {
p.innerHTML = p.innerText.replace(regex, replacement)
}
})
:root {
--primary-color: crimson;
--dark-color: #013;
--light-color: whitesmoke;
}
body {
color: var(--dark-color);
background-color: var(--light-color);
padding: 1rem;
}
header {
text-align: center;
}
main {
max-width: 567px;
margin: 0 auto;
}
.search__container {
text-align: center;
padding: 1rem;
margin: 0 auto;
}
.search__input {
border: 2px solid var(--dark-color);
padding: 1rem;
text-align: center;
}
.search__input:hover,
.search__input:focus {
outline: none;
border-color: var(--primary-color);
-webkit-box-shadow: 0 0 5px 0.1px var(--primary-color);
box-shadow: 0 0 5px 0.1px var(--primary-color);
}
.highlighted {
text-shadow: 0 0 6px var(--primary-color);
color: var(--primary-color);
}
<section class="search__container">
<input class="search__input" type="text" spellcheck="false" placeholder="search..." />
</section>
<section>
<div class="paragraphs">
<div id="p"><p>Buffalofish sawtooth </p>
<p>eel tigerperch, john dory sleeper,</p>
<p>Spanish mackerel </p>
<p>sockeye salmon sturgeon </p>
<p>gray mullet bottlenose. </p>
<p>Banjo catfish wallago; deep </p>
<p>sea smelt titan triggerfish </p>
<p>Australian grayling threadfin </p>
<p>bighead carp longnose lancetfish </p>
<p>pineconefish. Pipefish mojarra </p>
<p>northern pearleye cutthroat trout </p>
<p>sand diver; freshwater shark wrasse. </p></div>
</div>
</section>
I wrote this code, which allows you to count the number of words in your text that have your search term. AND you get a dynamic list of words that contain your search term. (I touch only HTML and JS files)
Hint: If you want to have the dynamic list more "beautiful" change the <div id="listFound"></div> with list tags, like <ul> and <li> but keep the id="listFound"
const input = document.querySelector('input')
const paragraphs = document.querySelectorAll('p')
input.addEventListener('input', e => {
document.getElementById('listFound').textContent = "" //init after each additional letter
const searchTerm = e.target.value
const regex = new RegExp(searchTerm, 'g')
const replacement = `<span class="highlighted">${ searchTerm }</span>`
for (const p of paragraphs) {
p.innerHTML = p.innerText.replace(regex, replacement)
}
/*ADD*/
string_to_array(document.getElementById('p').textContent)
checkWords(searchTerm)
let text = document.getElementById('p').textContent
let separator = searchTerm
let number = WordCount(text, separator)
document.getElementById('result').textContent = number
if(searchTerm === ""){
document.getElementById('listFound').textContent = ""
document.getElementById('result').textContent = 0
}
})
/*ADD 3 functions*/
var arrayFromWords = []
function string_to_array(str) {
arrayFromWords = str.trim().split(" ");
}
function checkWords(searchTerm){
arrayFromWords.forEach(element => {
if(element.includes(searchTerm)){
document.getElementById('listFound').textContent += element
}
})
}
function WordCount(str, separator) {
return (str.split(separator).length -1);
}
:root {
--primary-color: crimson;
--dark-color: #013;
--light-color: whitesmoke;
}
body {
color: var(--dark-color);
background-color: var(--light-color);
padding: 1rem;
}
header {
text-align: center;
}
main {
max-width: 567px;
margin: 0 auto;
}
.search__container {
text-align: center;
padding: 1rem;
margin: 0 auto;
}
.search__input {
border: 2px solid var(--dark-color);
padding: 1rem;
text-align: center;
}
.search__input:hover,
.search__input:focus {
outline: none;
border-color: var(--primary-color);
-webkit-box-shadow: 0 0 5px 0.1px var(--primary-color);
box-shadow: 0 0 5px 0.1px var(--primary-color);
}
.highlighted {
text-shadow: 0 0 6px var(--primary-color);
color: var(--primary-color);
}
<section class="search__container">
<input class="search__input" type="text" spellcheck="false" placeholder="search..." />
<div id="result"></div> <!--ADD-->
<div id="listFound"></div>
</section>
<section>
<div class="paragraphs">
<section>
<div class="paragraphs">
<div id="p"><p>Buffalofish sawtooth </p>
<p>eel tigerperch, john dory sleeper,</p>
<p>Spanish mackerel </p>
<p>sockeye salmon sturgeon </p>
<p>gray mullet bottlenose. </p>
<p>Banjo catfish wallago; deep </p>
<p>sea smelt titan triggerfish </p>
<p>Australian grayling threadfin </p>
<p>bighead carp longnose lancetfish </p>
<p>pineconefish. Pipefish mojarra </p>
<p>northern pearleye cutthroat trout </p>
<p>sand diver; freshwater shark wrasse. </p></div>
</div>
</section>
</div>
</section>
Maybe like this:
var input = document.querySelector('input');
var p = document.querySelectorAll('p');
input.addEventListener('input', e => {
var value = input.value;
p.forEach(p => {
if (!p.innerText.includes(value)) {
p.style.display = 'none';
} else {
p.style.display = 'block';
}
p.innerHTML = p.innerText.replace(value, '<span>'+value+'</span>')
})
})
:root {
--primary-color: crimson;
--dark-color: #013;
--light-color: whitesmoke;
}
body {
color: var(--dark-color);
background-color: var(--light-color);
padding: 1rem;
}
header {
text-align: center;
}
main {
max-width: 567px;
margin: 0 auto;
}
.search__container {
text-align: center;
padding: 1rem;
margin: 0 auto;
}
.search__input {
border: 2px solid var(--dark-color);
padding: 1rem;
text-align: center;
}
.search__input:hover,
.search__input:focus {
outline: none;
border-color: var(--primary-color);
-webkit-box-shadow: 0 0 5px 0.1px var(--primary-color);
box-shadow: 0 0 5px 0.1px var(--primary-color);
}
.paragraphs span {
text-shadow: 0 0 6px var(--primary-color);
color: var(--primary-color);
}
<section class="search__container">
<input class="search__input" type="text" spellcheck="false" placeholder="search..." />
</section>
<section>
<div class="paragraphs">
<p>Google Maps</p>
<p>Google Photos</p>
<p>Google Translate</p>
<p>Google Earth</p>
<p>Google Lens</p>
</div>
</section>

JavaScript Accordion not working when i click on accordion heading text or chevron icon

I have built an accordion which I can add dynamically from an input and everything works fine except when I click on accordion heading text it doesn't work and also when I click on the chevron icon on the right side I get an error!! I am not sure why this happening. if I click on an empty space area it just works fine without any error. you can check the demo & code here on codepen -> https://codepen.io/tauhidul-islam/pen/eYZBzLY
Also here is some screenshot so you can understand. please let me understand what's happening and why. Thank you.
const addForm = document.querySelector(".add");
const list = document.querySelector(".section-list");
// Template Generator Function
const generateTemplate = (section) => {
let html = `
<div class="accordion">
<span>${section}</span>
<i class="fa fa-chevron-down"></i>
</div>
<div class="panel">
<span>Hey there you did it! :-)</span>
</div>
`;
list.innerHTML += html;
// accordion Selector
const accordion = document.querySelectorAll(".accordion");
// Show/Hide accordion Content on Click
for (i = 0; i < accordion.length; i++) {
accordion[i].addEventListener("click", (e) => {
let panel = e.target.nextElementSibling;
if (panel.classList.contains("panel")) {
panel.classList.toggle("active");
}
});
}
};
// Add Section
addForm.addEventListener("submit", (e) => {
e.preventDefault();
const section = addForm.add.value.trim();
if (section.length) {
generateTemplate(section);
addForm.reset();
}
});
.container {
width: 960px;
margin: auto;
}
.add-input {
padding: 15px;
border: 1px solid #dadada;
}
.add-btn {
background: white;
padding: 15px 25px;
margin-bottom: 10px;
border: 1px solid #dadada;
cursor: pointer;
}
/* Accordian Panel */
.accordion {
display: flex;
justify-content: space-between;
background: #03a9f4;
color: white;
padding: 15px;
box-shadow: 0px 0px 4px 0px #dadada;
cursor: pointer;
}
.panel {
display: none;
background-color: white;
padding: 15px;
}
.active {
display: block;
}
<div class="container">
<!-- Add Section -->
<form class="add">
<input type="text" name="add" class="add-input">
<button type="submit" class="add-btn">Add Section</button>
</form>
<!-- Section List -->
<div class="section-list"></div>
</div>
Because you are using e.target in the click event of the generated div, that will reference the template span when you click on the text and the div when you click on the blue bar, so .nextElementSibling won't always point to the same element. Instead, you want to always be calling .nextElementSibling on the div. This can be accomplished by using this.nextElementSibling, however because you are also using an arrow function, this binding won't correctly reference the element that received the event (the div), so if you change to using an anonymous function and this, it works.
const addForm = document.querySelector(".add");
const list = document.querySelector(".section-list");
// Template Generator Function
const generateTemplate = (section) => {
let html = `
<div class="accordion">
<span>${section}</span>
<i class="fa fa-chevron-down">^</i>
</div>
<div class="panel">
<span>Hey there you did it! :-)</span>
</div>
`;
list.innerHTML += html;
// accordion Selector
const accordion = document.querySelectorAll(".accordion");
// Show/Hide accordion Content on Click
for (i = 0; i < accordion.length; i++) {
// Use an anonymous function for the event listener so that
// "this" will bind to the element that recieved the event,
// which is the `div` in this case.
accordion[i].addEventListener("click", function(e) {
// We don't want to reference the element that triggered the event
// because that might be the span or the div and you won't always get
// the correct reference with .nextElementSibling. We always want to
// start from the div, which recieves the event.
let panel = this.nextElementSibling;
if (panel.classList.contains("panel")) {
panel.classList.toggle("active");
}
});
}
};
// Add Section
addForm.addEventListener("submit", (e) => {
e.preventDefault();
const section = addForm.add.value.trim();
if (section.length) {
generateTemplate(section);
addForm.reset();
}
});
.container {
width: 960px;
margin: auto;
}
.add-input {
padding: 15px;
border: 1px solid #dadada;
}
.add-btn {
background: white;
padding: 15px 25px;
margin-bottom: 10px;
border: 1px solid #dadada;
cursor: pointer;
}
/* Accordian Panel */
.accordion {
display: flex;
justify-content: space-between;
background: #03a9f4;
color: white;
padding: 15px;
box-shadow: 0px 0px 4px 0px #dadada;
cursor: pointer;
}
.panel {
display: none;
background-color: white;
padding: 15px;
}
.active {
display: block;
}
<div class="container">
<!-- Add Section -->
<form class="add">
<input type="text" name="add" class="add-input">
<button type="submit" class="add-btn">Add Section</button>
</form>
<!-- Section List -->
<div class="section-list"></div>
</div>
Without the loop for assigning the click handlers:
const addForm = document.querySelector(".add");
const list = document.querySelector(".section-list");
const expand = (element) => {
let panel = element.nextElementSibling;
if (panel.classList.contains("panel")) {
panel.classList.toggle("active");
}
};
// Template Generator Function
const getAccordionItem = (section) => {
let html = `
<div class="accordion" onclick="expand(this)">
<span>${section}</span>
<i class="fa fa-chevron-down"></i>
</div>
<div class="panel">
<span>Hey there you did it! :-)</span>
</div>
`;
return html;
};
// Add Section
addForm.addEventListener("submit", (e) => {
e.preventDefault();
const section = addForm.add.value.trim();
if (section.length) {
list.innerHTML += getAccordionItem(section);
addForm.reset();
}
});
body {
margin: 50px 0;
background-color: #f2f2f2;
font-family: Arial, Helvetica, sans-serif;
}
.container {
width: 960px;
margin: auto;
}
.add-input {
padding: 15px;
border: 1px solid #dadada;
}
.add-btn {
background: white;
padding: 15px 25px;
margin-bottom: 10px;
border: 1px solid #dadada;
cursor: pointer;
}
/* Accordian Panel */
.accordion {
display: flex;
justify-content: space-between;
background: #03a9f4;
color: white;
padding: 15px;
box-shadow: 0px 0px 4px 0px #dadada;
cursor: pointer;
}
.panel {
display: none;
background-color: white;
padding: 15px;
}
.active {
display: block;
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Accordian</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
<!-- Add Section -->
<form class="add">
<input type="text" name="add" class="add-input">
<button type="submit" class="add-btn">Add Section</button>
</form>
<!-- Section List -->
<div class="section-list"></div>
</div>
<script src="app.js"></script>
</body>
</html>

Javascript - Creating a form then adding a list inside the created form

Pretty simple, just attempting to create a new form on my HTML document then after creating the form immediately adding a list to the new form.
This is kind of what I have, there a bit of extra code but it should give a gist of what I'm attempting to achieve:
The code works for me with the exception of the line "var newmodelli = document.getElementById('modelstatline' + (count + 1));" because you can't get an ID by adding the count it seems. So I need an alternate way to achieve what I need instead of adding it like so.
<!DOCTYPE html>
<html>
<body>
<style>
div {
position: relative;
left: 50px;
width: 600px;
height: 300px;
border: 20px solid rgb(0, 255, 0);
background-color: black;
padding: 25px;
margin: 25px;
}
#a {
float: left;
display: block;
padding: 7px;
text-align: center;
font-size: 15px;
background-color: black;
color: rgb(0, 255, 0);
}
#b {
float: left;
display: block;
padding: 0px;
text-align: center;
font-size: 16px;
}
li {
display: block;
}
ul {
list-style-type: none;
margin: 0px;
padding: 0px;
}
option {
color: black;
font-size: 15px;
}
select {
color: black;
font-size: 15px;
width: 95px;
height: 23px;
text-align: center;
}
input {
width:25px;
text-align:center;
font-size: 15px;
color: black;
}
button {
width: 22px;
text-align: center;
}
</style>
<script>
var count = 1;
var newF = document.createElement('form');
newF.id = 'modelstatline' + (count + 1);
var newli = document.createElement('li');
newli.id = 'a';
var newInM = document.createElement('input');
newInM.type = 'text';
newInM.name = 'WS' + (count + 1);
function addModelstatline()
{
var newmodelform = document.getElementById('modelstatlinegrp');
// [this is where the error lies] var newmodelli = document.getElementById('modelstatline' + (count + 1));
if (count < 4)
{
newmodelform.appendChild(newF);
// [child of error] newmodelli.appendChild(newli);
// aModelstatline.appendChild(newInM);
count++;
}
else
{
alert('Too many');
}
}
</script>
<div>
<ul id="modelstatlinegrp">
<form id="modelstatline1">
<li id="a">Model Statline 1<br />
<button type="button" onclick="javascript: addModelstatline();">+</button>
<li id="a">M<br /><input type="text" name="M1"></li>
<li id="a">WS<br /><input style="width:14px" type="text" name="WS1"><b style="font-size:20px"> +</b></li>
<li id="a">BS<br /><input style="width:14px" type="text" name="BS1"><b style="font-size:20px"> +</b></li>
<li id="a">S<br /><input type="text" name="S1"></li>
<li id="a">T<br /><input type="text" name="T1"></li>
<li id="a">W<br /><input type="text" name="W1"></li>
<li id="a">A<br /><input type="text" name="A1"></li>
<li id="a">Ld<br /><input type="text" name="Ld1"></li>
<li id="a">Sv<br /><input style="width:14px" type="text" name="Sv1"><b style="font-size:20px">+</b></li>
</form>
</ul>
<br />
<br />
</div>
</body>
</html>
Yeah, not totally sure what you want but it something like this, I think.
let form = document.createElement('form');
form.id = 'modelstatline2';
document.querySelector('.container').appendChild(form);
for (let ix = 1; ix < 4; ix++) {
let li = document.createElement('li');
li.id = 'a';
let input = document.createElement('input');
input.type = 'text';
input.name = 'WS' + ix;
li.appendChild(input);
form.appendChild(li);
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" rel="stylesheet" />
<div class="container"></div>

Categories