自定義web彈窗/層:簡易風格的msg與可拖放的dialog,生成博客園文章目錄彈窗

  前言

  作過web項目開發的人對layer彈層組件確定不陌生,做爲layUI的一個重要組件,使用簡單、接口參數豐富,功能健壯,深受廣大開發者的喜好,做爲一個熱(經)愛(常)工(劃)做(水),喜歡鑽研探索技術的程序員(狗),咱們本身來實現一個web彈窗/層,一窺layer的本源(/手動滑稽臉),進步,從模仿開始。css

  首先,模仿layer的部分功能,咱們先肯定下咱們要實現的功能:html

  msgjquery

  一、居中彈出、延時銷燬、自適應寬高、支持參數配置程序員

  dialogweb

  一、可拖動數組

  二、可最小化、最大化、關閉瀏覽器

  三、右下角可對窗口進行縮放app

  四、支持多個參數配置、擴展dom

 

  代碼編寫

  大部分的思路都在代碼註釋裏post

  css樣式

        /* web彈窗 */
        .tip-msg {
            background-color: rgba(61, 61, 61, 0.93);
            color: #ffffff;
            opacity: 0;
            max-width: 200px;
            position: fixed;
            text-align: center;
            line-height: 25px;
            border-radius: 30px;
            padding: 5px 15px;
            display: inline-block;
        }

        .tip-shade {
            z-index: 9999;
            background-color: rgb(0, 0, 0);
            opacity: 0.6;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }

        .tip-dialog {
            z-index: 10000;
            position: fixed;
            display: block;
            background: #e9e9e9;
            border-radius: 5px;
            opacity: 0;
            border: 1px solid #dad8d8;
            box-shadow: 0px 1px 20px 2px rgb(255, 221, 221);
        }

        .tip-title {
            cursor: move;
            padding: 5px;
            position: relative;
            height: 25px;
            border-bottom: 1px solid #dad8d8;
            user-select: none;
        }

        .tip-title-text {
            margin: 0;
            padding: 0;
            font-size: 15px;
        }

        .tip-title-btn {
            position: absolute;
            top: 5px;
            right: 5px;
        }

        .tip-content {
            padding: 8px;
            position: relative;
            word-break: break-all;
            font-size: 14px;
            overflow-x: hidden;
            overflow-y: auto;
        }

        .tip-resize {
            position: absolute;
            width: 15px;
            height: 15px;
            right: 0;
            bottom: 0;
            cursor: se-resize;
        }

  js

/**
 * 自定義web彈窗/層:簡易風格的msg與可拖放的dialog
 * 依賴jquery
 * 做者:https://www.cnblogs.com/huanzi-qch/
 * 出處:https://www.cnblogs.com/huanzi-qch/p/10082116.html
 */
