I am writing code to be able to search multiple fields within cards that pull customer data from endpoints. The first function that searches customerID works fine, but I would also like to be able to search the other fields. But I am having trouble getting it recognize multiple fields.
How can I register multiple functions inside an onkeyup field and if I am selecting the forename field in querySelector properly?
My HTML code:
<div class="container pt-3 pb-3">
<div class="form-outline" id="customer-search-bar">
<input type="search" id="filter" class="form-control" placeholder="Enter customer ID" aria-label="Search" onkeyup="searchID() && searchForename();" >
</div>
<div class="card-lists" id="card-lists">
<div class="row">
{% for data in raw.CustomerDataEntries %}
<div class="card col-sm-12 mb-3" id="card-perim">
<div class="card-body" id="card-body">
<h5 class="card-title">Customer ID: {{ data.ExternalId }}</h5>
<h5 class="card-title" name="forename">Forename: {{ data.Fields.forename }}</h5>
<h5 class="card-title">Surname: {{ data.Fields.surname }}</h5>
<h5 class="card-title">Postcode: {{ data.Fields.post_code }}</h5>
<h5 class="card-title">Matches: {{ data.Matches }}</h5>
Details
Edit
</div>
</div>
{% endfor %}
</div>
</div>
</div>
My JavaScript code:
function searchID() {
const input = document.getElementById('filter').value.toUpperCase();
const cardContainer = document.getElementById('card-lists');
console.log(cardContainer);
const cards = cardContainer.getElementsByClassName('card-body');
console.log(cards);
for (let i = 0; 0 < cards.length; i++){
let title = cards[i].querySelector(".card-body h5.card-title");
if(title.innerText.toUpperCase().indexOf(input) > -1){
cards[i].style.display = "";
} else {
cards[i].style.display = "none";
}
}
}
function searchForename() {
const input = document.getElementById('filter').value.toUpperCase();
const cardContainer = document.getElementById('card-lists');
console.log(cardContainer);
const cards = cardContainer.getElementsByClassName('card-body');
console.log(cards);
for (let i = 0; 0 < cards.length; i++){
let title = cards[i].querySelector(".card-body h5[name='forename']");
if(title.innerText.toUpperCase().indexOf(input) > -1){
cards[i].style.display = "";
} else {
cards[i].style.display = "none";
}
}
}
The value within the onkeyup attribute follows JS syntax, so you should separate the statements with a semi-colon, ;.
<input type="search" id="filter" onkeyup="searchID(); searchForename();" class="form-control" placeholder="Enter customer ID" aria-label="Search" />
However using inline event handlers is not good practice. Modern best practice is to unobtrusively bind your event handlers. In native JS you can achieve this by using addEventListener(), like this:
let filter = document.querySelector('#filter');
filter.addEventListener('keyup', () => {
searchID();
searchForename();
});
function searchID() {
console.log('searchID');
}
function searchForename() {
console.log('searchForename');
}
<input type="search" id="filter" class="form-control" placeholder="Enter customer ID" aria-label="Search" />
The second function searchForename() still doesn't work for me though
The main reason it doesn't work is partly because it's a duplication of the work being done in searchId(), so anything displayed by searchId() will be hidden in searchForname().
In addition, because you set display state for every .card-item in the card. This means that if the last item does not contain the searched text the entire card is hidden - even if a previous card did contain the text. To fix this hide the card to start with, then display it only if a match is found.
Here's a working example with the search logic corrected - note that I commented out searchForename() in the keyup handler as it works on a subset of the same
elements as searchID, so its use there is redundant.
let filter = document.querySelector('#filter');
filter.addEventListener('keyup', () => {
searchID();
//searchForename();
});
function searchID() {
const input = filter.value.toUpperCase();
const cardContainer = document.querySelector('#card-lists');
const cards = cardContainer.querySelectorAll('.card-body');
cards.forEach(card => {
card.style.display = 'none';
card.querySelectorAll(".card-body h5.card-title").forEach(title => {
if (title.innerText.toUpperCase().indexOf(input) > -1) {
card.style.display = "block";
}
});
});
}
function searchForename() {
const input = filter.value.toUpperCase();
const cardContainer = document.querySelector('#card-lists');
const cards = cardContainer.querySelectorAll('.card-body');
cards.forEach(card => {
card.style.display = 'none';
card.querySelectorAll(".card-body h5.card-title.forename").forEach(title => {
if (title.innerText.toUpperCase().indexOf(input) > -1) {
card.style.display = "block";
}
});
});
}
<div class="container pt-3 pb-3">
<div class="form-outline" id="customer-search-bar">
<input type="search" id="filter" class="form-control" placeholder="Enter customer ID" aria-label="Search" />
</div>
<div class="card-lists" id="card-lists">
<div class="row">
<div class="card col-sm-12 mb-3">
<div class="card-body">
<h5 class="card-title">Customer ID: 123</h5>
<h5 class="card-title forename">Forename: Lorem</h5>
<h5 class="card-title">Surname: Ipsum</h5>
<h5 class="card-title">Postcode: 90210</h5>
<h5 class="card-title">Matches: Foobar</h5>
Details
Edit
</div>
</div>
<div class="card col-sm-12 mb-3">
<div class="card-body">
<h5 class="card-title">Customer ID: 123</h5>
<h5 class="card-title forename">Forename: Dolor</h5>
<h5 class="card-title">Surname: Sit</h5>
<h5 class="card-title">Postcode: 92893</h5>
<h5 class="card-title">Matches: Fizzbuzz</h5>
Details
Edit
</div>
</div>
</div>
</div>
</div>
My JS code:
One last thing - note that I removed the id attributes in the content you create in the loop. This is because repeated id are invalid, they must contain unique values within the DOM.
Related
The script is like this to show the data from firebase database:
const onChildAdd = (snap) => { $('#contacts').append(contactHtmlFromObject(snap.key, snap.val()));}
//prepare contact object's HTML
const contactHtmlFromObject = (key, contact) => `
<div class="card contact" style="width: 100%;" id="${key}">
<div class="card-body">
<button class="arrival btn btn-danger btn-xs" onclick="delete_user()">X</button>
<h5 class="card-title">User: ${contact.a2}</h5>
<h6 class="card-subtitle mb-2 text-muted">User1: ${contact.a3}</h6>
<p class="card-text" title="${contact.a1}">
Expiry:${contact.a1} - Vip:${contact.a4} - Status:${contact.a5}
</p>
<input type="text" class="form-control" id="kea" value="${contact.a7}">
</div>
</div>`;
const spanText = (textStr, textClasses) => { const classNames = textClasses.map(c => `text-${c}`).join(' ');
return `<span class="${classNames}">${textStr}</span>`;}
I also have a function:
function delete_user() { firebase.database().ref().child("contact/" + auth.uid + "/" + $('#kea').val()).remove();
alert('The user is deleted successfully!'); }
I use input text to show my child key -Kvhovcjoggiobhuoy something like this or whatever.
I'm trying to delete child key with function delete_user() when click the button.
Any way to correct it, or any other way to delete it?
I am trying to access a Bootstrap card element so that I can hide its display when I use a search function. Currently I access the element inside the card in my to hide that when I collapse the cards when I do my filter search.
The problem with that is that there is a lot of white space that gets gets created from the Bootstrap element which increases as you select the cards closer to the bottom. The pictures below depict the problem. I will show you my unsuccessful solutions below too.
Here is what the page looks like uncollapsed:
Here is what the page looks like collapsed:
This is my HTML code for the section:
<div class="container pt-3 pb-3">
<div class="form-outline" id="customer-search-bar">
<input type="search" id="filter" class="form-control" placeholder="Search" aria-label="Search" >
</div>
<div class="card-lists">
<div class="row">
{% for data in raw.CustomerDataEntries %}
<div class="card col-sm-12 mb-3" id="card-perim">
<div class="card-body">
<h5 class="card-title">Customer ID: {{ data.ExternalId }}</h5>
<h5 class="card-title">Forename: {{ data.Fields.forename }}</h5>
<h5 class="card-title">Surname: {{ data.Fields.surname }}</h5>
<h5 class="card-title">Postcode: {{ data.Fields.post_code }}</h5>
<h5 class="card-title">Matches: {{ data.Matches }}</h5>
Details
Edit
</div>
</div>
{% endfor %}
</div>
</div>
</div>
This is my JavaScript code for the section:
let filter = document.querySelector('#filter');
filter.addEventListener('keyup', () => {
searchID();
});
function searchID() {
const input = filter.value.toUpperCase();
const cardContainer = document.querySelector('.card-lists');
const cardPerim = cardContainer.querySelectorAll('.card col-sm-12 mb-13');
// const cardPerim = cardContainer.querySelectorAll('#card-perim');
const cards = cardContainer.querySelectorAll('.card-body');
cards.forEach(card => {
//this option works, but does not target bootstrap container
card.style.display = 'none';
//here I am trying to change the css property, this does not work
// cardPerim.style.display = 'none';
//here I am trying to change the display property through bootstrap class attributes, this does not work
// cardPerim.className = "d-none card col-sm-12 mb-3";
card.querySelectorAll(".card-body h5.card-title").forEach(title => {
if (title.innerText.toUpperCase().indexOf(input) > -1) {
//this option works, but does not target bootstrap container
card.style.display = "block";
//here I am trying to change the css property, this does not work
// cardPerim.style.display = "block";
//here I am trying to change the display property through bootstrap class attributes, this does not work
// cardPerim.className = "card col-sm-12 mb-3 ";
}
});
});
}
Note that I try to unsuccessfully access the Bootstrap "card col-sm-12 mb-13" through its class name and id "card-perim". I also change its display property through changing the CSS style and through changing its Bootstrap class attributes, both unsuccessful.
How can I do it?
For cardContainer.querySelectorAll('.card col-sm-12 mb-13'), please change to cardContainer.querySelectorAll('.card .col-sm-12 .mb-13') for the class ref.
After the change, it should work.
I have implemented a tricky solution to display some questions without using v-for loop. I need it to avoid that all the questions are rendered at the same time in a mobile android app, this will avoid scroll. The logics works fine but I've noticed that when the user select an answer and the next new question is rendered, the radio input to select the answer will be not resetted and the selected radio input will be equal to the previous choiced from the user. The problem will not occur if I use v-for to render the questions but as I wrote, this isn't what I want.
<div class="container-fluid bg-light vh-100" v-if="isLoaded">
<div class="row m-0">
<div class="col-12 card shadow p-0 mt-5">
<div class="card-header">
<h6 class="fw-bold">{{ questions[n].question }}</h6>
</div>
<div class="card-body">
<div class="form-check mb-3" v-for="(choice, index) in questions[n].choices" :key="index">
<input class="form-check-input" type="radio" :name="questions[n].questionIndex" :value="index" #change="checkAnswer(questions[n].questionIndex, index)" :disabled="answeredQuestions[n]">
<small class="form-check-label" for="">{{ index }}) {{ choice }}</small>
</div>
</div>
</div>
</div>
</div>
<div class="navbar bg-light fixed-bottom">
<div class="container-fluid">
<small :class="score">{{ currentScore }}</small>
</div>
</div>
export default {
name: 'Quiz',
data() {
return {
n: 0,
answeredQuestions: [],
currentScore: 0
}
},
mounted() {
front.on('questions', (data) => {
console.log(data)
this.$store.commit('quizQuestions', data);
this.$store.commit('contentLoaded', true);
});
front.on('checkResponse', (response) => {
console.log(response);
if( response.answerCheck ){
this.currentScore++;
}
this.showNext();
});
},
computed: {
isLoaded() {
return this.$store.getters.showLoader;
},
questions() {
return this.$store.getters.quiz;
},
score() {
return this.currentScore > 0 ? 'text-success' : 'text-muted';
}
},
methods: {
showPrevious() {
if( this.n !== 0 ){
this.n--
}
},
showNext() {
if( this.n < this.$store.getters.quiz.length ){
this.n++
}
},
checkAnswer(questionIndex, choice) {
this.answeredQuestions.push(true);
front.send('checkAnswer', {questionIndex: questionIndex, choice: choice});
}
}
}
I think that the problem is with the name attribute of the radio inputs but not sure of this. Any idea of how I can solve this?
Maybe you can try this. bind your questions[n].answer in your input value. Once you get a new question, if the answer haven't fill in before. It will auto become null.
<input class="form-check-input" type="radio" :name="questions[n].questionIndex" :value="questions[n].answer" #change="checkAnswer(questions[n].questionIndex, index)" :disabled="answeredQuestions[n]">
I am new to React.js and I was creating a notes takign website using React.js and Bootstrap. I was saving the notes data in local storage for simplicity but I will upgrade this app to save the notes data in Firebase. However, when I was trying to load the notes from local storage and showing them in the a div element of id 'notes' it gives error that: TypeError: Cannot set property 'innerHTML' of null
I want to run the load notes function after all the html or every thing else is loaded.
When I run this function using button click listener after all the HTML is laoded it works.
The code is:
import React from 'react';
import './Home.css';
function Home() {
const loadNotes = () => {
let notes = localStorage.getItem('notes');
let titles = localStorage.getItem('titles');
let notesObj;
let titlesObj;
if (notes == null) {
notesObj = [];
}
else {
notesObj = JSON.parse(notes);
}
if (titles == null) {
titlesObj = [];
}
else {
titlesObj = JSON.parse(titles);
}
let html = '';
notesObj.forEach(function (element, index) {
html += `<div class="noteCard my-2 mx-2 card" style="width: 18rem;" id = ${index}>
<div class="card-body">
<h5 class="card-title">Note</h5>
<p class="card-text">${element}</p>
<button id = ${index} onclick = 'deleteNote(this.id)' class="btn btn-primary"><i class="fa fa-trash" style="margin-right: 10px;"></i>Delete Note</button>
</div>
</div>`
});
titlesObj.forEach(function (er, i) {
html = html.replace('<h5 class="card-title">Note</h5>', `<h5 class="card-title">${er}</h5>`);
});
let notesElm = document.getElementById('notes');
if (notesObj.length != 0) {
notesElm.innerHTML = html;
}
else {
notesElm.innerHTML = `<h4>Nothing to show here.</h4>`;
}
console.log('Notes shown.')
}
const addNote = () => {
let addTxt = document.getElementById('addTxt');
let notes = localStorage.getItem('notes');
let addTitle = document.getElementById('addTitle');
let titles = localStorage.getItem('titles');
let notesObj;
let titlesObj;
if (notes == null) {
notesObj = [];
}
else {
notesObj = JSON.parse(notes);
}
if (titles == null) {
titlesObj = [];
}
else {
titlesObj = JSON.parse(titles);
}
notesObj.push(addTxt.value);
titlesObj.push(addTitle.value);
localStorage.setItem('notes', JSON.stringify(notesObj));
localStorage.setItem('titles', JSON.stringify(titlesObj));
addTxt.value = '';
addTitle.value = '';
loadNotes();
console.log("Note added.")
}
return (
<div className="home">
<style type="text/css">
{`
.btn {
margin-right: 10px;t
}
.home__mainTitle {
margin-top: 60px;
}
`}
</style>
<div class="container my-3">
<h1 class='home__mainTitle'>Welcome to Magic Notes</h1>
<div class="card">
<div class="card-body" id = 'editor'>
<h5 class="card-title">Add title</h5>
<div class="form-group">
<input className='home__formInput' type="text" class="form-control" id="addTitle" rows="3" placeholder="Title"></input>
</div>
<h5 class="card-title">Add notes</h5>
<div class="form-group">
<textarea class="form-control" id="addTxt" rows="3" placeholder="Notes"></textarea>
</div>
<button class="btn btn-primary" onClick={ addNote } id='addBtn'><i class="fa fa-plus-square"></i>Add Note</button>
<button class="btn btn-primary" id='clearAllBtn'><i class="fa fa-eraser"></i>Clear All</button>
</div>
</div>
<h1 className='home__notesTitle'>Your Notes</h1>
<hr/>
<div id="notes" class="row container-fluid">
{/* <!-- <div class="noteCard my-2 mx-2 card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Note 1</h5>
<p class="card-text"></p>
Delete Note
</div>
</div> --> */}
{ loadNotes() }
</div>
</div>
</div>
);
}
export default Home;
Thanks and sorry from any bad mistakes in code or description of problem.
By trying to access the DOM and set innerHTML directly, you're sort of fighting against some of the general principals of React.
In this specific case, it's failing because the div doesn't actually exist in the DOM when you first try to mutate it.
Take a look at this very partial refactor:
import React, { useEffect, useState } from 'react';
import './Home.css';
function Home() {
const [notes, setNotes] = useState([]);
const [titles, setTitles] = useState([]);
useEffect(() => {
setNotes(JSON.parse(localStorage.getItem('notes')) ?? []);
setTitles(JSON.parse(localStorage.getItem('titles')) ?? []);
},[]);
const addNote = () => {
let addTxt = document.getElementById('addTxt');
let addTitle = document.getElementById('addTitle');
let newTitles = [...titles, addTitle.value];
let newNotes = [...notes, addTxt.value];
localStorage.setItem('notes', JSON.stringify(newNotes));
localStorage.setItem('titles', JSON.stringify(newTitles));
setNotes(newNotes)
setTitles(newTitles)
addTxt.value = '';
addTitle.value = '';
console.log("Note added.")
}
return (
<div className="home">
<style type="text/css">
{`
.btn {
margin-right: 10px;t
}
.home__mainTitle {
margin-top: 60px;
}
`}
</style>
<div class="container my-3">
<h1 class='home__mainTitle'>Welcome to Magic Notes</h1>
<div class="card">
<div class="card-body" id = 'editor'>
<h5 class="card-title">Add title</h5>
<div class="form-group">
<input className='home__formInput' type="text" class="form-control" id="addTitle" rows="3" placeholder="Title"></input>
</div>
<h5 class="card-title">Add notes</h5>
<div class="form-group">
<textarea class="form-control" id="addTxt" rows="3" placeholder="Notes"></textarea>
</div>
<button class="btn btn-primary" onClick={ addNote } id='addBtn'><i class="fa fa-plus-square"></i>Add Note</button>
<button class="btn btn-primary" id='clearAllBtn'><i class="fa fa-eraser"></i>Clear All</button>
</div>
</div>
<h1 className='home__notesTitle'>Your Notes</h1>
<hr/>
<div id="notes" class="row container-fluid">
{notes.map((note,index) => {
return <div class="noteCard my-2 mx-2 card" style={{width: '18rem'}} id={index}>
<div class="card-body">
<h5 class="card-title">{titles[index]}</h5>
<p class="card-text">{note}</p>
<button id={index} onclick='deleteNote(this.id)'
class="btn btn-primary"><i class="fa fa-trash" style={{marginRight: "10px;"}}></i>Delete Note</button>
</div>
</div>
})}
{notes.length === 0 ? <h4>Nothing to show here.</h4> : null}
</div>
</div>
</div>
);
}
export default Home;
Note how I'm using useState to store the notes and titles. Documentation: https://reactjs.org/docs/hooks-state.html
The useEffect is called when the component is mounted and loads the data from localStorage. https://reactjs.org/docs/hooks-effect.html
Then, in the body of the render, instead of calling loadNotes and trying to mutate a DOM that doesn't exist yet, I just map the notes and titles into the rendered content.
Note that this is not a complete refactor yet. For example, you may want to add listeners to your text area to keep track of the content automatically rather than pulling the content with document.getElementById. Also, delete hasn't been implemented yet, the test for localStorage content in useEffect is pretty minimal, etc. But, it's enough to get you started.
This error occurs because loadNotes() is wrapped inside notes div. So, you can check if the notes div is created in first place with if condition.
if (notesElm) {
if (notesObj.length != 0) {
notesElm.innerHTML = html;
}
else {
notesElm.innerHTML = `<h4>Nothing to show here.</h4>`;
}
}
Below is the working code:
import React from 'react';
import './Home.css';
function Home() {
const loadNotes = () => {
let notes = localStorage.getItem('notes');
let titles = localStorage.getItem('titles');
let notesObj;
let titlesObj;
if (notes == null) {
notesObj = [];
}
else {
notesObj = JSON.parse(notes);
}
if (titles == null) {
titlesObj = [];
}
else {
titlesObj = JSON.parse(titles);
}
let html = '';
notesObj.forEach(function (element, index) {
html += `<div class="noteCard my-2 mx-2 card" style="width: 18rem;" id = ${index}>
<div class="card-body">
<h5 class="card-title">Note</h5>
<p class="card-text">${element}</p>
<button id = ${index} onclick = 'deleteNote(this.id)' class="btn btn-primary"><i class="fa fa-trash" style="margin-right: 10px;"></i>Delete Note</button>
</div>
</div>`
});
titlesObj.forEach(function (er, i) {
html = html.replace('<h5 class="card-title">Note</h5>', `<h5 class="card-title">${er}</h5>`);
});
let notesElm = document.getElementById('notes');
if (notesElm) {
if (notesObj.length != 0) {
notesElm.innerHTML = html;
}
else {
notesElm.innerHTML = `<h4>Nothing to show here.</h4>`;
}
}
console.log('Notes shown.')
}
const addNote = () => {
let addTxt = document.getElementById('addTxt');
let notes = localStorage.getItem('notes');
let addTitle = document.getElementById('addTitle');
let titles = localStorage.getItem('titles');
let notesObj;
let titlesObj;
if (notes == null) {
notesObj = [];
}
else {
notesObj = JSON.parse(notes);
}
if (titles == null) {
titlesObj = [];
}
else {
titlesObj = JSON.parse(titles);
}
notesObj.push(addTxt.value);
titlesObj.push(addTitle.value);
localStorage.setItem('notes', JSON.stringify(notesObj));
localStorage.setItem('titles', JSON.stringify(titlesObj));
addTxt.value = '';
addTitle.value = '';
loadNotes();
console.log("Note added.")
}
return (
<div className="home">
<style type="text/css">
{`
.btn {
margin-right: 10px;t
}
.home__mainTitle {
margin-top: 60px;
}
`}
</style>
<div class="container my-3">
<h1 class='home__mainTitle'>Welcome to Magic Notes</h1>
<div class="card">
<div class="card-body" id = 'editor'>
<h5 class="card-title">Add title</h5>
<div class="form-group">
<input className='home__formInput' type="text" class="form-control" id="addTitle" rows="3" placeholder="Title"></input>
</div>
<h5 class="card-title">Add notes</h5>
<div class="form-group">
<textarea class="form-control" id="addTxt" rows="3" placeholder="Notes"></textarea>
</div>
<button class="btn btn-primary" onClick={ addNote } id='addBtn'><i class="fa fa-plus-square"></i>Add Note</button>
<button class="btn btn-primary" id='clearAllBtn'><i class="fa fa-eraser"></i>Clear All</button>
</div>
</div>
<h1 className='home__notesTitle'>Your Notes</h1>
<hr/>
<div id="notes" class="row container-fluid">
{/* <!-- <div class="noteCard my-2 mx-2 card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Note 1</h5>
<p class="card-text"></p>
Delete Note
</div>
</div> --> */}
{ loadNotes() }
</div>
</div>
</div>
);
}
export default Home;
I have a simple HTML form which has an event listener binded to it and when you click on the button inside the form that has a class of 'booking__form__counter--increase' this should increase the input field value by 1. It calls a javascript function named 'increaseCounter()' I declare a variable that points to this value but when i try to use the variable to increment it, it doesn't work. If i use the methods in the variable directly it works? I am missing something simple here but i cannot work out what.
let bookingForm = document.querySelector('.booking__form');
bookingForm.addEventListener('click', function (e) {
let target = e.target;
let inputCounterValue = target.parentElement.firstElementChild.value;
let inputMaxCounterValue = target.parentElement.firstElementChild.dataset.maxCount;
let showCounterValue = target.parentElement.firstElementChild.nextElementSibling.textContent;
if (target.classList.contains('booking__form__counter--increase')) {
increaseCounter();
}
function increaseCounter() {
if (inputCounterValue === inputMaxCounterValue) {
return;
} else {
//does not update
inputCounterValue++;
showCounterValue = inputCounterValue;
//this does update
target.parentElement.firstElementChild.value++;
target.parentElement.firstElementChild.nextElementSibling.textContent = target.parentElement.firstElementChild.value;
}
}
});
<form class="booking__form">
<div class="container">
<div class="booking__form__group">
<div class="booking__form__section booking__form__section--arrival">
<div class="booking__form__control">
<label for="arrival">Arrival Date</label>
<div class="booking__form__counter">
<span class="booking__form__counter--value">0</span>
<div class="booking__form__counter--button booking__form__counter--increase">
<svg class="fal fa-chevron-up"></svg>
</div>
<div class="booking__form__counter--button booking__form__counter--decrease">
<svg class="fal fa-chevron-down"></svg>
</div>
</div>
</div>
</div>
<div class="booking__form__section booking__form__section--duration">
<div class="booking__form__control">
<label for="arrival">Nights</label>
<div class="booking__form__counter">
<input type="hidden" name="duration" value="1" data-max-count="21">
<span class="booking__form__counter--value">1</span>
<div class="booking__form__counter--button booking__form__counter--increase">
<svg class="fal fa-chevron-up"></svg>
</div>
<div class="booking__form__counter--button booking__form__counter--decrease">
<svg class="fal fa-chevron-down"></svg>
</div>
</div>
</div>
</div>
<div class="booking__form__section booking__form__section--adults">
<div class="booking__form__control" id="booking--adults">
<label for="arrival">Adults</label>
<div class="booking__form__counter">
<input type="hidden" name="adults" value="1" data-max-count="8">
<span class="booking__form__counter--value">1</span>
<div class="booking__form__counter--button booking__form__counter--increase">
<svg class="fal fa-chevron-up"></svg>
</div>
<div class="booking__form__counter--button booking__form__counter--decrease">
<svg class="fal fa-chevron-down"></svg>
</div>
</div>
</div>
</div>
<div class="booking__form__section booking__form__section--children">
<div class="booking__form__control" id="booking--children">
<label for="arrival">Children</label>
<div class="booking__form__counter">
<input type="hidden" name="children" value="0" data-max-count="5">
<span class="booking__form__counter--value">0</span>
<div class="booking__form__counter--button booking__form__counter--increase">
<svg class="fal fa-chevron-up"></svg>
</div>
<div class="booking__form__counter--button booking__form__counter--decrease">
<svg class="fal fa-chevron-down"></svg>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
UPDATED Javascript
I have had a play around and added my updated javascript below which now seems to be working ok. I removed the data attributes 'data-max-count' and just added in the 'max' attribute and changed the variable decelerations around.
let bookingForm = document.querySelector('.booking__form');
bookingForm.addEventListener('click', function (e) {
let target = e.target;
let input = target.parentElement.firstElementChild;
let displayValue = target.parentElement.firstElementChild.nextElementSibling;
if (target.classList.contains('booking__form__counter--increase')) {
increaseCounter();
} else if (target.classList.contains('booking__form__counter--decrease')) {
decreaseCounter();
}
function increaseCounter() {
if (input.value === input.max) {
return;
} else {
input.value++;
displayValue.textContent = input.value;
}
}
});
I re-wrote your js and it now works.
You had some issues with your selectors and the way you updated the values.
I associated the max-count with the hidden input you have there and read the data-max-count attribute value. If this is not present then the auto-increment doesn't work because I set the initial value of inputMaxCounterValue equal to 0.
Keep in mind that I only update what the user sees and not the input value.
let bookingForm = document.querySelector('.booking__form');
bookingForm.addEventListener('click', function (e) {
let target = e.target;
let parentElem = target.parentElement;
let inputCounterValue = 0;
let valueContainer = parentElem.querySelector('.booking__form__counter--value');
if (typeof valueContainer.textContent!=="undefined") {
inputCounterValue = parseInt(valueContainer.textContent,10);
}
if (target.classList.contains('booking__form__counter--increase')) {
increaseCounter(valueContainer);
}
function increaseCounter(element) {
let inputMaxCounterValue = 0;
let parentElem = target.parentElement;
if (typeof parentElem.querySelector('input')!=="undefined" && parentElem.querySelector('input')!==null) {
inputMaxCounterValue = parentElem.querySelector('input').getAttribute("data-max-count");
}
if (inputCounterValue === inputMaxCounterValue) {
return;
} else {
//does not update
inputCounterValue++;
showCounterValue = inputCounterValue;
//this does update
element.textContent = inputCounterValue;
}