【个人博客定制】根据标签自动生成目录

洛辰 5月前 ⋅ 335 阅读

一、添加博客目录代码

博客目录我习惯放右边,效果达到滑动博文,目录标识会跟随表动,博文滑动超出固定目录范围后,目录变为浮动状态。代码如下:

<div class="panel panel-default widget sidebar-module article-module hide" style="top: 0;">
	<div class="panel-heading">
    	<h3 class="panel-title"><i class="fa fa-book fa-fw"></i><strong>本文目录</strong><i class="fa fa-close pull-right close-article-menu hide pointer"></i><small></small></h3>
	</div>
    <div id="article-menu">
        <ul class="list-unstyled"></ul>
    </div>
</div>

 

二、添加相关js

2.1 zhyd.core.js

主要对jquery做了继承,需要放到jquery.js的后面。

/**
 * MIT License
 *
 * Copyright (c) 2018 yadong.zhang
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * 项目核心Js库,主要包含核心工具类和 相关插件
 *
 * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
 * @website http://www.jsdblog.com
 * @version 1.0
 * @date 2017-09-02
 * @since 1.0
 */
(function ($) {
    $.extend({
        notification: {
            isSupported: function () {
                return 'Notification' in window;
            },
            isPermissionGranted: function () {
                return Notification.permission === 'granted';
            },
            requestPermission: function () {
                //验证浏览器是否支持Notification,如果不支持打印提示信息并返回
                if (!this.isSupported()) {
                    // console.log('当前浏览器不支持Notification API');
                    return;
                }
                //该方法将会询问用户是否允许显示通知,不能由页面调用( Notification.permission;
                    // console.log('permission: ' + permission);
                });
            },
            show: function (options) {
                // this.requestPermission();
                if (!this.isSupported()) {
                    // console.log('当前浏览器不支持Notification API');
                    return;
                }
                if (!this.isPermissionGranted()) {
                    // console.log('当前页面未被授权使用Notification通知');
                    return;
                }
                var op = $.extend({
                    title: "您有一条新消息",
                    icon: appConfig.staticPath + '/img/notification.png',
                    delay: 5000,
                    notification: null
                }, options);

                var n = new Notification(op.title, {
                    icon: op.icon,
                    noscreen: true,
                    body: op.notification
                });
                //  用户点击之后当天不再显示
                var hide = localStorage.getItem("hide");
                if (!hide || parseInt(hide) < new Date().getDate()) {
                    n.onshow = function () {
                        setTimeout(function () {
                            n.close();
                        }, op.delay);
                    };
                }


                n.onclick = function () {
                    // 打开视图?
                    n.close();
                    //  用户点击之后当天不再显示
                    localStorage.setItem("hide", new Date().getDate());
                };

                n.onerror = function () {
                    // console.log('产生错误');
                };

                n.onclose = function () {
                    // console.log('关闭通知窗口');
                };
            }
        }
    });
})(jQuery);
(function ($) {
    $.extend({
        websocket: {
            _this: null,
            _initialized: false,
            init: function (options) {
                if (!this.isSupported()) {
                    // console.error('Not support websocket');
                    return;
                }
                var op = $.extend({
                    callback: function () {
                    },
                    host: null,
                    reconnect: false
                }, options);
                if (!op.host) {
                    // console.error("初始化WebSocket失败,无效的请求地址");
                    return;
                }
                try {
                    this._this = new WebSocket(op.host);
                } catch (error) {
                    return;
                }
                this._initialized = true;
                //连接发生错误的回调方法
                this._this.onerror = function () {
                    // console.log("与服务器连接失败...");
                };

                //连接成功建立的回调方法
                this._this.onopen = function (event) {
                    // console.log("与服务器连接成功...");
                };

                //接收到消息的回调方法
                this._this.onmessage = function (event) {
                    // dwz.notification.show({notification: event.data});
                    op.callback(event.data);
                    // console.log("接收到服务器端推送的消息:" + event.data);
                };

                //连接关闭的回调方法
                this._this.onclose = function () {
                    $.websocket._initialized = false;
                    // console.log("已关闭当前链接");
                    if (op.reconnect) {
                        // 自动重连
                        setTimeout(function () {
                            $.websocket.open(op);
                        }, 5000);
                    }
                }
            },
            open: function (options) {
                var op = $.extend({
                    callback: function () {
                    },
                    host: null,
                    reconnect: false
                }, options);

                if (this._initialized) {
                    this.close();
                }
                this.init(options);
                //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
                window.onbeforeunload = function () {
                    // console.log("窗口关闭了");
                    $.websocket.close();
                }
            },
            isSupported: function () {
                return 'WebSocket' in window;
            },
            send: function (message) {
                if (!this._this) {
                    return;
                }
                this._this.send(message);
            },
            close: function () {
                if (!this._this) {
                    return;
                }
                this._this.close();
            }
        }
    });
})(jQuery);
(function ($) {
    // 覆盖jquery-confirm中的函数
    $.jqAlert = $.alert;
    $.jqConfirm = $.confirm;

    $.extend({
        alert: {
            info: function (content, delayTime, callback) {
                delayTime = delayTime ? "confirm|" + delayTime : "confirm|3000";
                $.jqAlert({
                    title: '友情提示',
                    content: content,
                    confirmButton: '关闭',
                    autoClose: delayTime,
                    confirm: callback
                });
            },
            error: function (content, delayTime, callback) {
                delayTime = delayTime ? "confirm|" + delayTime : "confirm|3000";
                $.jqAlert({
                    title: '警告',
                    content: content,
                    confirmButton: '关闭',
                    autoClose: delayTime,
                    confirm: callback
                });
            },
            confirm: function (content, confirmCallback, cancelCallback, delayTime) {
                delayTime = delayTime ? "cancel|" + delayTime : "cancel|5000";
                $.jqConfirm({
                    confirmButtonClass: 'btn-success',
                    cancelButtonClass: 'btn-default',
                    title: '友情提示',
                    content: content,
                    autoClose: delayTime,
                    confirmButton: '确定',
                    cancelButton: '关闭',
                    confirm: confirmCallback,
                    cancel: cancelCallback
                });
            },
            ajaxSuccessConfirm: function (json, callback) {
                if (json.status == 200) {
                    if(json.message){
                        $.alert.confirm(json.message, callback);
                    }
                } else {
                    if(json.message){
                        $.alert.error(json.message);
                    }
                }
            },
            ajaxSuccess: function (json) {
                if (json.status == 200) {
                    if(json.message){
                        $.alert.info(json.message);
                    }
                } else {
                    if(json.message){
                        $.alert.error(json.message);
                    }
                }
            },
            ajaxError: function () {
                $.alert.error("网络超时!");
            }
        }
    });
    $.extend({
        tool: {
            isEmpty: function (value) {
                return value == null || this.trim(value) == "";
            },
            isInteger: function () {
                return (new RegExp(/^\d+$/).test(this));
            },
            isNumber: function (value, element) {
                return (new RegExp(/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/).test(this));
            },
            trim: function (value) {
                if (value == null) {
                    return "";
                }
                return value.replace(/(^\s*)|(\s*$)|\r|\n/g, "");
            },
            html2Txt: function (value) {
                value = this.trim(value);
                value = value.replace(/(\n)/g, "");
                value = value.replace(/(\t)/g, "");
                value = value.replace(/(\r)/g, "");
                value = value.replace(/<\/?[^>]*>/g, "");
                value = value.replace(/\s*/g, "");
                return value;
            },
            currentPath: function () {
                debugger
                // 域
                var domain = document.domain;
                // 当前页
                var nowurl = document.URL;
                // 来源页
                var fromurl = document.referrer;

                return window.location.pathname;
            },
            getMeta: function (name) {
                var meta = document.getElementsByTagName('meta');
                var share_desc = '';
                for (var i in meta) {
                    if (typeof meta[i].name != "undefined" && meta[i].name.toLowerCase() == name.toLowerCase()) {
                        share_desc = meta[i].content;
                        break;
                    }
                }
                return share_desc;
            },
            random: function (min, max) {
                return Math.floor((Math.random() * max) + min);
            },
            parseFormSerialize: function (serialize) {
                var result = {};
                if (!serialize) {
                    return null;
                }
                var datas = serialize.split("&");
                for (var i = 0, len = datas.length; i < len; i++) {
                    var singleInfo = datas[i].split("=");
                    result[singleInfo[0]] = decodeURIComponent(singleInfo[1]);
                }
                return result;
            }
        }
    });
    /**
     * 扩展String方法
     */
    $.extend(String.prototype, {
        trim: function () {
            return this.replace(/(^\s*)|(\s*$)|\r|\n/g, "");
        },
        startsWith: function (pattern) {
            return this.indexOf(pattern) === 0;
        },
        endsWith: function (pattern) {
            var d = this.length - pattern.length;
            return d >= 0 && this.lastIndexOf(pattern) === d;
        },
        replaceSuffix: function (index) {
            return this.replace(/\[[0-9]+\]/, '[' + index + ']').replace('#index#', index);
        },
        getRequestURI: function () {
            var indexOf = this.indexOf("?");
            return (indexOf == -1) ? this : this.substr(0, indexOf);
        },
        getParams: function (encode) {
            var params = {},
                    indexOf = this.indexOf("?");
            if (indexOf != -1) {
                var str = this.substr(indexOf + 1),
                        strs = str.split("&");
                for (var i = 0; i < strs.length; i++) {
                    var item = strs[i].split("=");
                    var val = encode ? item[1].encodeParam() : item[1];
                    params[item[0]] = item.length > 1 ? val : '';
                }
            }
            return params;
        },
        encodeParam: function () {
            return encodeURIComponent(this);
        },
        replaceAll: function (os, ns) {
            return this.replace(new RegExp(os, "gm"), ns);
        },
        skipChar: function (ch) {
            if (!this || this.length === 0) {
                return '';
            }
            if (this.charAt(0) === ch) {
                return this.substring(1).skipChar(ch);
            }
            return this;
        },
        isPositiveInteger: function () {
            return (new RegExp(/^[1-9]\d*$/).test(this));
        },
        isInteger: function () {
            return (new RegExp(/^\d+$/).test(this));
        },
        isNumber: function (value, element) {
            return (new RegExp(/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/).test(this));
        },
        isValidPwd: function () {
            return (new RegExp(/^([_]|[a-zA-Z0-9]){6,32}$/).test(this));
        },
        isValidMail: function () {
            return (new RegExp(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/).test(this.trim()));
        },
        isSpaces: function () {
            for (var i = 0; i < this.length; i += 1) {
                var ch = this.charAt(i);
                if (ch != ' ' && ch != "\n" && ch != "\t" && ch != "\r") {
                    return false;
                }
            }
            return true;
        },
        isMobile: function () {
            return (new RegExp(/(^[0-9]{11,11}$)/).test(this));
        },
        isUrl: function () {
            return (new RegExp(/^[a-zA-z]+:\/\/([a-zA-Z0-9\-\.]+)([-\w .\/?%&=:]*)$/).test(this));
        },
        isExternalUrl: function () {
            return this.isUrl() && this.indexOf("://" + document.domain) == -1;
        },
        parseCurrency: function (num) {
            var numberValue = parseFloat(this);
            return parseFloat(numberValue.toFixed(num || 2));
        }
    });
    $.extend(Date.prototype, {
        format: function (format) {
            /*
             * eg:format="YYYY-MM-dd hh:mm:ss";
             */
            var o = {
                "M+": this.getMonth() + 1, // month
                "d+": this.getDate(), // day
                "h+": this.getHours(), // hour
                "m+": this.getMinutes(), // minute
                "s+": this.getSeconds(), // second
                "q+": Math.floor((this.getMonth() + 3) / 3), // quarter
                "S": this.getMilliseconds()
                // millisecond
            };

            if (/(y+)/.test(format)) {
                format = format.replace(RegExp.$1, (this.getFullYear() + "")
                        .substr(4 - RegExp.$1.length));
            }

            for (var k in o) {
                if (new RegExp("(" + k + ")").test(format)) {
                    format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k]
                            : ("00" + o[k]).substr(("" + o[k]).length));
                }
            }
            return format;
        }
    });

    /* 星星点赞 */
    $.extend({
        rating: {
            _rating: ".rating",
            init: function (count) {
                count = (count || 5) + 1;
                $(this._rating).each(function () {
                    var $this = $(this);
                    var starCount = parseInt($this.attr("data-star") || 0);
                    var star = '', active = '';
                    for (var i = 1; i < count; i++) {
                        if (starCount > 0 && i <= starCount) {
                            active = 'active';
                        }
                        star += '<i class="fa fa-star-o ' + active + '"></i> ';
                        active = '';
                    }
                    $this.html(star);

                    if ($this.hasClass("ignore")) {
                        return;
                    }

                    /* 绑定星星的鼠标滑过事件 */
                    var $i = $this.find("i");
                    $i.each(function () {
                        $(this).mouseover(function () {
                            $i.each(function () {
                                $(this).removeClass("active");
                            });
                            $(this).addClass("active");
                            var brotherNode = $(this).prevAll();
                            brotherNode.each(function () {
                                $(this).addClass("active");
                            });
                        });
                    });

                    /* 绑定星星的点击事件 */
                    $this.on('click', 'i', function () {
                        var brotherNode = $(this).prevAll();
                        var selectedCount = brotherNode.length + 1;
                    });
                });
            }
        }
    });

    /* 鼠标点击向上冒泡弹出提示动画 */
    $.extend({
        bubble: {
            _tip: ['JAVA', '百度', 'C++', 'R语言', '阿里', 'Python', 'HTML', '腾讯', 'CSS', 'JS', 'SpringBoot', '网易'],
            init: function () {
                var bubbleIndex = 0;
                $('body').click(function (e) {
                    bubbleIndex = bubbleIndex >= $.bubble._tip.length ? 0 : bubbleIndex;
                    if (!e.originalEvent) {
                        return;
                    }
                    var x = e.originalEvent.x || e.originalEvent.layerX || 0;
                    var y = e.originalEvent.y || e.originalEvent.layerY || 0;
                    var html = '<span style="position: fixed;z-index:9999;left: ' + x + 'px;top: ' + y + 'px;"></span>';
                    var $box = $(html).appendTo($(this));
                    $box.effectBubble({
                        y: -100,
                        className: 'thumb-bubble',
                        fontSize: 0.5,
                        content: '<i class="fa fa-smile-o"></i>' + $.bubble._tip[bubbleIndex]
                    });
                    setTimeout(function () {
                        $box.remove();
                    }, 1002);
                    bubbleIndex++;
                });
            },
            unbind: function (duration) {
                $("body").unbind('click');
                if (duration && !isNaN(duration = parseInt(duration))) {
                    setTimeout(function () {
                        $.bubble.init();
                    }, duration);
                }
            }
        }
    });
    /* 鼠标点击弹出提示动画 */

    $.fn.extend({
        // 翻牌效果
        flipCard: function (options) {
            var op = $.extend({}, options);
            return this.each(function () {
                var $box = $(this);
                $box.removeClass('animation-rotate-up').removeClass('animation-rotate-up-back');
                $box.addClass("animation-rotate-up");
                setTimeout(function () {
                    $box.addClass("animation-rotate-up-back");
                }, 500);
            });
        },

        // 文字向上冒泡
        effectBubble: function (options) {
            var op = $.extend({content: '+1', y: -100, duration: 1000, effectType: 'ease', className: '', fontSize: 2}, options);

            return this.each(function () {
                var $box = $(this), flyId = 'effect-fly-' + (new Date().getTime());

                var tpl = '<span id="#flyId#" class="effect-bubble-fly #class#" style="opacity: 1;top:#top#px;left:#left#px;font-size: #fontSize#rem;">#content#</span>';
                var html = tpl.replaceAll('#left#', 12).replaceAll('#top#', -8)
                        .replaceAll('#flyId#', flyId).replaceAll('#content#', op.content)
                        .replaceAll('#class#', op.className).replaceAll('#fontSize#', op.fontSize);

                var $fly = $(html).appendTo($box);
                $fly.fadeIn(100, "swing", function () {
                    $fly.animate({top: op.y, opacity: 0}, 100, function () {
                        $fly.fadeOut(op.duration, function () {
                            $fly.remove();
                        });
                    });
                });
            });
        }
    });
})(jQuery);

