대소문자 구분:


《 CSS JavaScript HTML 》
Click to copy
    <!-- CSS Section Start -->
    <!-- CSS for Highlight -->   
    <style>
        .highlight {
            font-weight: bold;
            color: #333333;
            background-color: Yellow;
            padding: 3px 5px;
        }
        .current {
            outline: 2px solid crimson;
        }
    </style>

    <!-- CSS for Buttons and other decorations -->
<style>
    /* Input Field Styles */
    input[type="text"] {
        padding: 10px;
        border: 1px solid #ccc;
        border-radius: 5px;
        font-size: 16px;
        width: 300px;
        box-sizing: border-box;
        margin-right: 10px;
        transition: all 0.3s ease;
    }

    input[type="text"]:focus {
        border-color: #007bff;
        box-shadow: 0 0 8px rgba(0, 123, 255, 0.2);
        outline: none;
    }

    /* Button Style */
    button {
        padding: 10px 15px;
        border: 1px solid #007bff;
        background-color: #007bff;
        color: #fff;
        border-radius: 5px;
        cursor: pointer;
        font-size: 16px;
        transition: background-color 0.3s ease;
        margin-right: 5px;
    }

    button:hover {
        background-color: #0056b3;
    }

    button:focus {
        outline: none;
        box-shadow: 0 0 8px rgba(0, 123, 255, 0.3);
    }

    /* Radio Button Style */
    .case-sensitivity {
        margin-top: 15px;
        font-size: 14px;
    }

    .case-sensitivity label {
        margin-right: 15px;
        font-weight: normal;
    }

    .case-sensitivity input[type="radio"] {
        margin-right: 5px;
    }

    /* Mobile-friendly design */
    @media (max-width: 480px) {
        input[type="text"] {
            width: 100%;
            margin-bottom: 10px;
        }

        button {
            width: 100%;
            margin-bottom: 10px;
        }

        .case-sensitivity {
            text-align: left;
        }
    }
</style>
<!-- CSS Section End -->


