使用纯 JS 和 JSON 自动完成 - 使 api 获取一次,然后让“输入”事件仅进行过滤

Posted

技术标签:

【中文标题】使用纯 JS 和 JSON 自动完成 - 使 api 获取一次,然后让“输入”事件仅进行过滤【英文标题】:Autocomplete with pure JS and JSON - Make api fetch once, and then have the "input" event merely do the filtering 【发布时间】:2022-01-18 22:48:49 【问题描述】:

我有一个纯 JS 和 JSON 的自动完成功能。

我的代码在每次击键时都会触发一个新的 ajax 请求,我不知道如何让我的 api 获取一次,然后让输入事件进行过滤。

我的代码运行良好,但如果我让它保持这样的状态,当我拥有大量 json 内容时,它需要数年时间才能加载。

states.json 数据仍然相同(并且处于自动完成状态),我应该加载一次而不是每次击键。对每个密钥的额外请求不是一个好主意。那么有人可以帮帮我吗?

<!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 class="container">
            <div class="search-wrapper">
                <input type="text" name="" autocomplete="off" id="search" placeholder="Search now">
                <ul class="match-list">
                    
                </ul>
            </div>
        </div>
    
        <script src="function.js"></script>
    </body>
    
    </html>

这是我的 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 class="match-list-element">
        <h4 class="">$match.name ($match.abbr)</h4>
        <span class="">$match.capital</span>
        <small class="">Lat:$match.lat/ Long: $match.long</small>
        </li>
        `
        ).join('');

        matchList.innerHTML = html;

     else 
        matchList.innerHTML = null;
    




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

【问题讨论】:

假设查询结果很小,请在window.onload 上进行提取。将所有结果写入 dom。在输入时,重写 dom,写一些&lt;li&gt; 和一些&lt;li hidden&gt; 你能给我一个更详细的例子,以便我更好地理解吗? 好的 - 速写,需要注意的是它没有经过测试甚至编译,当然也没有验证任何 OP 代码。 @danh 你能告诉我可能出了什么问题,这样我就可以获得全部知识了吗? 【参考方案1】:

只需更改这部分;

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

这样,当您的输入字段即将失去焦点时,这将被触发。例如,在输入外部单击后,这将起作用。你也可以搜索 onblur 方法

如果你想存储和使用它,你也可以查看 localStorage 和 sessionStorage 概念

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)
;

然后你可以检查你的函数,如果它没有获取

【讨论】:

显然,如果我将其更改为 "onFocus" ,它不再起作用,并且 "onBlur" 也不起作用。我的自动完成下拉菜单不会触发。 对不起,您可以尝试使用 focusout 而不是 onfocusout,或者您可以尝试仅在输入上提供 onfocusout 并分配函数以调用 @Marius 还是不行。我试过 onfocusout。 你能举个例子说明我应该如何添加我的 if 语句吗?我真的很困惑 我更新了我的答案,这样你只会获取一次。如果数据变化不大,这将是解决方案的正确答案。如果您可以共享 data/states.json 我也可以给出工作示例【参考方案2】:

如果“数据/状态”的总数在几百个,那么最简单的选择是在页面加载时进行查询,并有选择地设置/取消设置 DOM 中的隐藏道具。

只是一个草图,使用尽可能多的 OP 代码...

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' class="match-list-element">
        <h4 class="">$state.name ($state.abbr)</h4>
        <span class="">$state.capital</span>
        <small class="">Lat:$state.lat/ Long: $state.long</small>
        </li>
        `
     ).join('');

     matchList.innerHTML = html;


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

【讨论】:

我不得不改变 const 状态来让状态和它的工作原理!非常感谢! 啊——不错。已编辑。

以上是关于使用纯 JS 和 JSON 自动完成 - 使 api 获取一次,然后让“输入”事件仅进行过滤的主要内容,如果未能解决你的问题,请参考以下文章

Jquery UI 和 Bootstrap JS 冲突使自动完成停止工作

jQuery UI 自动完成多个远程(JSON、PHP、JS)

Json对象 Json纯文本 和js对象的区别:

如何使用 node.js 在服务器端使用纯 JavaScript 读取 JSON 文件?

jQuery自动完成JSON响应

使用来自外部 JSON 的 JQuery 的自动完成表单字段不起作用