zoukankan      html  css  js  c++  java
  • 自定义web弹窗/层:简易风格的msg与可拖放的dialog,生成博客园文章目录弹窗

      前言

      做过web项目开发的人对layer弹层组件肯定不陌生,作为layUI的一个重要组件,使用简单、接口参数丰富,功能健壮,深受广大开发者的喜爱,作为一个热(经)爱(常)工(划)作(水),喜欢钻研探索技术的程序员(狗),我们自己来实现一个web弹窗/层,一窥layer的本源(/手动滑稽脸),进步,从模仿开始。

      首先,模仿layer的部分功能,我们先确定下我们要实现的功能:

      msg

      1、居中弹出、延时销毁、自适应宽高、支持参数配置

      dialog

      1、可拖动

      2、可最小化、最大化、关闭

      3、右下角可对窗口进行缩放

      4、支持多个参数配置、扩展

      代码编写

      大部分的思路都在代码注释里

      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 = ' 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>
    " +
                "    <!-- 遮阴层 -->
    " +
                "    <div class="tip tip-shade"></div>
    " +
                "    <!-- 主体 -->
    " +
                "    <div class="tip tip-dialog " + clazz + "">
    " +
                "        <!-- 标题 -->
    " +
                "        <div class="tip tip-title">
    " +
                "            <h2 class="tip tip-title-text"></h2>
    " +
                "            <div class="tip tip-title-btn">
    " +
                "                <button class="tip tip-title-min" title="最小化">--</button>
    " +
                "                <button class="tip tip-title-max" title="最大化">O</button>
    " +
                "                <button class="tip tip-title-close" title="关闭">X</button>
    " +
                "            </div>
    " +
                "        </div>
    " +
                "        <!-- 窗口内容 -->
    " +
                "        <div class="tip tip-content"></div>
    " +
                "        <!-- 右下角改变窗口大小 -->
    " +
                "        <div class="tip tip-resize"></div>
    " +
                "    </div>
    " +
                "</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

      https://www.cnblogs.com/nzbin/p/8117535.html

      生成博客园目录

      我们利用这个弹窗,生成博客园的博文目录。

      1、我们需要向博客园官方申请js权限(个人主页 --> 设置 --> 博客设置);

      2、把我们上面的代码添加进去

      页首、页脚的意思就是我们的代码是嵌在页首还是页脚

      3、生成目录索引,把下面的代码也添加进去

     
      //锚点信息数组
        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,而且目前只有两个标题;生成目录的简单思路是:在每个h2、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修复

      1、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); 

      然后效果就出来了

  • 相关阅读:
    Oracle巡检html版
    bat批处理常用脚本
    UiBot踩坑记录
    服务器的一些优化
    开始学算法(一)
    docker 容器服务脚本自启动
    Cenots Ubuntu linux系统服务脚本开机自启方法
    docker容器添加自定义hosts
    docker 常用命令
    《图解HTTP》学习笔记
  • 原文地址:https://www.cnblogs.com/huanzi-qch/p/10082116.html
Copyright © 2011-2022 走看看