<!-- JavaScript Section Start -->
<script>
    let highlights = [];            // Array to store highlighted elements
    let currentIndex = -1;          // Index of the current highlight position
    let lastSearchTerm = '';        // Variable to store the last searched term
    let searchTimer = null;         // Timer to delay search after input changes
    let isComposing = false;        // Variable to track input composition state
    let caseSensitive = false;      // Flag for case sensitivity (default is insensitive)

    /**
     * Function to escape special characters in a string
     * @param {string} string - The string to be escaped
     * @returns {string} - The escaped string
     */
    function escapeRegExp(string) {
        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }

    /**
     * Function to execute the search and highlight matching text
     * @param {string} searchTerm - The search term
     */
    function search(searchTerm) {
        if (!searchTerm || searchTerm === lastSearchTerm) return;

        lastSearchTerm = searchTerm;
        resetHighlights();

        searchTerm = escapeRegExp(searchTerm);

        const content = document.getElementById('content_to_search');
        searchAndHighlight(content, searchTerm);

        if (highlights.length > 0) {
            currentIndex = 0;
            scrollToHighlight(currentIndex);
        }

        document.getElementById('searchTerm').focus();
    }

    /**
     * Function to find and highlight matching text within a text node
     * @param {Node} element - The node to search within
     * @param {string} searchTerm - The search term
     */
    function searchAndHighlight(element, searchTerm) {
        if (element.nodeType === 3) { // Text node
            const regex = new RegExp(searchTerm, caseSensitive ? 'g' : 'gi');
            const matches = element.textContent.match(regex);
            if (matches) {
                const parent = element.parentNode;
                const fragment = document.createDocumentFragment();
                let lastIndex = 0;
                matches.forEach(match => {
                    const matchStart = element.textContent.indexOf(match, lastIndex);
                    const matchEnd = matchStart + match.length;

                    fragment.appendChild(document.createTextNode(element.textContent.slice(lastIndex, matchStart)));

                    const span = document.createElement('span');
                    span.className = 'highlight';
                    span.textContent = match;
                    fragment.appendChild(span);

                    lastIndex = matchEnd;
                    highlights.push(span);
                });

                fragment.appendChild(document.createTextNode(element.textContent.slice(lastIndex)));
                parent.replaceChild(fragment, element);
            }
        } else {
            Array.from(element.childNodes).forEach(child => searchAndHighlight(child, searchTerm));
        }
    }

    /**
     * Function to scroll to the current highlight element
     * @param {number} index - The index of the highlight element
     */
    function scrollToHighlight(index) {
        if (highlights.length > 0) {
            highlights.forEach(el => el.classList.remove('current'));
            highlights[index].classList.add('current');
            highlights[index].scrollIntoView({ behavior: 'smooth', block: 'center' });
        }
    }

    /**
     * Function to move to the next highlight
     */
    function nextHighlight() {
        if (highlights.length > 0) {
            currentIndex = (currentIndex + 1) % highlights.length;
            scrollToHighlight(currentIndex);
        }
    }

    /**
     * Function to move to the previous highlight
     */
    function previousHighlight() {
        if (highlights.length > 0) {
            currentIndex = (currentIndex - 1 + highlights.length) % highlights.length;
            scrollToHighlight(currentIndex);
        }
    }

    /**
     * Function to reset all highlights
     */
    function resetHighlights() {
        highlights.forEach(el => {
            el.outerHTML = el.innerHTML;
        });
        highlights = [];
        currentIndex = -1;
        lastSearchTerm = '';
    }

    /**
     * Function to reset the search and scroll to the input field
     */
    function resetSearch() {
        resetHighlights();
        document.getElementById('searchTerm').value = '';
        scrollToSearchInput();
    }

    /**
     * Function to scroll to the search input field
     */
    function scrollToSearchInput() {
        const searchInput = document.getElementById('searchTerm');
        searchInput.scrollIntoView({ behavior: 'smooth', block: 'center' });
        searchInput.focus(); // Also focus on the input field
    }

    /**
     * Function to handle the search process
     */
    function handleSearch() {
        const searchTerm = document.getElementById('searchTerm').value;

        // Reset and scroll to input field if search term is empty
        if (searchTerm === '') {
            resetSearch();
            return;
        }

        if (searchTerm && searchTerm !== lastSearchTerm) {
            search(searchTerm);

            if (highlights.length === 0) {
                scrollToSearchInput();
            }
        }
    }

    document.getElementById('searchTerm').addEventListener('keydown', function(e) {
        if (e.key === 'Enter') {
            e.preventDefault();
            const searchTerm = this.value;
            if (searchTerm) {
                if (currentIndex === -1) {
                    handleSearch();
                } else {
                    nextHighlight();
                }
            }
        }
    });

    document.getElementById('searchTerm').addEventListener('input', function() {
        if (searchTimer) clearTimeout(searchTimer);
        if (!isComposing) {
            searchTimer = setTimeout(handleSearch, 500);
        }
    });

    document.getElementById('searchTerm').addEventListener('compositionstart', function() {
        isComposing = true;
    });

    document.getElementById('searchTerm').addEventListener('compositionend', function() {
        isComposing = false;
        handleSearch();
    });

    document.querySelectorAll('input[name="caseOption"]').forEach(radio => {
        radio.addEventListener('change', handleCaseOptionChange);
    });

    /**
     * Function to handle changes in case sensitivity setting
     */
    function handleCaseOptionChange() {
        caseSensitive = document.querySelector('input[name="caseOption"]:checked').value === 'sensitive';
        search(document.getElementById('searchTerm').value);
    }
</script>
<!-- JavaScript Section End -->


<!-- HTML Section Start -->
<input type="text" id="searchTerm" placeholder="Enter search term and press Enter">
<button onclick="nextHighlight()">Next</button> <button onclick="previousHighlight()">Previous</button> <button onclick="resetSearch()">Reset</button>

<!-- Radio buttons to select case sensitivity -->
<!-- Hide if not needed; default is case insensitive -->
<div>
Case sensitivity: <label><input type="radio" name="caseOption" value="insensitive" checked>Insensitive</label> <label><input type="radio" name="caseOption" value="sensitive">Sensitive</label>
</div>

<!-- Highlight target range - start -->
<div id="content_to_search">
Body text to search for
</div>
<!-- Highlight target range - end -->
<!-- HTML Section End -->