博客园装饰——(三)博客园导航目录

一、功能描述

  1. 初始位置在左下角

  2. 鼠标放上按钮会显示箭头(目录 展开方向收起方向 ),移开会显示 “目录” 两个字

  3. 左键按住按钮,可进行拖拽,且会根据目录的整体大小自动调整在窗口内能拖动到的区域

  4. 右键点击按钮,可展开或收起目录

  5. 点击目录内的级别高的条目,可将同级的其他条目内的子条目收起,并展开或收起自己内部的低级别的子条目。

  6. 点击目录内的条目,可自动导航到其所在的页面内的绝对位置(高度)

1.1 粗略演示

博客园导航目录-LMLPHP

二、 核心算法思想

2.1 假设页面情况

2.1.1 文字描述

先模拟博客园的文章中的标题环境:

  1. 二、三、四级标题可能是 连续邻近的
  2. 或者可能它们之间会 穿插很多其他标签及内容

2.1.2 代码描述

<div id="cnblogs_post_body">
		<h2>h2-1</h2>
		<img src="../01-html文档结构和常用标签/image/臭鼬.jpg" alt="">
		<h3>h3-1</h3>
		<h4>h4-1</h4>
		<a href="">花Q</a>
		<h3>h3-1</h3>
		<h4>h4-1</h4>
		<h3>h3-1</h3>

		<h2>h2-2</h2>
		<span>街头霸王</span>
		<h3>h3-2</h3>
		<h3>h3-2</h3>
		<h3>h3-2</h3>
		<h4>h4-2</h4>
		<div>DD斩首</div>
		<h4>h4-2</h4>

		<h2>h2-3</h2>
		<h3>h3-3</h3>
		<span>壁纸</span>
		<h4>h4-3</h4>
		<h3>h3-3</h3>
		<img src="../01-html文档结构和常用标签/image/17.jpg" alt="">
		<h4>h4-3</h4>
		<h3>h3-3</h3>
		<h4>h4-3</h4>
	</div>

2.1.3 效果图片展示

博客园导航目录-LMLPHP

2.1.4 问题描述

正如上图所示,模拟出的文章环境中,夹着其他很多非二、三、四级标题标签,而我们需要从中 过滤筛选 出标题标签,才能进一步自动生成目录的条目或结构。

2.1.5 解决办法

var oH = $('#cnblogs_post_body').find('h2,h3,h4');

2.2 目录结构

2.2.1 代码描述

  1. ul 是2,3,4级标题对应的目录条目的容器(用于展开或收起)
  2. span 才是我们到时,目录可点击的并产生相应事件的条目(用于导航或触发条目的展开或收起)
<div class="diy_menu clearfix">
		<ul class="level2_con">
			<li>
				<!-- 第一个H2 -->
				<span id="0" class="level2">h2-1</span>
				<ul class="level3_con">

					<li>
						<!-- 第一个H3 -->
						<span id="1" class="level3">h3-1</span>
						<ul class="level4_con">
							<li>
								<!-- 第一个H3中的第一个H4 -->
								<span id="2" class="level4">h4-1</span>
							</li>
						</ul>
					</li>

					<li>
						<!-- 第二个H3 -->
						<span id="3" class="level3">h3-1</span>
						<ul class="level4_con">
							<li>
								<!-- 第二个H3中的第一个H4 -->
								<span id="4" class="level4">h4-1</span>
							</li>
						</ul>
					</li>

					<li>
						<!-- 第三个H3 -->
						<span id="5" class="level3">h3-1</span>
						<ul class="level4_con">
							<!-- 第三个H3中没有H4 -->
						</ul>
					</li>

				</ul>
			</li>

			<li>
				<!-- 第二个H2 -->
				<span id="6">h2-2</span>
				<ul>
					.
					.
					.
				</ul>
			</li>

				.
				.
				.


		</ul>
</div>

2.2.2 图片效果展示

博客园导航目录-LMLPHP

