통합검색

Javascript

[Javascript] 검색창 자동완성 기능 구현

  • 2026.08.27 13:21:26




검색 키워드 입력 창에서 키워드 입력하는 경우
자동완성 기능 구현 (방향키, Enter 등도 구현)

[!]css[/!]  
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 30px;
background: #f5f7fb;
font-family: Arial, "Noto Sans KR", sans-serif;
color: #222;
}
.autocomplete_demo {
width: min(100%, 520px);
padding: 40px;
border-radius: 18px;
background: #fff;
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.1);
}
.autocomplete_demo h1 {
margin: 0 0 10px;
font-size: 26px;
}
.autocomplete_demo .summary {
margin: 0 0 25px;
color: #777;
font-size: 14px;
line-height: 1.6;
}
.autocomplete_wrap {
position: relative;
}
.autocomplete_input {
width: 100%;
height: 54px;
padding: 0 18px;
border: 1px solid #d9dde7;
border-radius: 10px;
outline: none;
font-size: 16px;
transition: border-color 0.2s, box-shadow 0.2s;
}
.autocomplete_input:focus {
border-color: #3867f0;
box-shadow: 0 0 0 4px rgba(56, 103, 240, 0.12);
}
.autocomplete_list {
position: absolute;
top: calc(100% + 8px);
left: 0;
z-index: 10;
display: none;
width: 100%;
max-height: 250px;
margin: 0;
padding: 7px;
overflow-y: auto;
list-style: none;
border: 1px solid #e3e6ee;
border-radius: 10px;
background: #fff;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.12);
}
.autocomplete_list.show {
display: block;
}
.autocomplete_item button {
width: 100%;
padding: 13px 12px;
border: 0;
border-radius: 7px;
background: transparent;
text-align: left;
font-size: 15px;
cursor: pointer;
}
.autocomplete_item button:hover,
.autocomplete_item.active button {
background: #f0f4ff;
color: #2453db;
}
.autocomplete_item strong {
color: #2453db;
}
.result_text {
min-height: 24px;
margin: 22px 0 0;
color: #555;
font-size: 14px;
}
</style>

[!]javascript[/!] 
<script>
const keywords = [
    '홈페이지 제작',
    '홈페이지 유지보수',
    '홈페이지 리뉴얼',
    '쇼핑몰 제작',
    '반응형 웹 제작',
    '웹 퍼블리싱',
    'PHP 개발',
    'JavaScript 개발',
    '기업 홈페이지',
    '병원 홈페이지'
];

const keyword_input = document.querySelector('#keyword');
const autocomplete_list = document.querySelector('#autocomplete_list');
const result_text = document.querySelector('#result_text');

let active_index = -1;
let filtered_keywords = [];

const highlight_keyword = (keyword, value) => {
    const index = keyword.toLowerCase().indexOf(value.toLowerCase());

    if (index === -1) return keyword;

    return (
        keyword.slice(0, index) +
        '<strong>' +
        keyword.slice(index, index + value.length) +
        '</strong>' +
        keyword.slice(index + value.length)
    );
};

const close_list = () => {
    autocomplete_list.classList.remove('show');
    autocomplete_list.innerHTML = '';
    active_index = -1;
};

const select_keyword = (keyword) => {
    keyword_input.value = keyword;
    result_text.textContent = '선택된 검색어: ' + keyword;
    close_list();
};

const render_list = () => {
    const value = keyword_input.value.trim();

    autocomplete_list.innerHTML = '';
    active_index = -1;

    if (!value) {
        close_list();
        return;
    }

    filtered_keywords = keywords.filter(keyword =>
        keyword.toLowerCase().includes(value.toLowerCase())
    );

    if (!filtered_keywords.length) {
        close_list();
        return;
    }

    filtered_keywords.forEach(keyword => {
        const item = document.createElement('li');
        const button = document.createElement('button');

        item.className = 'autocomplete_item';
        button.type = 'button';
        button.innerHTML = highlight_keyword(keyword, value);

        button.addEventListener('click', () => {
            select_keyword(keyword);
        });

        item.appendChild(button);
        autocomplete_list.appendChild(item);
    });

    autocomplete_list.classList.add('show');
};

const update_active_item = () => {
    const items = autocomplete_list.querySelectorAll('.autocomplete_item');

    items.forEach((item, index) => {
        item.classList.toggle('active', index === active_index);
    });

    if (items[active_index]) {
        items[active_index].scrollIntoView({
            block: 'nearest'
        });
    }
};

keyword_input.addEventListener('input', render_list);

keyword_input.addEventListener('keydown', event => {
    if (!filtered_keywords.length || !autocomplete_list.classList.contains('show')) {
        return;
    }

    if (event.key === 'ArrowDown') {
        event.preventDefault();
        active_index = (active_index + 1) % filtered_keywords.length;
        update_active_item();
    }

    if (event.key === 'ArrowUp') {
        event.preventDefault();
        active_index =
            active_index <= 0
                ? filtered_keywords.length - 1
                : active_index - 1;
        update_active_item();
    }

    if (event.key === 'Enter' && active_index >= 0) {
        event.preventDefault();
        select_keyword(filtered_keywords[active_index]);
    }

    if (event.key === 'Escape') {
        close_list();
    }
});

document.addEventListener('click', event => {
    if (!event.target.closest('.autocomplete_wrap')) {
        close_list();
    }
});
</script>


[!]html[/!] 
<div class="autocomplete_demo">
    <h1>자동완성 검색</h1>
    <p class="summary">
        홈페이지, 쇼핑몰, 반응형 등을 입력해보세요.<br>
        키보드 방향키와 Enter도 사용할 수 있습니다.
    </p>

    <div class="autocomplete_wrap">
        <input
            type="text"
            id="keyword"
            class="autocomplete_input"
            placeholder="검색어를 입력하세요"
            autocomplete="off"
        >
        <ul id="autocomplete_list" class="autocomplete_list"></ul>
    </div>

    <p id="result_text" class="result_text">선택된 검색어가 여기에 표시됩니다.</p>
</div>