var tip = {

    /**
     * 初始化
     */
    init: function () {
        var titleDiv = null;//標題元素
        var dialogDiv = null;//窗口元素
        var titleDown = false;//是否在標題元素按下鼠標
        var resizeDown = false;//是否在縮放元素按下鼠標
        var offset = {x: 0, y: 0};//鼠標按下時的座標系/計算後的座標
        /*
            使用 on() 方法添加的事件處理程序適用於當前及將來的元素(好比由腳本建立的新元素)。
            問題:事件綁定在div上出現div移動速度跟不上鼠標速度,致使鼠標移動太快時會脫離div,從而沒法觸發事件。
            解決:把事件綁定在document文檔上,不管鼠標在怎麼移動,始終是在文檔範圍以內。
        */
        //鼠標在標題元素按下
        $(document).on("mousedown", ".tip-title", function (e) {
            var event1 = e || window.event;
            titleDiv = $(this);
            dialogDiv = titleDiv.parent();
            titleDown = true;
            offset.x = e.clientX - parseFloat(dialogDiv.css("left"));
            offset.y = e.clientY - parseFloat(dialogDiv.css("top"));
        });
        //鼠標移動
        $(document).on("mousemove", function (e) {
            var event2 = e || window.event;
            var eveX = event2.clientX;             // 獲取鼠標相對於瀏覽器x軸的位置
            var eveY = event2.clientY;             // 獲取鼠標相對於瀏覽器Y軸的位置
            // var height = document.body.clientHeight;//表示HTML文檔所在窗口的當前高度;
            // var width = document.body.clientWidth;//表示HTML文檔所在窗口的當前寬度;
            var height = window.innerHeight;//瀏覽器窗口的內部高度;
            var width = window.innerWidth;//瀏覽器窗口的內部寬度;

            //在標題元素按下
            if (titleDown) {

                //處理滾動條
                if (tip.hasXScrollbar()) {
                    height = height - tip.getScrollbarWidth();
                }
                if (tip.hasYScrollbar()) {
                    width = width - tip.getScrollbarWidth();
                }

                //上邊
                var top = (eveY - offset.y);
                if (top <= 0) {
                    top = 0;
                }
                if (top >= (height - dialogDiv.height())) {
                    top = height - dialogDiv.height() - 5;
                }

                //左邊
                var left = (eveX - offset.x);
                if (left <= 0) {
                    left = 0;
                }
                if (left >= (width - dialogDiv.width())) {
                    left = width - dialogDiv.width() - 5;
                }
                dialogDiv.css({
                    "top": top + "px",
                    "left": left + "px"
                });
            }

            //在縮放元素按下
            if (resizeDown) {
                //避免undefined.XXX報錯
                dialogDiv[0].resize = dialogDiv[0].resize ? dialogDiv[0].resize : {};
                var newWidth = (dialogDiv[0].resize.width + (eveX - offset.x));
                if (dialogDiv[0].resize.initWidth >= newWidth) {
                    newWidth = dialogDiv[0].resize.initWidth;
                }
                var newHeight = (dialogDiv[0].resize.height + (eveY - offset.y));
                if (dialogDiv[0].resize.initHeight >= newHeight) {
                    newHeight = dialogDiv[0].resize.initHeight;
                }

                dialogDiv.css("width", newWidth + "px");
                dialogDiv.find(".tip-content").css("height", newHeight + "px");
            }
        });
        //鼠標彈起
        $(document).on("mouseup", function (e) {
            //清空對象
            titleDown = false;
            resizeDown = false;
            titleDiv = null;
            dialogDiv = null;
            offset = {x: 0, y: 0};
        });
        //阻止按鈕事件冒泡
        $(document).on("mousedown", ".tip-title-min,.tip-title-max,.tip-title-close", function (e) {
            e.stopPropagation();//阻止事件冒泡
        });
        //最小化
        $(document).on("click", ".tip-title-min", function (e) {
            // var height = document.body.clientHeight;//表示HTML文檔所在窗口的當前高度;
            // var width = document.body.clientWidth;//表示HTML文檔所在窗口的當前寬度;
            var height = window.innerHeight;//瀏覽器窗口的內部高度;
            var width = window.innerWidth;//瀏覽器窗口的內部寬度;
            var $parent = $(this).parents(".tip-dialog");
            //顯示瀏覽器滾動條
            document.body.parentNode.style.overflowY = "auto";

            //當前是否爲最大化
            if ($parent[0].isMax) {
                $parent[0].isMax = false;
                $parent.css({
                    "top": $parent[0].topMin,
                    "left": $parent[0].leftMin,
                    "height": $parent[0].heightMin,
                    "width": $parent[0].widthMin
                });
            }
            //當前是否爲最小化
            if (!$parent[0].isMin) {
                $parent[0].isMin = true;
                $parent[0].bottomMin = $parent.css("bottom");
                $parent[0].leftMin = $parent.css("left");
                $parent[0].heightMin = $parent.css("height");
                $parent[0].widthMin = $parent.css("width");
                $parent.css({
                    "top": "",
                    "bottom": "5px",
                    "left": 0,
                    "height": "30px",
                    "width": "95px"
                });
                $parent.find(".tip-title-text").css("display", "none");
                $parent.find(".tip-content").css("display", "none");
            } else {
                $parent[0].isMin = false;
                $parent.css({
                    "top": $parent[0].topMin,
                    "bottom": $parent[0].bottomMin,
                    "left": $parent[0].leftMin,
                    "height": $parent[0].heightMin,
                    "width": $parent[0].widthMin
                });
                $parent.find(".tip-title-text").css("display", "block");
                $parent.find(".tip-content").css("display", "block");
            }
        });
        //最大化
        $(document).on("click", ".tip-title-max", function (e) {
            // var height = document.body.clientHeight;//表示HTML文檔所在窗口的當前高度;
            // var width = document.body.clientWidth;//表示HTML文檔所在窗口的當前寬度;
            var height = window.innerHeight;//瀏覽器窗口的內部高度;
            var width = window.innerWidth;//瀏覽器窗口的內部寬度;
            var $parent = $(this).parents(".tip-dialog");
            //當前是否爲最小化
            if ($parent[0].isMin) {
                $parent[0].isMin = false;
                $parent.css({
                    "top": $parent[0].topMin,
                    "bottom": $parent[0].bottomMin,
                    "left": $parent[0].leftMin,
                    "height": $parent[0].heightMin,
                    "width": $parent[0].widthMin
                });
                $parent.find(".tip-title h2").css("display", "block");
            }
            //當前是否爲最大化
            if (!$parent[0].isMax) {
                //隱藏瀏覽器滾動條
                document.body.parentNode.style.overflowY = "hidden";
                $parent[0].isMax = true;
                $parent[0].topMin = $parent.css("top");
                $parent[0].leftMin = $parent.css("left");
                $parent[0].heightMin = $parent.css("height");
                $parent[0].widthMin = $parent.css("width");
                $parent.css({
                    "top": 0,
                    "left": 0,
                    "height": height - 5 + "px",
                    "width": width - 5 + "px"
                });
            } else {
                //顯示瀏覽器滾動條
                document.body.parentNode.style.overflowY = "auto";
                $parent[0].isMax = false;
                $parent.css({
                    "top": $parent[0].topMin,
                    "left": $parent[0].leftMin,
                    "height": $parent[0].heightMin,
                    "width": $parent[0].widthMin
                });
            }
        });
        //縮放
        $(document).on("mousedown", ".tip-resize", function (e) {
            var event1 = e || window.event;
            dialogDiv = $(this).parent();
            resizeDown = true;
            offset.x = e.clientX;
            offset.y = e.clientY;
            //點擊時的寬高
            dialogDiv[0].resize.width = dialogDiv.width();
            dialogDiv[0].resize.height = dialogDiv.find(".tip-content").height();
        });
        //關閉
        $(document).on("click", ".tip-title-close", function (e) {
            $(this).parents(".tip-dialog").parent().remove();
            //顯示瀏覽器滾動條
            document.body.parentNode.style.overflowY = "auto";
        });
        //點擊窗口優先顯示
        $(document).on("click", ".tip-dialog", function (e) {
            $(".tip-dialog").css("z-index","9999");
            $(this).css("z-index","10000");
        });
    },

    /**
     * 是否存在X軸方向滾動條
     */
    hasXScrollbar: function () {
        return document.body.scrollWidth > (window.innerWidth || document.documentElement.clientWidth);
    },

    /**
     * 是否存在Y軸方向滾動條
     */
    hasYScrollbar: function () {
        return document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight);
    },

    /**
     * 計算滾動條的寬度
     */
    getScrollbarWidth: function () {
        /*
            思路:生成一個帶滾動條的div,分析獲得滾動條長度,而後過河拆橋
         */
        var scrollDiv = document.createElement("div");
        scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;';
        document.body.appendChild(scrollDiv);
        var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
        document.body.removeChild(scrollDiv);

        return scrollbarWidth;

    },

    /**
     * tip提示
     * tip.msg("哈哈哈哈哈");
     * tip.msg({text:"哈哈哈哈哈",time:5000});
     */
    msg: function (setting) {
        var time = setting.time || 2000; // 顯示時間(毫秒) 默認延遲2秒關閉
        var text = setting.text || setting; // 文本內容

        //組裝HTML
        var tip = "<div class='tip tip-msg'>"
            + text +
            "</div>";

        //刪除舊tip
        $(".tip-msg").remove();

        //添加到body
        $("body").append(tip);

        //獲取jq對象
        var $tip = $(".tip-msg");

        //動畫過渡
        $tip.animate({opacity: 1}, 500);

        //計算位置瀏覽器窗口上下、左右居中
        // var height = document.body.clientHeight;//表示HTML文檔所在窗口的當前高度;
        var width = document.body.clientWidth;//表示HTML文檔所在窗口的當前寬度;
        var height = window.innerHeight;//瀏覽器窗口的內部高度;
        // var width = window.innerWidth;//瀏覽器窗口的內部寬度;
        width = ((width / 2) - ($tip.css("width").replace("px", "") / 2)) / width;
        height = ((height / 2) - ($tip.css("height").replace("px", "") / 2)) / height;
        $tip.css({
            "top": (height * 100) + "%",
            "left": (width * 100) + "%"
        });

        //延遲刪除
        setTimeout(function () {
            //動畫過渡
            $tip.animate({opacity: 0}, 500, function () {
                $tip.remove();
            });
        }, time);
    },

    /**
         可拖放窗口
         tip.dialog({title:"測試彈窗標題",content:"測試彈窗內容"});
         tip.dialog({
            title:"測試彈窗標題",
            class:"myClassName",
            content:"<h1>測試彈窗內容</h1>",
            offset: ['100px', '50px'],
            area:["200px","100px"],
            shade:0,
            callBack:function(){
                console.log('彈窗已加載完畢');
            },
            closeCallBack:function(){
                console.log('你點擊了關閉按鈕');
            }
         });
     */
    dialog: function (setting) {
        var title = setting.title || "這裏是標題"; // 標題
        var clazz = setting.class || ""; // class
        var content = setting.content || "這裏是內容"; // 內容
        var area = setting.area; // 寬高
        var offset = setting.offset || "auto"; // 位置 上、左
        var shade = setting.shade !== undefined ? setting.shade : 0.7;//遮陰 爲0時無遮陰對象

        //組裝HTML
        var tip = "<div>\n" +
            "    <!-- 遮陰層 -->\n" +
            "    <div class=\"tip tip-shade\"></div>\n" +
            "    <!-- 主體 -->\n" +
            "    <div class=\"tip tip-dialog " + clazz + "\">\n" +
            "        <!-- 標題 -->\n" +
            "        <div class=\"tip tip-title\">\n" +
            "            <h2 class=\"tip tip-title-text\"></h2>\n" +
            "            <div class=\"tip tip-title-btn\">\n" +
            "                <button class=\"tip tip-title-min\" title=\"最小化\">--</button>\n" +
            "                <button class=\"tip tip-title-max\" title=\"最大化\">O</button>\n" +
            "                <button class=\"tip tip-title-close\" title=\"關閉\">X</button>\n" +
            "            </div>\n" +
            "        </div>\n" +
            "        <!-- 窗口內容 -->\n" +
            "        <div class=\"tip tip-content\"></div>\n" +
            "        <!-- 右下角改變窗口大小 -->\n" +
            "        <div class=\"tip tip-resize\"></div>\n" +
            "    </div>\n" +
            "</div>";

        var $tip = $(tip);

        //添加到body
        $("body").append($tip);

        //設置遮陰
        $tip.find(".tip-shade").css("opacity", shade);
        if (shade === 0) {
            $tip.find(".tip-shade").css({
                "width": "0",
                "height": "0"
            });
        }

        //獲取dialog對象
        $tip = $tip.find(".tip-dialog");

        //標題
        $tip.find(".tip-title-text").html(title);

        //內容
        $tip.find(".tip-content").append(content);

        //設置初始寬高
        if (area) {
            $tip.css({
                "width": area[0],
            });
            $tip.find(".tip-content").css({
                "height": area[1]
            });
        }

        //動畫過渡
        $tip.animate({opacity: 1}, 500);

        //計算位置瀏覽器窗口上下、左右居中
        if (offset === "auto") {
            // var height = document.body.clientHeight;//表示HTML文檔所在窗口的當前高度;
            var width = document.body.clientWidth;//表示HTML文檔所在窗口的當前寬度;
            var height = window.innerHeight;//瀏覽器窗口的內部高度;
            // var width = window.innerWidth;//瀏覽器窗口的內部寬度;
            width = ((width / 2) - ($tip.css("width").replace("px", "") / 2)) / width;
            height = ((height / 2) - ($tip.css("height").replace("px", "") / 2)) / height;
            $tip.css({
                "top": (height * 100) + "%",
                "left": (width * 100) + "%"
            });
        } else if (Array.isArray(offset)) {
            $tip.css({
                "top": offset[0],
                "left": offset[1]
            });
        }

        //初始值寬高
        //避免undefined.XXX報錯
        $tip[0].resize = $tip[0].resize ? $tip[0].resize : {};
        $tip[0].resize.initWidth = $tip.width();
        $tip[0].resize.initHeight = $tip.find(".tip-content").height();

        //綁定關閉按鈕回調
        if(setting.closeCallBack){
            $(".tip-title-close").click(function (e) {
                setting.closeCallBack();
            });
        }

        //執行回調
        setting.callBack && setting.callBack();
    },

    //生成目錄彈窗,錨點信息數組
    navCategoryAnchor : [],

    /**
     * 生成目錄彈窗,支持到二級目錄
     * {
     *     list1:$('#cnblogs_post_body h2'),//目錄的一級標題集合(如何找到一級目錄)
     *     list2:"$list1.nextAll('h3')",//目錄的二級標題集合,(如何從每一個一級目錄節點$list1下面找到二級目錄)
     *     offset: ['40%', '10%'],//彈窗位置
     *    area:["156px","250px"]//彈窗大小
     * }
     */
    generateContentList : function(setting){
        setting.offset ? setting.offset : setting.offset = ['40%', '10%'];//彈窗位置
        setting.area ? setting.area : setting.area = ["156px","250px"];//彈窗大小

        //點擊章節,滾動帶動畫效果
        $("body").on("click","#navCategory a",function() {
            $("html, body").animate({
                scrollTop: $($(this).attr("href")).offset().top - 100 + "px"
            }, 800);
            return false;
        });

        //監聽鼠標滾動事件
        window.addEventListener('scroll', function () {
            //無需頻繁的進行遍歷判斷
            if(new Date().getTime() % 2 == 0){
                var scrolled = document.documentElement.scrollTop || document.body.scrollTop
                for(var i = 0;i<tip.navCategoryAnchor.length;i++){
                    if((i==0) ?
                        (tip.navCategoryAnchor[i+1].offset >= scrolled) :
                        (tip.navCategoryAnchor[i].offset <= scrolled && ((i == tip.navCategoryAnchor.length - 1) ? true : tip.navCategoryAnchor[i + 1].offset >= scrolled))){

                        $("#"+tip.navCategoryAnchor[i].a).css("color","#519cea");
                    }else{
                        $("#"+tip.navCategoryAnchor[i].a).css("color","");
                    }
                }
            }
        });

        //生成目錄索引列表
        var h2_list = setting.list1;//目錄的一級標題,找到全部h2

        if(h2_list.length>0){
            //返回頂部,元素以前追加
            $("body").prepend('<a name="_labelTop"></a>');

            var content    = '<div id="navCategory">';
            content    += '<ul>';
            //一級標題
            for(var i =0;i<h2_list.length;i++){
                var h2_id = "_label_h2" + i;
                var h2_text = $(h2_list[i]).text();
                //去左右空格;
                h2_text = h2_text.replace(/(^\s*)|(\s*$)/g, "");
                $(h2_list[i]).attr("id",h2_id);
                //錨點位置
                tip.navCategoryAnchor.push({a:h2_id+"_a",offset:$(h2_list[i]).offset().top});
                content += '<li><a id="'+h2_id+'_a" href="#' + h2_id + '">' + h2_text + '</a></li>';
                //目錄的二級標題,找到全部的h3
                var h3_list = eval(setting.list2.replace("$list1","$(h2_list[i])"));

                for(var j=0; j<h3_list.length; j++){
                    var h3_id = "_label_h3_" + i + "_" + j;
                    var h3_text = $(h3_list[j]).text();
                    //去左右空格;
                    h3_text = h3_text .replace(/(^\s*)|(\s*$)/g, "");
                    $(h3_list[j]).attr("id",h3_id);
                    //錨點位置
                    tip.navCategoryAnchor.push({a:h3_id+"_a",offset:$(h3_list[j]).offset().top});
                    content += '<li style="padding-left: 25px"><a id="'+h3_id+'_a" href="#' + h3_id + '">' + h3_text + '</a></li>';
                }
            }
            content += '</ul>';
            content += '</div>';

            //生成目錄拖拽彈窗
            tip.dialog({title:"目錄",content:content,offset: setting.offset,area:setting.area,shade:0});
        }
    }
};