2.2.3 问题引入

为了,以及实现自动根据页面的文本结构自动生成这样的目录结构,当然不可能手动编写html,来生成目录,所以我们需要借助js来识别文章里的标题标签,并通过算法写入,改变文档流,从而生成相应的目录结构。

<!-- 即往这个初始结构中,自动写入html代码 -->
<div class="diy_menu clearfix">
	<ul class="level2_con">

    	</ul>
</div>

2.3 ※ (前期准备)目录生成算法

2.3.1 图文描述

博客园导航目录-LMLPHP

2.3.2 代码描述

// 过滤筛选出h2,h3,h4的标签元素
var oH = $('#cnblogs_post_body').find('h2,h3,h4');
var tagName_list = [];
var tagHigh_list = [];

// 相当于python中的for循环遍历;元素集才可以使用each()函数,列表不能
oH.each(function(i){
    tagName_list.push($(this).prop('tagName'));
    tagHigh_list.push(Math.ceil($(this).offset().top));  // 得到的高度值进行向上取整
})
// console.log(tagName_list);
// console.log(tagHigh_list);

2.4 ※ (核心)目录生成算法

2.4.1 文字描述

  1. 我们最核心的思想就是,从而在相应的位置插入相应的级别条目
  2. 正因为我们每一次循环需要判断它是第几级标签,故我们需要一个列表 tagName_list ,将 "oH" 元素集对应的标签名存起来
  3. 同时我们可以看出来,文章中的 标题标签顺序tagName_list 里的标签名的,所以我们可以配合 ,实现自动识别并写入不同级别的条目,从而构成一个目录,同时也从侧面反映出我们设计一个 **tagName_list 列表 **的初衷
  4. eleh2_3eleh4 用于控制写入位置(即节点)

2.4.2 代码描述

var itagName_list = tagName_list.length;
var eleh2_3 = undefined;  // 用于插入2级或3级标题标签对应的条目的节点
var eleh4 = undefined;  // 用于插入4级标题标签对应的条目的节点

for(var i=0;i<itagName_list;i++){

    var a = tagName_list[i];

    if (a == 'H2'){
        eleh2_3 = $('.level2_con');  // 获取用于插入2级标题标签对应的条目的节点
        var oh2 = oH.eq(i);
        var plus = '<li><span id="tagHigh_' + i + '" class="level2">'+ oh2.html() +'</span><ul class="level3_con"></ul></li>';
        eleh2_3.append(plus);
        // eleh2_3.html(eleh2_3.html() + plus);
        eleh2_3 = $('.level2_con>li:last ul'); //选择最后一个即创建的最新的一个li,获取用于插入3级标题标签对应的条目的节点
        // console.log(eleh2_3.html());
    }

    else if( a == 'H3'){
        var oh3 = oH.eq(i);
        var plus = '<li><span id="tagHigh_' + i + '" class="level3">' + oh3.html() + '</span><ul class="level4_con"></ul></li>';
        // eleh2_3.html(eleh2_3.html() + plus);
        eleh2_3.append(plus);
        eleh4 = eleh2_3.children("li:last-child").children('ul');  // 获取用于插入4级标题标签对应的条目的节点
        // console.log(eleh4.html());
    }

    else if( a == 'H4'){
        var oh4 = oH.eq(i);
        var plus = '<li><span id="tagHigh_' + i + '" class="level4">' + oh4.html() + '</span></li>';
        // eleh4.html(eleh4.html() + plus);
        eleh4.append(plus);

    }

}

2.5 ※ 目录生成算法总览