/* 返回顶部插件 */
(function ($) {
    $.fn.toTop = function (opt) {
        //variables
        var elem = this;
        var win = $(window);
        var doc = $('html, body');
        var options = opt;
        //如果没有配置自定义的参数,则使用默认
        if (!options) {
            options = $.extend({
                autohide: true,
                offset: 100,
                speed: 500,
                right: 15,
                bottom: 50
            }, opt);
        }
        elem.css({
            'position': 'fixed',
            'right': options.right,
            'bottom': options.bottom,
            'cursor': 'pointer'
        });
        if (options.autohide) {
            elem.css('display', 'none');
        }
        elem.click(function () {
            doc.animate({scrollTop: 0}, options.speed);
        });
        win.scroll(function () {
            var scrolling = win.scrollTop();
            if (options.autohide) {
                if (scrolling > options.offset) {
                    elem.fadeIn(options.speed);
                }
                else elem.fadeOut(options.speed);
            }
        });
    };
})(jQuery);
/* 返回顶部插件 end */

/* 文字滚动 start */
(function ($) {
    $.fn.textSlider = function (settings) {
        settings = jQuery.extend({
            speed: "normal",
            line: 2,
            timer: 3000
        }, settings);
        return this.each(function () {
            $.fn.textSlider.scllor($(this), settings);
        });
    };
    $.fn.textSlider.scllor = function ($this, settings) {
        var ul = $("ul:eq(0)", $this);
        var timerID;
        var li = ul.children();
        var liHight = $(li[0]).height();
        var upHeight = 0 - settings.line * liHight;//滚动的高度;
        var scrollUp = function () {
            ul.animate({marginTop: upHeight}, settings.speed, function () {
                for (i = 0; i < settings.line; i++) {
                    ul.find("li:first", $this).appendTo(ul);
                }
                ul.css({marginTop: 0});
            });
        };
        var autoPlay = function () {
            timerID = window.setInterval(scrollUp, settings.timer);
        };
        var autoStop = function () {
            window.clearInterval(timerID);
        };
        //事件绑定
        ul.hover(autoStop, autoPlay).mouseout();
    };
})(jQuery);
/* 文字滚动 end */
2.2 zhyd.js