$(function(){
    //初始化
    tip.init();
});

 

 

 

  調用

// tip提示
    tip.msg("哈哈哈哈哈");
    tip.msg({text:"哈哈哈哈哈",time:5000});

    //可拖放窗口
    tip.dialog({title:"測試彈窗標題",content:"測試彈窗內容"});
    tip.dialog({title:"測試彈窗標題",content:"<h1>測試彈窗內容</h1>",offset: ['100px', '50px'],area:["200px","100px"],shade:0});

 

  效果演示

  實時效果請看個人博客裏面的目錄、點贊功能、返回頂部(點擊火箭)

  msg

  彈出、銷燬有動畫效果,設置了一個max-width,超出會換行,支持參數配置

  

  dialog

  可拖動、可最小化、最大化、關閉,右下角可進行縮放,支持參數配置,發光特效

 

  總結

  實現拖拽、縮放功能,主要是監聽了document的mousedown,mousemove,mouseup事件,並對特殊元素進行特殊處理,一波騷操做,玩出了花(滑稽),從而實現咱們想要的效果。

  參考:

  http://layer.layui.com/

  http://www.17sucai.com/pins/demo-show?id=4218

  http://www.javashuo.com/article/p-ucavgype-cw.html

 

  生成博客園目錄

  咱們利用這個彈窗,生成博客園的博文目錄。

  一、咱們須要向博客園官方申請js權限(我的主頁 --> 設置 --> 博客設置);

  二、把咱們上面的代碼添加進去

  頁首、頁腳的意思就是咱們的代碼是嵌在頁首仍是頁腳

  三、生成目錄索引,把下面的代碼也添加進去

 
  //錨點信息數組
    var navCategoryAnchor = [];
    
    //點擊章節,滾動帶動畫效果
    $("body").on("click","#navCategory a",function() {
        $("html, body").animate({
        scrollTop: $($(this).attr("href")).offset().top - 100 + "px"
        }, 800);
        return false;
    });
    
    //監聽鼠標滾動事件
    window.addEventListener('scroll', function () {
        //無需頻繁的進行遍歷判斷
        if(new Date().getTime() % 2 == 0){
            var scrolled = document.documentElement.scrollTop || document.body.scrollTop
          for(var i = 0;i<navCategoryAnchor.length;i++){
                if((i==0) ? 
                   (navCategoryAnchor[i+1].offset >= scrolled) : 
                   (navCategoryAnchor[i].offset <= scrolled && ((i == navCategoryAnchor.length - 1) ? true : navCategoryAnchor[i + 1].offset >= scrolled))){

                   $("#"+navCategoryAnchor[i].a).css("color","#519cea");
                }else{
                    $("#"+navCategoryAnchor[i].a).css("color","");
                }
            }
        }
   });

    //生成目錄索引列表
    function GenerateContentList(){
        var h2_list = $('#cnblogs_post_body h2');//目錄的一級標題,找到全部h2

        if(h2_list.length>0){
            //返回頂部,元素以前追加
            $("body").prepend('<a name="_labelTop"></a>');
            
            var content    = '<div id="navCategory">';
            content    += '<ul>';
            //一級標題
            for(var i =0;i<h2_list.length;i++){
                var h2_id = "_label_h2" + i;
                var h2_text = $(h2_list[i]).text();
                //去左右空格;
                h2_text = h2_text.replace(/(^\s*)|(\s*$)/g, "");
                $(h2_list[i]).attr("id",h2_id);
                //錨點位置
                navCategoryAnchor.push({a:h2_id+"_a",offset:$(h2_list[i]).offset().top});
                content += '<li><a id="'+h2_id+'_a" href="#' + h2_id + '">' + h2_text + '</a></li>';
                //目錄的二級標題,找到全部的h3
                var h3_list = $(h2_list[i]).nextAll("h3");

                for(var j=0; j<h3_list.length; j++){
                    var tmp = $(h3_list[j]).prevAll('h2').first();
                    if(!tmp.is(h2_list[i])){
                        break;
                    }
                    var h3_id = "_label_h3_" + i + "_" + j;
                    var h3_text = $(h3_list[j]).text();
                    //去左右空格;
                    h3_text = h3_text .replace(/(^\s*)|(\s*$)/g, "");
                    $(h3_list[j]).attr("id",h3_id);
                    //錨點位置
                    navCategoryAnchor.push({a:h3_id+"_a",offset:$(h3_list[j]).offset().top});
                    content += '<li style="padding-left: 25px"><a id="'+h3_id+'_a" href="#' + h3_id + '">' + h3_text + '</a></li>';
                }
            }
            content += '</ul>';
            content += '</div>';
            
            //生成目錄拖拽彈窗
            tip.dialog({title:"目錄",content:content,offset: ['40%', '10%'],area:["156px","250px"],shade:0});
        }    
    }

    $(function($){
        //執行代碼
        GenerateContentList();
    })

 

    咱們這裏一級標題對應的是h2,二級標題對應的是h3,並且目前只有兩個標題;生成目錄的簡單思路是:在每一個h二、h3標籤都添加一個惟一id,而後生成對應的a標籤連接過去,例如:

  <h2 id="_label_h20">  前言</h2>     

  <a id="_label_h20_a" href="#_label_h20">前言</a>

  最後將拼接成的HTML字符串傳入咱們的tip.dialog方法,生成目錄彈窗

  

  終極封裝

  我把js代碼跟css代碼單獨寫成tip.js,tip.css,並上傳到博客的文件管理那裏,而且擴展了generateContentList方法:生成目錄彈窗,支持到二級目錄,如今想使用咱們這個可拖動彈窗就很簡單了,引入咱們的文件便可使用tip對象,調用對應的方法

    <!-- 自定義web彈窗 CSS 文件 -->
    <link href="https://files.cnblogs.com/files/huanzi-qch/tip.css" rel="stylesheet"/>
    <script src="https://files.cnblogs.com/files/huanzi-qch/tip.js"></script>

  封裝