/*------------------------------------目录:1.读取页面内的标签并生成目录----------------------------------*/

    // 过滤筛选出h2,h3,h4的标签元素
    var oH = $('#cnblogs_post_body').find('h2,h3,h4');
    var tagName_list = [];
    var tagHigh_list = [];

    // 相当于python中的for循环遍历;元素集才可以使用each()函数,列表不能
    oH.each(function(i){
        tagName_list.push($(this).prop('tagName'));
        tagHigh_list.push(Math.ceil($(this).offset().top));  // 得到的高度值进行向上取整
    })
    // console.log(tagName_list);
    // console.log(tagHigh_list);

    var itagName_list = tagName_list.length;
    var eleh2_3 = undefined;  // 用于插入2级或3级标题标签对应的条目的节点
    var eleh4 = undefined;  // 用于插入4级标题标签对应的条目的节点

    /*---------目录自动生成算法-------------*/
    for(var i=0;i<itagName_list;i++){

        var a = tagName_list[i];

        if (a == 'H2'){
            eleh2_3 = $('.level2_con');  // 获取用于插入2级标题标签对应的条目的节点
            var oh2 = oH.eq(i);
            var plus = '<li><span id="tagHigh_' + i + '" class="level2">'+ oh2.html() +'</span><ul class="level3_con"></ul></li>';
            eleh2_3.append(plus);
            // eleh2_3.html(eleh2_3.html() + plus);
            eleh2_3 = $('.level2_con>li:last ul'); // 选择最后一个即创建的最新的一个li,获取用于插入3级标题标签对应的条目的节点
            // console.log(eleh2_3.html());
        }

        else if( a == 'H3'){
            var oh3 = oH.eq(i);
            var plus = '<li><span id="tagHigh_' + i + '" class="level3">' + oh3.html() + '</span><ul class="level4_con"></ul></li>';
            // eleh2_3.html(eleh2_3.html() + plus);
            eleh2_3.append(plus);
            eleh4 = eleh2_3.children("li:last-child").children('ul');  // 获取用于插入4级标题标签对应的条目的节点
            // console.log(eleh4.html());
        }

        else if( a == 'H4'){
            var oh4 = oH.eq(i);
            var plus = '<li><span id="tagHigh_' + i + '" class="level4">' + oh4.html() + '</span></li>';
            // eleh4.html(eleh4.html() + plus);
            eleh4.append(plus);

        }

    }

2.6 ※ 目录内条目的展开收起与自动导航

 /*------------------------目录:2.目录内的条目展开和收起的动画-------------------------------*/
      /*-------------利用事件委托,减少相同元素绑定相同事件的次数,提高性能---------------------*/

    var olevel2_con = $('.level2_con');

    olevel2_con.delegate('.level2, .level3, .level4', 'click', function(){
        // console.log($(this).prop('id').substring(8));
        // 滚动到相应标签位置
        var tagHighIndex = $(this).prop('id').substring(8);

        var tagHigh = tagHigh_list[tagHighIndex];  // 取该条目对应的标签的高度值

        $('html,body').stop().animate({'scrollTop':tagHigh-250},1000);

        // 目录自动合上与展开动画
        if ( $(this).prop('className') == 'level2' ){
            $(this).parent().siblings().children('.level3_con').children('li').children('.level4_con').stop().slideUp();
            $(this).next().stop().slideToggle().parent().siblings().children('ul').stop().slideUp();
        }
        else if ( $(this).prop('className') == 'level3' ){
            //当前点击的元素紧挨的同辈元素向下展开,再跳到此元素的父级(li),再跳到此父级的其他的同辈元素(li),
            //选择其他同辈元素(li)的子元素ul,然后将它向上收起。
            // 通过stop() 可以修正反复点击导致的持续动画的问题
            $(this).next().stop().slideToggle().parent().siblings().children('ul').stop().slideUp();
        }

        // console.log($(this).prop('className'));
    })

三、完整代码展示

前言

3.1 HTML 部分

