On DAY-6 we made a Type Ahead feature where we've got a list of names of cities and states with their population in it. When we type something into the box, it's going to show all matching results containing that word and the words will also be highlighted.
This lesson was really very interesting and I would definitely like to use this in my future projects. I learned a lot of new things so let's dive right into it.
Lessons Learned
- First and foremost the data containing the city and state names along with their population would be coming from an external source - a cities.json which Wes obtained from github.
const endpoint =
"https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json";
So what we would be doing in this project is first we are going to fetch that data and then whenever someone types something we'll filter the array down to a subset of all the ones in which either the city/state names match.
First of all we'll need an empty array to put our data in
const cities = [];
Now we need to fetch our data for which we would be using fetch
API.
MDN says:
The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest, but the new API provides a more powerful and flexible feature set.
The fetch() method takes one mandatory argument, the path to the resource you want to fetch. It returns a Promise that resolves to the Response to that request — as soon as the server responds with headers — even if the server response is an HTTP error status.
In simple terms Wes mentioned that the data that comes back from our fetch, it doesn't know what kind of data it is just yet. Also since we know it's JSON a simple JSON.parse()
wouldn't work.
So to convert the raw data into JSON we can see in the console the promise that is returned by fetch()
contains a function .json()
but this also doesn't do the full work as it also returns a promise and after resolving it we finally get our data.
Another challenge is since our empty array is const
we can't simply put data into it. We could although simply change it to let
but it's always better to use const
. Wes showed at this point that a .push()
into the array would result in a nested array that is our main array will have only one element which would be the array containing our data. He also showed that if we simply add integers like [2,3,4]
into our exiting array they are simply added into the array and are not nested. So using this logic and to put elements into the array as individual elements we use Array spread operator.
const endpoint =
"https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json";
const cities = [];
fetch(endpoint)
.then((blob) => blob.json())
.then((data) => cities.push(...data));
- Next thing we'll do is when someone types into the box we'll run a function that will take this massive array & filter it down into a subset where we can then listen to it.
To find the word matches we create a findMatches()
function. Here the challenge is that inside the .match()
function we can't use variables and whatever people search for will be variable and for that we need to use RegExp
and then feed the result of the regular expression to .match()
.
function findMatches(wordToMatch, cities) {
return cities.filter((place) => {
// here we need to figure out if the city or state matches what was searched
const regex = new RegExp(wordToMatch, "gi");
return place.city.match(regex) || place.state.match(regex);
});
}
Here the flags in RegExp
used are gi where g is for glob al (look through entire string to match) and i for case-insensitive.
Now that we have found the matches we'll proceed to add the event listeners.
First we'll add a change
event listener to the box. But the change
event fires off only when you go off the input field, not whenever you type into and key up so to fire off an event on key up as well we'll another event listener keyup
.
const searchInput = document.querySelector(".search");
searchInput.addEventListener("change", displayMatches);
searchInput.addEventListener("keyup", displayMatches);
The whole displayed content on screen would be handles inside the displayMatches
function.
Inside this function this.value
contains whatever the user has typed into the box and so we pass this to our findMatches()
function.
const matchArray = findMatches(this.value, cities);
We'll use the content in the variable matchArray to generate the html content for our page. Here to make things pretty we would also highlight the text that has matched so for that we'll create another RegExp
and then use replace()
which is going to find whatever is matched in the regx and replace it with a span having class of hl that is highlight. Here are the contents of hl class
.hl {
background: #ffc600;
}
and here is the complete displayMatches() function
function displayMatches() {
const matchArray = findMatches(this.value, cities);
const html = matchArray
.map((place) => {
const regx = new RegExp(this.value, "gi");
const cityName = place.city.replace(
regx,
`<span class="hl">${this.value}</span>`
);
const stateName = place.state.replace(
regx,
`<span class="hl">${this.value}</span>`
);
return `
<li>
<span class="name">${cityName},${stateName}</span>
<span class="population">${numberWithCommas(place.population)}</span>
</li>
`;
})
.join("");
suggestions.innerHTML = html;
}
Finally to beautify things Wes used a function to add commas in the population.
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
and with this our project for the day was completed.
GitHub repo:
Blog on Day-5 of javascript30
Blog on Day-4 of javascript30
Blog on Day-3 of javascript30
Follow me on Twitter
Follow me on Linkedin
DEV Profile
You can also do the challenge at javascript30
Thanks WesBos to share this with us! 😊💖
Please comment and let me know your views
Top comments (2)
👌👌
👍