var tip = {

    省略其餘代碼...
    
    //生成目錄彈窗,錨點信息數組
    navCategoryAnchor : [],
    
    /**
    * 生成目錄彈窗,支持到二級目錄
    * {
    *     list1:$('#cnblogs_post_body h2'),//目錄的一級標題集合(如何找到一級目錄)
    *     list2:"$list1.nextAll('h3')",//目錄的二級標題集合,(如何從每一個一級目錄節點$list1下面找到二級目錄)
    *     offset: ['40%', '10%'],//彈窗位置
    *    area:["156px","250px"]//彈窗大小
    * }
    */
    generateContentList : function(setting){
        setting.offset ? setting.offset : setting.offset = ['40%', '10%'];//彈窗位置
        setting.area ? setting.area : setting.area = ["156px","250px"];//彈窗大小
        
        //點擊章節,滾動帶動畫效果
        $("body").on("click","#navCategory a",function() {
            $("html, body").animate({
            scrollTop: $($(this).attr("href")).offset().top - 100 + "px"
            }, 800);
            return false;
        });
        
        //監聽鼠標滾動事件
        window.addEventListener('scroll', function () {
            //無需頻繁的進行遍歷判斷
            if(new Date().getTime() % 2 == 0){
                var scrolled = document.documentElement.scrollTop || document.body.scrollTop
              for(var i = 0;i<tip.navCategoryAnchor.length;i++){
                    if((i==0) ? 
                       (tip.navCategoryAnchor[i+1].offset >= scrolled) : 
                       (tip.navCategoryAnchor[i].offset <= scrolled && ((i == tip.navCategoryAnchor.length - 1) ? true : tip.navCategoryAnchor[i + 1].offset >= scrolled))){

                       $("#"+tip.navCategoryAnchor[i].a).css("color","#519cea");
                    }else{
                        $("#"+tip.navCategoryAnchor[i].a).css("color","");
                    }
                }
            }
       });

        //生成目錄索引列表
        var h2_list = setting.list1;//目錄的一級標題,找到全部h2

        if(h2_list.length>0){
            //返回頂部,元素以前追加
            $("body").prepend('<a name="_labelTop"></a>');
            
            var content    = '<div id="navCategory">';
            content    += '<ul>';
            //一級標題
            for(var i =0;i<h2_list.length;i++){
                var h2_id = "_label_h2" + i;
                var h2_text = $(h2_list[i]).text();
                //去左右空格;
                h2_text = h2_text.replace(/(^\s*)|(\s*$)/g, "");
                $(h2_list[i]).attr("id",h2_id);
                //錨點位置
                tip.navCategoryAnchor.push({a:h2_id+"_a",offset:$(h2_list[i]).offset().top});
                content += '<li><a id="'+h2_id+'_a" href="#' + h2_id + '">' + h2_text + '</a></li>';
                //目錄的二級標題,找到全部的h3
                var h3_list = eval(setting.list2.replace("$list1","$(h2_list[i])"));

                for(var j=0; j<h3_list.length; j++){
                    var h3_id = "_label_h3_" + i + "_" + j;
                    var h3_text = $(h3_list[j]).text();
                    //去左右空格;
                    h3_text = h3_text .replace(/(^\s*)|(\s*$)/g, "");
                    $(h3_list[j]).attr("id",h3_id);
                    //錨點位置
                    tip.navCategoryAnchor.push({a:h3_id+"_a",offset:$(h3_list[j]).offset().top});
                    content += '<li style="padding-left: 25px"><a id="'+h3_id+'_a" href="#' + h3_id + '">' + h3_text + '</a></li>';
                }
            }
            content += '</ul>';
            content += '</div>';
            
            //生成目錄拖拽彈窗
            tip.dialog({title:"目錄",content:content,offset: setting.offset,area:setting.area,shade:0});
        }    
    }
};

 

  直接調用生成目錄彈窗方法

    //生成目錄彈窗
    tip.generateContentList({
        list1:$('#cnblogs_post_body h2'),//目錄的一級標題集合(如何找到一級目錄)
        list2:"$list1.nextAll('h3')",//目錄的二級標題集合,(如何從每一個一級目錄節點$list1下面找到二級目錄)
        offset: ['40%', '10%'],//彈窗位置
        area:["156px","250px"]//彈窗大小
    });

 

  bug修復

  一、2019-05-17;bug復現:當有多個彈窗且大小不一致,拖動其中一個彈窗進行縮放,會影響到其餘彈窗,而且寬度變成跟最大的那個同樣

  問題分析:代碼中咱們使用 dialogDiv = titleDiv.parent(),是jq對象,經過一個小測試發現,$獲取到的jq對象每次都是新的,jq對象裏面的DOM對象纔是惟一不變

  

  因此咱們把數據寫在jq裏面的dom對象就好了,可是若是更開始resize沒有值的時候,undefined.width就會報錯,這一點要注意

  bug解決:使用jq對象裏面的jsDOM對象,且在用的對象以前進行非空判斷便可,我tip.js已經替換成最新代碼,bug已經解決;

