본문 바로가기
개발 및 운영

티스토리 본문 중 태그 자동 링크 걸기

by Joseph.Lee 2025. 1. 15.

스킨 편집에서 html script 에 아래를 추가하면 된다.

지금 보고 있는 이 블로그에도 적용되어 있다.

    document.addEventListener("DOMContentLoaded", function() {
        // 태그와 링크를 가져오기
        async function fetchTagsFromHTML() {
            try {
                const response = await fetch("/tags"); // 태그 페이지
                const text = await response.text();

                // HTML 파싱
                const parser = new DOMParser();
                const doc = parser.parseFromString(text, "text/html");

                // 태그와 href 추출
                const tags = [...doc.querySelectorAll(".tags a")].map(a => ({
                    tag: a.textContent.trim(),
                    href: a.href.trim(),
                }));

                return tags;
            } catch (e) {
                console.error("tag fetch failed: ", e);
                return [];
            }
        }

        function escapeRegExp(string) {
                return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
        }

        // 태그 링크 적용하기
        async function applyTagLinks() {
            const tags = await fetchTagsFromHTML();
            if (tags.length === 0) return;

            const contentElement = $("article[id='content'] div.article-view")[0]; // 본문 영역
            if (!contentElement) return;

            const tagMap = {};
            tags.forEach(({ tag, href }) => {
                tagMap[tag.toLowerCase()] = href;
            });

            const walker = document.createTreeWalker(contentElement, NodeFilter.SHOW_TEXT);

            const nodes = [];

            let currentNode;
            while ((currentNode = walker.nextNode())) {
                nodes.push(currentNode);
            }
            for (const currentNode of nodes) {
                const parentElement = currentNode.parentNode;

                const parentTag = parentElement.tagName.toLowerCase();

                // 부모가 <a> 또는 다른 HTML 태그인 경우 건너뛰기
                if (parentElement.closest("a") || parentTag === 'a') {
                    continue;
                }

                // 텍스트 노드 처리
                let textContent = currentNode.nodeValue;

                try {
                    let updated = false;
                    const regex = new RegExp(`\\b(${tags.map(v => escapeRegExp(v.tag)).join('|')})\\b`, "gi");
                    textContent = textContent.replace(
                      regex,
                        function (matchedTag) {
                            updated = true;
                            const href = tagMap[matchedTag.toLowerCase()];
                            return `<a href="${href}" target="_blank" style="color: blue; text-decoration: underline;">${matchedTag}</a>`
                        }
                    );

                    if (updated) {
                      // 텍스트 노드 업데이트
                      const tempDiv = document.createElement("div");
                      tempDiv.innerHTML = textContent;

                      // 기존 텍스트 노드를 교체
                      while (tempDiv.firstChild) {
                          parentElement.insertBefore(tempDiv.firstChild, currentNode);
                      }
                      parentElement.removeChild(currentNode);
                    }
                } catch (e) {
                    console.error(e);
                }
            }
        }

        // 실행
        applyTagLinks();
    });
반응형

댓글