<body>
	<div id="cnblogs_post_body">
		<h2>h2-1</h2>
		<img src="../01-html文档结构和常用标签/image/臭鼬.jpg" alt="">
		<h3>h3-1</h3>
		<h4>h4-1</h4>
		<a href="">花Q</a>
		<h3>h3-1</h3>
		<h4>h4-1</h4>
		<h3>h3-1</h3>

		<h2>h2-2</h2>
		<span>街头霸王</span>
		<h3>h3-2</h3>
		<h3>h3-2</h3>
		<h3>h3-2</h3>
		<h4>h4-2</h4>
		<div>DD斩首</div>
		<h4>h4-2</h4>

		<h2>h2-3</h2>
		<h3>h3-3</h3>
		<span>壁纸</span>
		<h4>h4-3</h4>
		<h3>h3-3</h3>
		<img src="../01-html文档结构和常用标签/image/17.jpg" alt="">
		<h4>h4-3</h4>
		<h3>h3-3</h3>
		<h4>h4-3</h4>
	</div>

	<div class="totop"><span>↑</span></div>
	<div class="tobottom"><span>↓</span></div>

	<div class="diy_menu clearfix">
		<input id="diyMenu_btn" type="button" value="→" title="左键按住我,可以进行拖拽哟~右键点击可以展开哟~">
		<ul class="level2_con" title="左键按住左边的按钮可进行拖拽哟~右键点击可以隐藏哟~">


		</ul>
	</div>
	<!--
    <p>文档内容</p>
	<br />
	<br />
	<br />
	<br />
	<br />
	<br />
	<br />
	<br />
	<br />
	<br />

	(p{文档内容}+br*10)*20  上面这部分代码重复20遍,为了让页面足够长
	-->
</body>

3.2 CSS 部分

body,ul{
    margin:0px;
    padding:0px;
}

.clearfix:after,.clearfix:before{ content: "";display: table;}
.clearfix:after{ clear:both;}
.clearfix{zoom:1;}

ul{list-style:none;}

.diy_menu{
    position:fixed;
    bottom:100px;
    left:75px;
    height: 75px;
    /*cursor: pointer;*/
    /*background-color: gold;*/
}

#diyMenu_btn{
    float:left;
    width:75px;
    font:bold 50px/75px '黑体';
    border:0px;
    color:white;
    cursor: pointer;
    border-radius: 20px;
    /*用于去除点击按钮后的提示边框*/
    outline: none;
    opacity: 0.7;
    background-color: #c2c0c0b5;
}

@keyframes color_turn{
    form{
        background-color: #c2c0c0b5;
    }
    to{
        background-color: #40c8f4;
    }
}

.btn_color_turn{
    animation:color_turn 750ms ease 2 alternate;
}

.level2_con{
    width:650px;
    border-radius: 10px;
    overflow: hidden;
    float:left;
    margin:0px;
    /* opacity: 0.7;*/
}

.level2_con .level2, .level3_con .level3, .level4_con .level4{
    display:block;
    width:650px;
    height:30px;
    line-height:30px;
    text-decoration:none;
    background-color:#fc6d86;
    color:#fff;
    font-size:20px;
    font-weight: bold;
    text-indent:10px;
    border-bottom:4px dashed white;
    opacity: 0.7;
    cursor:pointer;
}

#diyMenu_btn:hover, .level2_con .level2:hover, .level3_con .level3:hover, .level4_con .level4:hover{
    opacity: 1;
}

.level3_con .level3{
    background-color:#ff8ea2;
    color:#1eb21ee0;
    font-size:17px;
    text-indent:20px;
    border-bottom:3px solid #1eb21ee0;
}

.level4_con .level4{
    background-color:pink;
    color:#2893f0d1;
    font-size:14px;
    text-indent:30px;
    border-bottom:2px dashed #2893f0d1;
}

.level3_con{
    display: none;
}

.level4_con{
    display: none;
}

3.3 JS 部分