//避免undefined.XXX報錯
dialogDiv[0].resize = dialogDiv[0].resize ? dialogDiv[0].resize : {};

   建立彈窗時初始化最小寬高哪裏也要改一下,同時也要先進行非空判斷

        //初始值寬高
        //避免undefined.XXX報錯
        $tip[0].resize = $tip[0].resize ? $tip[0].resize : {};
        $tip[0].resize.initWidth = $tip.width();
        $tip[0].resize.initHeight = $tip.find(".tip-content").height();

 

 

 

   推薦關注收藏

  效果

 

 

  代碼

  首先樣式.tip-title取消邊框,改爲.tip-content給個上下邊框

    border-bottom: 1px solid #dad8d8;
    border-top: 1px solid #dad8d8;

  接下來是js代碼,先克隆一個這個對象,而後在生成目錄彈窗後進行樣式處理、追加

 //推薦、關注、收藏
let $clone = $("#green_channel").clone();
$clone.css({
   "margin-top": "4px",
    "margin-bottom": "4px",
    "font-size": "12px",
    "width": "164px",
    "text-align": "center",
    "border": "#c0c0c0 0px dashed"
});
$clone.find("#green_channel_digg").text("推薦");
$clone.find("#green_channel_follow").text("關注");
$clone.find("#green_channel_favorite").text("收藏");
$clone.find("#green_channel_weibo").remove();
$clone.find("#green_channel_wechat").remove();

$(".tip-dialog").append($clone); 

  而後效果就出來了

相關文章
相關標籤/搜索