본문 바로가기
업무 기록/WEB

자바스크립트 마크다운 검색창 만들기

by code2772 2023. 9. 8.

[ 목차 ]

    728x90
    반응형

    #in 에 마크다운을 작성하게 되면 #out에 htm로 자동 변환되는 상황에서 검색창을 입력하여 내가 원하는 단어를 찾는 간단한 Javascript이용하여 쉽게 만들 수 있을거라고 생각하였다. 하지만 만들면서 여러 문제점이 발생하였다. 화면에 표출되는 #out 부분의 제목 부분이 #, ##, ### 과 같이 해시를 사용하여 이상한 반복이 발생하는 경우도 있고 또는 #out 부분이 없어지거나 #in 마크다운 내용으로 바뀌는 경우등 많은 문제점들이 발생하였다.
     

    기본 HTML 틀

    <div id="in">
        <form>
            <textarea id="code" contenteditable="true">
    # News Title 1
    ## Subheading 1
    ### Subheading 1.1
    # Another topic
            </textarea>
        </form>
    </div>
    <div id="out" class="markdown-body"></div>
    <div id="tree"></div>
    <div id="search-box">
          <input type="text" id="search-input" placeholder="Search">
      </div>

    이전 자동 목록 만들기와 구조는 유사하다.
     

    전체코드

    <div id="in">
        <form>
            <textarea id="code" contenteditable="true">
    # News Title 1
    ## Subheading 1
    ### Subheading 1.1
    # Another topic
            </textarea>
        </form>
    </div>
    <div id="out" class="markdown-body"></div>
    <div id="tree"></div>
    
    <script>
        // Markdown을 HTML로 변환하는 함수
        function convertMarkdownToHTML() {
            var markdownText = document.getElementById('code').value;
            var converter = new showdown.Converter();
            var htmlText = converter.makeHtml(markdownText);
            return htmlText;
        }
    
        // #in의 textarea 내용이 변경될 때마다 호출
        document.getElementById('code').addEventListener('input', function () {
            var htmlText = convertMarkdownToHTML();
            document.getElementById('out').innerHTML = htmlText;
        });
    
        var lastSearchQuery = '';
        var lastMatchedIndices = [];
        var currentIndex = -1;
    
        function clearHighlight() {
            var outputDiv = document.getElementById('out');
            var highlightedHTML = outputDiv.innerHTML;
            highlightedHTML = highlightedHTML.replace(/<mark class="highlight">([^<]+)<\/mark>/gi, '$1');
            outputDiv.innerHTML = highlightedHTML;
        }
    
        function searchAndHighlight() {
            var query = document.getElementById('search-input').value;
            var outputDiv = document.getElementById('out');
            var textToSearch = outputDiv.innerText; // HTML 태그를 제외한 텍스트만 검색
            var regex = new RegExp(query, 'gi');
            var matches = Array.from(textToSearch.matchAll(regex));
    
            // 검색을 시작할 때 이전에 강조된 부분을 초기화
            clearHighlight();
    
            // 검색 결과 저장
            lastMatchedIndices = matches.map(function (match) {
                return match.index;
            });
    
            // 다음 일치 항목 찾기
            currentIndex = (currentIndex + 1) % lastMatchedIndices.length;
            var nextIndex = lastMatchedIndices[currentIndex];
    
            // 일치 항목을 강조 표시
            var highlightedHTML = outputDiv.innerHTML;
            if (nextIndex !== undefined) {
                var matchText = matches[currentIndex][0];
                highlightedHTML = highlightedHTML.replace(
                    new RegExp('(' + matchText + ')(?![^<]*>)', 'gi'),
                    '<mark class="highlight">$1</mark>'
                );
    
                // 스크롤 위치 조정
                var newScrollPosition = nextIndex;
                outputDiv.scrollTop = newScrollPosition;
            }
            outputDiv.innerHTML = highlightedHTML;
        }
    
        document.getElementById('search-input').addEventListener('keydown', function (event) {
            if (event.key === 'Enter') {
                event.preventDefault();
                searchAndHighlight();
            }
        });
    
        // 초기에 Markdown을 HTML로 변환하여 #out에 표시
        var initialHTML = convertMarkdownToHTML();
        document.getElementById('out').innerHTML = initialHTML;
    </script>

    ctrl + f 키랑 기능은 동일하다

     
     

    세부코드 설명

    Markdown을 HTML로 변환하는 함수:

    // Markdown을 HTML로 변환하는 함수
    function convertMarkdownToHTML() {
        var markdownText = document.getElementById('code').value;
        var converter = new showdown.Converter();
        var htmlText = converter.makeHtml(markdownText);
        return htmlText;
    }

    이 함수는 showdown 라이브러리를 사용하여 Markdown 형식의 텍스트를 HTML로 변환한다.
    #code 요소의 값을 가져와 Markdown을 HTML로 변환하고 결과를 반환한다.
     

    Markdown 입력 내용이 변경될 때 HTML 업데이트:

    // #in의 textarea 내용이 변경될 때마다 호출
    document.getElementById('code').addEventListener('input', function () {
        var htmlText = convertMarkdownToHTML();
        document.getElementById('out').innerHTML = htmlText;
    });

    #code 요소의 내용이 변경되면, convertMarkdownToHTML 함수를 호출하여 Markdown을 HTML로 변환하고, 변환된 HTML을 #out 요소에 업데이트한다.
     

    검색 및 강조 표시 기능:

    var lastSearchQuery = '';
    var lastMatchedIndices = [];
    var currentIndex = -1;
    
    // ...
    
    function searchAndHighlight() {
        // ...
    
        // 검색을 시작할 때 이전에 강조된 부분을 초기화
        clearHighlight();
    
        // 검색 결과 저장
        lastMatchedIndices = matches.map(function (match) {
            return match.index;
        });
    
        // ...
    
        // 일치 항목을 강조 표시
        var highlightedHTML = outputDiv.innerHTML;
        if (nextIndex !== undefined) {
            // ...
        }
        outputDiv.innerHTML = highlightedHTML;
    }
    
    // ...
    
    // 검색 입력 필드에서 Enter 키를 누를 때 처리
    document.getElementById('search-input').addEventListener('keydown', function (event) {
        if (event.key === 'Enter') {
            event.preventDefault();
            searchAndHighlight();
        }
    });

    이 부분은 사용자가 검색어를 입력하고 Enter 키를 누를 때 검색 및 강조 표시를 수행하는 기능이다.
    clearHighlight 함수를 사용하여 이전에 강조된 부분을 제거.
    검색 결과를 찾고, 일치하는 항목을 강조 표시한다.
    검색 결과는 lastSearchQuery, lastMatchedIndices, currentIndex 변수를 사용하여 관리된다.
     

    초기 변환 및 HTML 출력

    // 초기에 Markdown을 HTML로 변환하여 #out에 표시
    var initialHTML = convertMarkdownToHTML();
    document.getElementById('out').innerHTML = initialHTML;

    페이지가 로드될 때 초기 Markdown을 HTML로 변환하고, 변환된 HTML을 #out 요소에 출력한다.
     
     
    솔직히 그냥 구글에 돌아다니는 코드 복붙하면 10분이면 끝나겠다고 생각하였지만 바로 내용을 찾는게 아닌 숨겨진? 내용이나 좀 얽힌 내용이 많아 너무 많은 삽질을하였다....

    반응형