$(function(){

    /*------------------------------------目录:1.读取页面内的标签并生成目录----------------------------------*/

    // 过滤筛选出h2,h3,h4的标签元素
    var oH = $('#cnblogs_post_body').find('h2,h3,h4');
    var tagName_list = [];
    var tagHigh_list = [];

    // 相当于python中的for循环遍历;元素集才可以使用each()函数,列表不能
    oH.each(function(i){
        // i 为索引值
        // console.log(i);
        // console.log($(this).index());
        tagName_list.push($(this).prop('tagName'));
        tagHigh_list.push(Math.ceil($(this).offset().top));  // 得到的高度值进行向上取整
    })
    // console.log(tagName_list);
    // console.log(tagHigh_list);

    var itagName_list = tagName_list.length;
    var eleh2_3 = undefined;  // 用于插入2级或3级标题标签对应的条目的节点
    var eleh4 = undefined;  // 用于插入4级标题标签对应的条目的节点

    /*---------目录自动生成算法-------------*/
    for(var i=0;i<itagName_list;i++){

        var a = tagName_list[i];

        if (a == 'H2'){
            eleh2_3 = $('.level2_con');  // 获取用于插入2级标题标签对应的条目的节点
            var oh2 = oH.eq(i);
            var plus = '<li><span id="tagHigh_' + i + '" class="level2">'+ oh2.html() +'</span><ul class="level3_con"></ul></li>';
            eleh2_3.append(plus);
            // eleh2_3.html(eleh2_3.html() + plus);
            eleh2_3 = $('.level2_con>li:last ul'); // 选择最后一个即创建的最新的一个li,获取用于插入3级标题标签对应的条目的节点
            // console.log(eleh2_3.html());
        }

        else if( a == 'H3'){
            var oh3 = oH.eq(i);
            var plus = '<li><span id="tagHigh_' + i + '" class="level3">' + oh3.html() + '</span><ul class="level4_con"></ul></li>';
            // eleh2_3.html(eleh2_3.html() + plus);
            eleh2_3.append(plus);
            eleh4 = eleh2_3.children("li:last-child").children('ul');  // 获取用于插入4级标题标签对应的条目的节点
            // console.log(eleh4.html());
        }

        else if( a == 'H4'){
            var oh4 = oH.eq(i);
            var plus = '<li><span id="tagHigh_' + i + '" class="level4">' + oh4.html() + '</span></li>';
            // eleh4.html(eleh4.html() + plus);
            eleh4.append(plus);

        }

    }

    /*------------------------目录:2.目录内的条目展开和收起的动画-------------------------------*/
      /*-------------利用事件委托,减少相同元素绑定相同事件的次数,提高性能---------------------*/

    var olevel2_con = $('.level2_con');

    olevel2_con.delegate('.level2, .level3, .level4', 'click', function(){
        // console.log($(this).prop('id').substring(8));
        // 滚动到相应标签位置
        var tagHighIndex = $(this).prop('id').substring(8);

        var tagHigh = tagHigh_list[tagHighIndex];

        $('html,body').stop().animate({'scrollTop':tagHigh-250},1000);

        // 目录自动合上与展开动画
        if ( $(this).prop('className') == 'level2' ){
            $(this).parent().siblings().children('.level3_con').children('li').children('.level4_con').stop().slideUp();
            $(this).next().stop().slideToggle().parent().siblings().children('ul').stop().slideUp();
        }
        else if ( $(this).prop('className') == 'level3' ){
            //当前点击的元素紧挨的同辈元素向下展开,再跳到此元素的父级(li),再跳到此父级的其他的同辈元素(li),
            //选择其他同辈元素(li)的子元素ul,然后将它向上收起。
            // 通过stop() 可以修正反复点击导致的持续动画的问题
            $(this).next().stop().slideToggle().parent().siblings().children('ul').stop().slideUp();
        }

        // console.log($(this).prop('className'));
    })

    /*------------------------目录:3.目录整体的展开和隐藏的动画---------------------------------*/
        /*---------------左键按住按键可进行目录拖拽,右键点击课展开和收起目录------------------*/
    // 获取按钮元素
    var menu_btn = $('#diyMenu_btn');

    var olevel2 = $('.level2');
    // 一开始获取原高度,是为了能够使下面的animate动画顺利展开到原高度
    var OriHeight = olevel2_con.height();

    /*
        一开始是隐藏状态,所以将height和width设为0,CSS部分按照正常显示的宽度设置,
        页面是等js加载完了,才会显示页面元素,所以无需担心一开始目录会出现突然消失的现象,
        更何况程序运行是一瞬间的事情,肉眼无法分辨,从这个角度思考,也是不用担心。
    */
    olevel2_con.css({'height': 0, 'width': 0});

    // 为了使目录能在浏览器窗口内拖拽的标志,'0'表示目录隐藏,默认目录隐藏,所以初始值为0
    var unfoldFlag = 0;

    // 拖拽目录
    var diyMenu = $('.diy_menu');
    var menu_btnHeight = menu_btn.outerHeight(true);
    var menuOriHigh = olevel2_con.outerHeight(true);
    var oriColor = menu_btn.css('background-color');

    /*-----------------------鼠标放在按钮上会切换为箭头显示--------------------*/
    // 获取原来按钮的值
    var menuBtnVal = menu_btn.val();
    // 将初始显示重新设置为目录(即鼠标未放上去时显示目录)
    menu_btn.val('目录');
    // 由于目录两字如果使用原大小50px,会显得特别大
    menu_btn.css({'font-size':25});

    menu_btn.mouseenter(function(){
        menu_btn.val(menuBtnVal);
        menu_btn.css({'font-size':50});
    })

    menu_btn.mouseleave(function(){
        // 离开按钮前再一次获取当前按钮值,用于下一次显示
        menuBtnVal = menu_btn.val();
        menu_btn.css({'font-size':25});
        menu_btn.val('目录');
    })
    /*------------------------------------------------------------------------*/

    menu_btn.mousedown(function(e) {

        // 禁止了右键点击该按钮,会弹出浏览器系统菜单
        $(this).bind("contextmenu",function(e){
            return false;
        });

        // 获取目录在页面的绝对位置,'e'代表鼠标对象,'e.pageX' 表示鼠标相对于页面的横向位置
        var positionDiv = diyMenu.offset();
        var distenceX = e.pageX - positionDiv.left;  // 获得鼠标点击位置相对于目录左侧的距离
        var distenceY = e.pageY - positionDiv.top;  // 获得鼠标点击位置相对于目录顶部的距离
        // console.log(e.pageY, positionDiv.top);

        // alert(distenceX)
        // alert(positionDiv.left);

        // console.log(e.which);  // '1' 代表左键触发事件,'2' 代表中键,'3' 代表右键

        // '3' 是右键,用于展开或隐藏目录
        if( e.which == 3){

            // 移除变为蓝色的动画效果,使得animation动画得以重复播放
            // 放在这里,给计算机足够的反应时间,不会出现bug
            menu_btn.removeClass('btn_color_turn');

            if ( menu_btn.val() == '→'){
                menu_btn.val('←');
                // 为了使目录能在浏览器窗口内拖拽的标志,'1'表示目录展开
                unfoldFlag = 1;

                /*-------解决animate动画无法直接让高度恢复为auto值-----------------*/
                olevel2_con.stop().animate({
                    width:olevel2.width(),
                    height:30,
                },500,function(){

                    olevel2_con.stop().animate({
                        height:OriHeight,
                    },1000,function(){
                        // 放在里面就会在动画结束后,才进行赋值
                        olevel2_con.css({'height':'auto'});
                    })

                })
                // 由于 animate动画的持续时间,不会影响程序的正常运行(多任务)
                // 所以下面这一句会在动画还没结束前,先运行,而由于动画过程会动态改变height的值,所以瞬间'auto'被覆盖掉了
                // olevel2_con.css({'height':'auto'});

            }

            else{
                menu_btn.val('→');
                // 为了使目录能在浏览器窗口内拖拽的标志,'0'表示目录收起,未展开
                unfoldFlag = 0;

                olevel2.parent().siblings().children('.level3_con').children('li').children('.level4_con').stop().slideUp();
                olevel2.next().stop().slideUp().parent().siblings().children('ul').slideUp();

                olevel2_con.stop().animate({
                    height:30,
                },1000,function(){
                    olevel2_con.stop().animate({
                        width:0,
                        height:0,
                    },500)
                })

            }
        }
        // '1'代表左键,所以左键负责拖拽
        else if( e.which == 1 ){

            // console.log(oriColor);

            menu_btn.css({'background-color': 'lightgreen'});

            $(document).mousemove(function(e) {

                // console.log(e.pageX, e.pageY);

                /*
                    鼠标移动后的横向位置 - 鼠标原位置相对于目录左侧的距离 = 目录横向移动后相对于页面的横向位置
                    (但不是相对于浏览器窗口的位置,由于没有横向滚动条,所以页面宽度和浏览器宽度一样,就无需减去页面滚动距离)
                */
                var x = e.pageX - distenceX;
                /*
                    重点:※ 如何获取元素相对于浏览器窗口的距离?
                        鼠标移动后的纵向位置 - 鼠标原位置相对于目录顶部的距离 = 目录纵向移动后相对于页面的纵向位置
                    (但不是相对于浏览器窗口的位置,由于有纵向滚动条,所以要获得相对于浏览器窗口的纵向位置,就必须
                    减去页面向上滚动的距离,即$(document).scrollTop())
                */
                // var y = e.pageY - distenceY;  // 获得目录纵向移动后相对于页面的纵向位置(但不是相对于浏览器窗口的位置)
                var y = e.pageY - distenceY - $(document).scrollTop();
                // console.log($(window).height(), diyMenu.height());

                if (x < 0) {
                    x = 0;
                }
                else if (x > $(window).width() - diyMenu.outerWidth(true)) {
                    x = $(window).width() - diyMenu.outerWidth(true);
                }

                if (y < 0) {
                    y = 0;
                }
                else{
                    // 目录没有展开的情况下
                    if ( (unfoldFlag == 0) && (y > $(window).height() - menu_btnHeight)){
                        y = $(window).height() - menu_btnHeight;
                    }
                    // 目录展开的情况下
                    else if( (unfoldFlag == 1) && (y > $(window).height() - olevel2_con.outerHeight(true))){
                        y = $(window).height() - olevel2_con.outerHeight(true);
                    }

                }

                diyMenu.css({
                    'left': x + 'px',
                    'top': y + 'px'
                });

            });

        }

        $(document).mouseup(function() {
            $(document).off('mousemove');
            menu_btn.css({'background-color': oriColor});
            /*---------------------如何使用 jquery + CSS 实现背景色动画效果?------------------*/
            // 不能放到此处,因为离下面那一句太近了,会出现bug,即animation动画只能播放一次
            // menu_btn.removeClass('btn_color_turn');
            // 右键点击会播放animation换色动画
            if (e.which == 3){
                menu_btn.addClass('btn_color_turn');
            }
        });
    });
    /*----------------------------------------------------------------------------------------------------------*/
})

3.4 效果展示

3.4.1 目录展开收起与导航

博客园导航目录-LMLPHP

3.4.2 拖拽演示(左键按住按钮拖拽)

博客园导航目录-LMLPHP

3.4.3 按钮内容与颜色变化

博客园导航目录-LMLPHP

四、总结与后言

上面我着重讲解了核心算法,是因为实现了目录最核心的两个功能:自动生成条目导航

但并不是说其他功能或设计就没什么难度或不重要,就如窗口区域内拖动这个功能,网上寻找很多案例,都无法达到我的想法与预期,所以博主我绞尽脑汁,通过自己的思考终于解决了这一难题。个人认为关于这一功能的算法讲解与难点的剖析,还是很有探讨价值的,故本人打算用另一篇随笔进行讲解。

08-02 22:27