I am Making a name searching app in javascript. I have created all list items dynamically in javascript.
And when I use charAt(0) the app is not working nothing is displaying I want to match the first chracter of input and first character of array
names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"]
let ulEl = document.getElementById("ul-el")
let searchInput = document.getElementById("searchInput");
function render(things) {
let listItems = ""
for (let i = 0; i < things.length; i++) {
listItems += `
<li class="names">
${things[i]}
</li>
`
}
ulEl.innerHTML = listItems
}
render(names)
searchInput.addEventListener("keyup", function(event) {
let searchQuery = event.target.value.toLowerCase().charAt(0);
let allNamesDOMCollection = document.getElementsByClassName('names');
console.log(allNamesDOMCollection.textContent)
console.log(allNamesDOMCollection.textContent)
for (let i = 0; i < allNamesDOMCollection.length; i++) {
let currentName = allNamesDOMCollection[i].textContent.toLowerCase().charAt(0);
if (currentName.includes(searchQuery)) {
allNamesDOMCollection[i].style.display = "block";
} else {
allNamesDOMCollection[i].style.display = "none";
}
}
})
<h1>Search Names</h1>
<div class="container">
<input type="text" id="searchInput" placeholder="Search">
</div>
<div class="container cont-modifier">
<ul id="ul-el"></ul>
</div>
The undefined
thing is because the HTMLCollection
returned by getElementsByClassName
has no textContent
property, but since that's just in your console.log
we can largely disregard that.
The main problem is that the first character of the list items is a whitespace character because of how you build the listItems
string, you need to trim()
the text content if you want the first character of the name. (Or change how you build listItems
.)
I also suggest using the input
event, not keyup
.
It seems odd that your search input allows multiple characters but you're only using the first, but you confirmed in a comment that's what you wanted, so I've left it that way — but see the end of the answer for how to do a full match if you prefer.
See ***
comments:
// *** Add a declaration
const names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"];
let ulEl = document.getElementById("ul-el");
let searchInput = document.getElementById("searchInput");
function render(things) {
let listItems = "";
for (let i = 0; i < things.length; i++) {
listItems += `
<li class="names">
${things[i]}
</li>
`;
}
ulEl.innerHTML = listItems;
}
render(names);
// *** Use `input`, not `keyup`
searchInput.addEventListener("input", function(event) {
let searchQuery = event.target.value.toLowerCase().charAt(0);
let allNamesDOMCollection = document.getElementsByClassName('names');
for (let i = 0; i < allNamesDOMCollection.length; i++) {
// *** Remove the whitespace before getting first character
let currentName = allNamesDOMCollection[i].textContent.trim().toLowerCase().charAt(0);
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^
if (currentName.includes(searchQuery)) {
allNamesDOMCollection[i].style.display = "block";
} else {
allNamesDOMCollection[i].style.display = "none";
}
}
});
<h1>Search Names</h1>
<div class="container">
<input type="text" id="searchInput" placeholder="Search">
</div>
<div class="container cont-modifier">
<ul id="ul-el"></ul>
</div>
Alternatively, change how you build listItems
:
function render(things) {
let listItems = "";
for (let i = 0; i < things.length; i++) {
listItems += `<li class="names">${things[i]}</li>`; // *** Remove whitespace around name
}
ulEl.innerHTML = listItems;
}
Live Copy:
// *** Add a declaration
const names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"];
let ulEl = document.getElementById("ul-el");
let searchInput = document.getElementById("searchInput");
function render(things) {
let listItems = ""
for (let i = 0; i < things.length; i++) {
listItems += `<li class="names">${things[i]}</li>`; // *** Remove whitespace around name
}
ulEl.innerHTML = listItems;
}
render(names);
// *** Use `input`, not `keyup`
searchInput.addEventListener("input", function(event) {
let searchQuery = event.target.value.toLowerCase().charAt(0);
let allNamesDOMCollection = document.getElementsByClassName('names');
for (let i = 0; i < allNamesDOMCollection.length; i++) {
let currentName = allNamesDOMCollection[i].textContent.toLowerCase().charAt(0);
if (currentName.includes(searchQuery)) {
allNamesDOMCollection[i].style.display = "block";
} else {
allNamesDOMCollection[i].style.display = "none";
}
}
});
<h1>Search Names</h1>
<div class="container">
<input type="text" id="searchInput" placeholder="Search">
</div>
<div class="container cont-modifier">
<ul id="ul-el"></ul>
</div>
A couple of other side notes. When deal with natural language text, in general it's best to:
Prefer toLocaleLowerCase()
(and toLocaleUpperCase()
) to their non-locale-specific versions.
Use normalize()
(in environments where it's supported) to normalize any sequences of combining characters, etc., so you aren't hit by false negatives because the same glyph (ç
, for instance) is written in different ways (ç
[the "c-cedilla" code point, U+00E7] and ç
[a c
followed by a cedilla/cédille combining mark (U+0327], for instance, which look almost the same but are not the same sequence of code points). If you don't normalize, for instance, and the user types in Franç
(for example) using the single code point, it won't find François
in the list if it's written with a c
and a combining mark:
let input = `Franç`;
let name = "François";
console.log(name.includes(input)); // false
input = input.normalize();
name = name.normalize();
console.log(name.includes(input)); // true
In a comment you asked what you'd change if you did want to check the entire input against the entire name. To do that, just remove the two .charAt(0)
calls, if you want to match anywhere in the name. If you only want to match at the start of the name, use startsWith
instead of includes
.
Here's the above, using:
includes
to match anywhere in the nametoLocaleLowerCase
and normalize
for-of
(ES2015+) rather than for
since it's a bit more convenientconst names = ["Ayesha", "Alina", "Bilal", "Kaira", "Per", "Henry", "Liba", "Rimsha"];
const ulEl = document.getElementById("ul-el");
const searchInput = document.getElementById("searchInput");
function render(things) {
let listItems = "";
for (const thing of things) {
listItems += `
<li class="names">
${thing}
</li>
`;
}
ulEl.innerHTML = listItems;
}
render(names);
function prepForCompare(str) {
str = str.trim().toLocaleLowerCase();
// `normalize` is newer than most of the rest of what we're using
if (str.normalize) {
str = str.normalize();
}
return str;
}
searchInput.addEventListener("input", function(event) {
const searchQuery = prepForCompare(event.target.value);
const allNamesDOMCollection = document.getElementsByClassName("names");
for (const li of allNamesDOMCollection) {
const currentName = prepForCompare(li.textContent);
li.classList.toggle("hidden", !currentName.includes(searchQuery));
}
});
.hidden {
display: none;
}
<h1>Search Names</h1>
<div class="container">
<input type="text" id="searchInput" placeholder="Search">
</div>
<div class="container cont-modifier">
<ul id="ul-el"></ul>
</div>