MarkdownPad2 自动生成目录原创

生成html时自动输出目录,完全离线,浏览时自动定位到当前目录并高亮显示:
image
image

点击查看代码
<style>
/* 目录容器样式 */
.folt_div {
  max-height: 700px;
  min-width: 250px;
  overflow: auto;
  border-radius: 8px;
  z-index: 9999;
  position: fixed;
  right: 10px;
  top: 10px;
  padding: 10px;
  background: #ffffff;
  font-family: 'Arial', sans-serif;
  font-size: 14px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  border: 1px solid #ccc;
}

/* 目录标题样式 */
.catalog-title {
  font-weight: bold;
  margin-bottom: 15px;
  font-size: 18px;
  text-align: center;
  color: #333;
  background-color: #f0f0f0;
  padding: 8px 0;
  border-radius: 6px;
}

/* 目录项样式 */
.catalog {
  cursor: pointer;
  margin-left: 0;
  padding: 3px 5px;
  border-radius: 4px;
  margin-bottom: 0px;
  transition: background-color 0.2s ease;
}

.catalog a {
  color: #333;
  text-decoration: none;
  font-weight: 600;
}

/* 鼠标悬停时 */
.catalog:hover {
  background-color: #e0e0e0;
}

.catalog a:hover {
  text-decoration: underline;
}

/* 高亮当前目录项 */
.catalog-active {
  background-color: #f0f0f0;
  color: white;
  font-weight: bold;
  border-radius: 4px;
}

/* 展开子目录时 */
.catalog-open > .catalog-children {
  display: block;
}

/* 子目录样式 */
.catalog-children {
  display: none;
  margin-left: 0px;
}

/* 目录项折叠图标 */
.toggle-icon {
  display: inline-block;
  width: 1.2em;
  cursor: pointer;
  font-weight: bold;
  margin-right: 0.0em;
  text-align: center;
}

/* 不同层级的目录颜色 */
.catalog-h1 a { color: #333333; }  /* 深灰 */
.catalog-h2 a { color: #666666; }  /* 中灰 */
.catalog-h3 a { color: #999999; }  /* 浅灰 */
.catalog-h4 a { color: #b3b3b3; }  /* 更浅的灰 */
.catalog-h5 a { color: #cccccc; }  /* 很浅的灰 */
.catalog-h6 a { color: #e0e0e0; }  /* 非常浅的灰 */

/* 滚动到特定位置时的目录高亮 */
.catalog a.active {
  color: #4CAF50;
  font-weight: bold;
}
</style>

<script>
document.addEventListener("DOMContentLoaded", function() {
    var container = document.createElement("div");
    container.id = "catalogs";
    container.className = "folt_div";

    var title = document.createElement("div");
    title.className = "catalog-title";
    title.innerText = "📑 目录";
    container.appendChild(title);

    document.body.insertBefore(container, document.body.firstChild);

    var headings = Array.from(document.querySelectorAll("h1,h2,h3,h4,h5,h6"));
    if (headings.length === 0) return;

    var idCount = 1;
    function createNode(el) {
        var tag = el.tagName.toLowerCase();
        var level = parseInt(tag[1]);
        var text = el.innerText.trim();
        var id = "catalog" + (idCount++);
        el.id = id;
        return { level: level, id: id, text: text, children: [], element: el };
    }

    var tree = [], stack = [];
    headings.forEach(el => {
        var node = createNode(el);
        while (stack.length > 0 && node.level <= stack[stack.length - 1].level) {
            stack.pop();
        }
        if (stack.length === 0) {
            tree.push(node);
        } else {
            stack[stack.length - 1].children.push(node);
        }
        stack.push(node);
    });

    function renderTree(nodes) {
        var list = document.createElement("div");
        nodes.forEach(node => {
            var item = document.createElement("div");
            item.className = "catalog catalog-h" + node.level;

            if (node.children.length > 0) {
                let toggle = document.createElement("span");
                toggle.className = "toggle-icon";
                toggle.innerText = "+";
                toggle.onclick = function(e) {
                    e.stopPropagation();
                    item.classList.toggle("catalog-open");
                    toggle.innerText = item.classList.contains("catalog-open") ? "−" : "+";
                };
                item.appendChild(toggle);
            } else {
                let spacer = document.createElement("span");
                spacer.className = "toggle-icon";
                spacer.innerHTML = "&nbsp;";
                item.appendChild(spacer);
            }

            let link = document.createElement("a");
            link.href = "javascript:void(0)";
            link.innerText = node.text;
            link.onclick = function(e) {
                e.preventDefault();
                node.element.scrollIntoView({ behavior: "smooth", block: "start" });
                history.replaceState(null, '', '#' + node.id);
                window.dispatchEvent(new Event("scroll")); // 更新高亮
            };
            item.appendChild(link);

            if (node.children.length > 0) {
                let childrenContainer = document.createElement("div");
                childrenContainer.className = "catalog-children";
                childrenContainer.appendChild(renderTree(node.children));
                item.appendChild(childrenContainer);
            }

            list.appendChild(item);
        });
        return list;
    }

    container.appendChild(renderTree(tree));

    var currentId = null;
    window.addEventListener("scroll", function() {
        let currentSection = null;
        let currentOffset = Number.MAX_VALUE;
        let currentLevel = -1;

        headings.forEach(function(heading) {
            let rect = heading.getBoundingClientRect();
            let topDistance = rect.top + window.scrollY;
            if (topDistance >= window.scrollY && topDistance < currentOffset) {
                currentSection = heading.id;
                currentOffset = topDistance;
                currentLevel = parseInt(heading.tagName[1]);
            }
        });

        if (currentSection && currentId !== currentSection) {
            currentId = currentSection;

            let allCatalogs = container.querySelectorAll(".catalog");
            allCatalogs.forEach(c => {
                let link = c.querySelector("a");
                if (link && link.innerText && c.classList.contains("catalog-h" + currentLevel)) {
                    if (link.href.endsWith("#" + currentSection)) {
                        c.classList.add("catalog-active");
                    } else {
                        c.classList.remove("catalog-active");
                    }
                } else {
                    c.classList.remove("catalog-active");
                }
            });

            let activeLink = document.querySelector(`a[href*="#${currentSection}"]`);
            if (activeLink) {
                let parent = activeLink.closest(".catalog-children");
                while (parent) {
                    parent.style.display = "block";
                    if (parent.previousElementSibling?.querySelector(".toggle-icon")) {
                        parent.previousElementSibling.querySelector(".toggle-icon").innerText = "−";
                    }
                    parent = parent.parentElement.closest(".catalog-children");
                }
            }
        }
    });

    document.querySelectorAll('.toggle-icon').forEach(function(icon) {
        icon.addEventListener('click', function(e) {
            let parentCatalog = e.target.closest('.catalog');
            let childrenContainer = parentCatalog.querySelector('.catalog-children');
            if (childrenContainer) {
                childrenContainer.style.display = childrenContainer.style.display === 'block' ? 'none' : 'block';
                e.target.innerText = childrenContainer.style.display === 'block' ? '−' : '+';
            }
            e.stopPropagation();
        });
    });

    window.dispatchEvent(new Event("scroll"));
});
</script>

posted @ 2025-04-08 13:42  Nlce2Cu  阅读(40)  评论(0)    收藏  举报