目录生成的主要逻辑代码,主要功能是通过读取博文里的<h>标签根据<h>标签的编号规律生成目录。代码如下:

/**
 *
 * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
 * @website https://www.zhyd.me
 * @version 1.0
 * @date 2017-04-01
 * @since 1.0
 */
function initArticeMenu() {
    $(function () {
        debugger
        var $blogContentBody = $('.content-body');//博文dom
        var $h = $blogContentBody.find('h1, h2, h3, h4, h5, h6, h7');
        var $articleBox = $('.article-module');
        var $articleMenu = $('#article-menu');
        if ($blogContentBody && $blogContentBody[0]) {
            var padding = [0, 10, 20, 30, 40, 50, 60];
            var liDom, aDom;
            var hasMenu = false;

            // 目录导航定位
            $(window).scroll(function () {
                var top = $(document).scrollTop();
                var currentId = ""; //滚动条现在所在位置的item id
                $h.each(function () {
                    var $this = $(this);
                    //注意:$this.offset().top代表每一个item的顶部位置
                    if (top > $this.offset().top - 200) {
                        currentId = "#" + $this.prev().attr("id");
                    } else {
                        return false;
                    }
                });

                var currentLink = $articleMenu.find(".active");
                if (currentId && currentLink.attr("href") != currentId) {
                    currentLink.removeClass("active");
                    $articleMenu.find("[href=" + currentId + "]").parent().addClass("active");
                }
            });
            // 生成目录
            $h.each(function (index) {
                var $this = $(this);
                var tagText = $this.text().trim();
                var tagName = $this[0].tagName.toLowerCase();
                var tagIndex = parseInt(tagName.charAt(1)) - 1;

                $this.addClass("menu-title").before($('<span id="menu_' + index + '" class="menu-point"></span>'));
                aDom = '<a href="#menu_' + index + '" style="display:inline-block;"><i class="fa fa-caret-right"></i>' + '  ' + tagText + '</a>';
                liDom = '<li style="padding-left:' + padding[tagIndex] + 'px;">' + aDom + '</li>';
                $articleMenu.find("ul").append(liDom);
                hasMenu = true;
            });
            if (hasMenu) {
                $articleMenu.show();
                $articleBox.removeClass('hide');
                bindMenuScroll(), $(window).scroll(function () {
                    bindMenuScroll();
                });

                function bindMenuScroll() {
                    $articleBox.css('display', ($(window).width() < 768) ? 'none' : 'block');
                    if($(window).width() < 768) {
                        return false;
                    }
                    if ($.tool.currentPath().indexOf('/post/') !== -1) {
                        if ($(document).scrollTop() >= 200) {
                            if (!$articleBox.hasClass("fixed")) {
                                $articleBox.addClass('fixed').css({
                                    width: '25.2%'
                                }).animate({top: '85px'}, 100);
                                $('.close-article-menu').removeClass('hide');
                            }
                        } else {
                            $articleBox.removeClass('fixed').removeAttr('style');
                            $('.close-article-menu').addClass('hide');
                        }
                    }
                }

                $('.close-article-menu').click(function () {
                    $articleBox.addClass('hide');
                });
            }

        }
    });
}

$(function () {
    initArticeMenu();
});

三、效果图

3.1 初始固定效果

3.2 浮动效果


全部评论: 0

    我有话说: