<div class='container'>
<form>
<label for="autoComplete">Search Counties</label>
<input
id="autoComplete"
type="search"
dir="ltr"
spellcheck=false
tabindex="1"
>
<label for="tax-rate">Tax Rate:</label>
<output id="tax-rate"></output>
</form>
</div>
@import 'https://assets.codepen.io/3351103/__reset.css';
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500&display=swap');
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: 'Poppins', sans-serif;
font-size: 100%;
color: #222;
background-color: #fafafe;
padding: 2rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: center;
}
form {
display: flex;
align-items: center;
gap: 10px;
}
#tax-rate {
display: inline-block;
width: 150px;
line-height: 40px;
height: 40px;
padding-left: .75rem;
background-color: #f1f3f4;
border-radius: 8px;
}
.autoComplete_wrapper {
display: inline-block;
position: relative;
}
.autoComplete_wrapper > input {
width: 370px;
height: 40px;
padding-left: 20px;
font-size: 1rem;
color: rgba(123, 123, 123, 1);
border-radius: 8px;
border: 0;
outline: none;
background-color: #f1f3f4;
}
.autoComplete_wrapper > input::placeholder {
color: rgba(123, 123, 123, 0.5);
transition: all 0.3s ease;
}
.autoComplete_wrapper > ul {
position: absolute;
max-height: 226px;
overflow-y: scroll;
top: 100%;
left: 0;
right: 0;
padding: 0;
margin: 0.5rem 0 0 0;
border-radius: 0.6rem;
background-color: #fff;
box-shadow: 0 3px 6px rgba(149, 157, 165, 0.15);
border: 1px solid rgba(33, 33, 33, 0.07);
z-index: 1000;
outline: none;
}
.autoComplete_wrapper > ul[hidden],
.autoComplete_wrapper > ul:empty {
display: block;
opacity: 0;
transform: scale(0);
}
.autoComplete_wrapper > ul > li {
margin: 0.3rem;
padding: 0.3rem 0.5rem;
list-style: none;
text-align: left;
font-size: 1rem;
color: #212121;
transition: all 0.1s ease-in-out;
border-radius: 0.35rem;
background-color: rgba(255, 255, 255, 1);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: all 0.2s ease;
}
.autoComplete_wrapper > ul > li::selection {
color: rgba(#ffffff, 0);
background-color: rgba(#ffffff, 0);
}
.autoComplete_wrapper > ul > li:hover {
cursor: pointer;
background-color: rgba(123, 123, 123, 0.1);
}
.autoComplete_wrapper > ul > li mark {
background-color: transparent;
color: #d84e04;
font-weight: bold;
}
.autoComplete_wrapper > ul > li mark::selection {
color: rgba(#ffffff, 0);
background-color: rgba(#ffffff, 0);
}
.autoComplete_wrapper > ul > li[aria-selected="true"] {
background-color: rgba(123, 123, 123, 0.1);
}
@media only screen and (max-width: 600px) {
.autoComplete_wrapper > input {
width: 18rem;
}
}
View Compiled
// https://assets.codepen.io/3351103/counties-usa.json
// helper functions
/**
* pluck - takes an array of objects and returns
* each object's specified key value
* @example
* const people = [{name: 'Fred', age: 42}, {name: 'Barney', age: 40}]
* const names = pluck(people, 'name')
* console.log(names) // ['Fred', 'Barney']
*/
function pluck(collection, key) {
const length = collection.length;
const plucked = Array(length);
for (let i = 0; i < length; i++) {
plucked[i] = collection[i][key];
}
return plucked;
}
/**
* props - A higher order function which gets the value(s) of an object's key(s).
* If multiple keys are supplied, an array of values is returned.
* @example
* const obj = { a: 1, b: 2, c: 3 };
* const getC = prop('c');
* getC(obj); // returns 3
* const getAB = prop('a', 'b');
* getAB(obj); // returns [1, 2]
*/
function props(...keys) {
function fromObj (obj) {
// if only one key return the value
if (keys.length === 1) {
return obj?.[keys[0]];
}
// if more than one key return an array of values
const values = [];
for (const key of keys) {
values.push(obj?.[key]);
}
return values;
}
return fromObj;
};
// general function to fetch JSON
// see: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
async function fetchJson(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Response status: ${response.status}`);
}
return response.json();
} catch(error) {
console.error(error.message);
}
}
function getTaxRates(counties) {
// counties.map(props('county', 'taxRate')) will return an array of county and taxRate paired values
// e.g.
// [
// {"county":"Los Angeles","state":"California","population":10014009,"taxRate":1.000377},
// {"county":"Cook","state":"Illinois","population":5275541,"taxRate":1.005214}, ...
// ]
// → [["Los Angeles", 1.000377], ["Cook", 1.005214], ...]
const countiesAndTaxes = counties.map(props('county', 'taxRate'))
// Object.fromEntries(countiesAndTaxes) will convert those pairs to an object
// e.g.
// → {
// "Los Angeles": 1.000377,
// "Cook": 1.005214, ...
// }
const countyTaxRates = Object.fromEntries(countiesAndTaxes)
return countyTaxRates;
}
async function main() {
const counties = await fetchJson('https://assets.codepen.io/3351103/counties-usa.json')
const countyTaxRates = getTaxRates(counties)
const countyNames = pluck(counties, 'county')
const autoCompleteJS = new autoComplete({
selector: "#autoComplete", // input element
placeHolder: "Counties...",
data: {
src: countyNames, // our list of county names (array)
filter: (list) => {
// match starts of words 'lo' -> (Lo)s Angeles, St. (Lo)uis
const results = list.filter((item) => {
const inputValue = autoCompleteJS.input.value.toLowerCase();
// split the county names in the select options on space
// and check if words start with input
const itemValues = item.value.toLowerCase().split(' ');
if (itemValues.some((term) => term.startsWith(inputValue))) {
return item.value;
}
});
return results;
}
},
threshold: 2,
resultsList: { maxResults: 10 },
resultItem: { highlight: true, },
events: {
input: {
selection(event) {
const selection = event.detail.selection.value;
autoCompleteJS.input.value = selection;
// with a selected county, update the tax rate
document.querySelector('#tax-rate').textContent = countyTaxRates[selection];
}
},
}
});
// Clear taxrate, when search is cleared. Must be a better way to do this.
document.querySelector('#autoComplete').addEventListener('input', (event) => {
if (!event.target.value) {
document.querySelector('#tax-rate').textContent = ""
}
})
}
main()
This Pen doesn't use any external CSS resources.