Home > Software design >  Autocomplete with pure JS and JSON - Make api fetch once, and then have the "input" event
Autocomplete with pure JS and JSON - Make api fetch once, and then have the "input" event

Time:12-16

I have an autocomplete with pure JS and JSON.

My code fires a new ajax request on every single keystroke and i don't know how to make my api fetch once, and then have the input event do the filtering.

My code works fine , but if i would leave it like that it would take years to load when i would have a large json content.

The states.json data is still the same (and is in autocomplete), i should load it once instead of every keystroke. Extra request for each key is not a good idea. So can someone please help me?

<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="css/main.css">
        <title>Test</title>
    </head>
    <body>
        <div >
            <div >
                <input type="text" name="" autocomplete="off" id="search" placeholder="Search now">
                <ul >
                    
                </ul>
            </div>
        </div>
    
        <script src="function.js"></script>
    </body>
    
    </html>

and here is my javascript

const search = document.querySelector("#search");
const matchList = document.querySelector(".match-list");

// search states.json and filter it
const searchStates = async searchText => {
    const res = await fetch('data/states.json');
    const states = await res.json();

    //Get matches to current text input
    let matches = states.filter(state => {
        const regex = new RegExp(`^${searchText}`, 'gi');
        return state.name.match(regex) || state.abbr.match(regex);
    });

    if (searchText.length === 0) {
        matches = [];
    };

    

    outputHtml(matches)
};

//show results in html
const outputHtml = matches => {
    if (matches.length > 0) {
        const html = matches.map(match =>`
        <li >
        <h4 >${match.name} (${match.abbr})</h4>
        <span >${match.capital}</span>
        <small >Lat:${match.lat}/ Long: ${match.long}</small>
        </li>
        `
        ).join('');

        matchList.innerHTML = html;

    } else {
        matchList.innerHTML = null;
    }

}


search.addEventListener('input', () => searchStates(search.value));

CodePudding user response:

If the total number of "data/states" is in the few hundreds, then the simplest option is to do the query on page load, and selectively set/unset hidden props in the DOM.

Just a sketch, using as much of the OP code as possible...

const search = document.querySelector("#search");
const matchList = document.querySelector(".match-list");
let states = []

// get the data once
window.onload = async () => {
    const res = await fetch('data/states.json');
    states = await res.json();
    searchStates('');
}

// run this on input. no fetching here
const searchStates = searchText => {
    for (let state of states) {
        const regex = new RegExp(`^${searchText}`, 'gi');
        // mark every state with a bool "match" whether it matches the current search text
        state.match = state.name.match(regex) || state.abbr.match(regex);
    }    
    outputHtml()
};

// put the whole list into the dom, but with some elements hidden
const outputHtml =() => {
    // note the interpolated string in "<li ...": hidden or not depending on state.match
    const html = states.map(state =>`
        <li ${state.match ? '' : 'hidden'} >
        <h4 >${state.name} (${state.abbr})</h4>
        <span >${state.capital}</span>
        <small >Lat:${state.lat}/ Long: ${state.long}</small>
        </li>
        `
     ).join('');

     matchList.innerHTML = html;
}

search.addEventListener('input', () => searchStates(search.value));

CodePudding user response:

Just change this part;

search.addEventListener('focusout', () => searchStates(search.value));

This way when your input field is about to lose focus this will be triggered. For example, after clicking outside of input this will work. You can also search for onblur method as well

If you want to store and use it, you can check localStorage and sessionStorage concepts as well

const searchStates = async searchText => {
const storedStates = JSON.parse(localStorage["states"]);
let states = storedStates;
if(!storedStates) {
const res = await fetch('data/states.json');
states = await res.json();
localStorage.setItem("states",JSON.stringfy(states));
}

//Get matches to current text input
let matches = states.filter(state => {
    const regex = new RegExp(`^${searchText}`, 'gi');
    return state.name.match(regex) || state.abbr.match(regex);
});

if (searchText.length === 0) {
    matches = [];
};



outputHtml(matches)
};

Then you can check in your function if its there don't fetch

  • Related