自动生成博客目录

操作说明

  关于博客目录自动生成,已经封装成catalog.js文件,只要引用该文件即可

    //默认地,为页面上所有的h3标签生成目录
    <script src="http://files.cnblogs.com/files/xiaohuochai/catalog.js"></script>
    //或者,为页面上所有class="test"的标签生成目录
    <script src="http://files.cnblogs.com/files/xiaohuochai/catalog.js" data-seletor=".test"></script>

  如下图所示,打开HTML源代码编辑器,在最后引入js即可

  【功能简要说明】

  1、点击目录项,对应章节标题将显示在可视区上方

  2、滚动滚轮,目录项会对应章节标题的变化而相应地变化

  3、点击目录右上角的关闭按钮,可以将目录缩小为"显示目录"四个字,双击缩小后的目录,可恢复默认状态

  4、目录可以拖拽至任意地方

目录参照

  首先,要确定的是,基于什么生成目录。是文章中的<h3>标签,还是文章中的class="list"的标签。所以,更人性化的做法是,将其作为参数,默认参数为<h3>标签

  由于博客园的博文除了自己生成的博客内容外,博客园还会添加诸如评论、公告、广告等元素。所以,第一步要先定位博文

  博文最终都处于id="cnblogs_post_body"的div中

//DOM结构稳定后再操作
window.onload = function(){

    /*设置章节标题函数*/
    function setCatalog(){
        //获取页面中所有的script标题
        var aEle = document.getElementsByTagName(‘script‘);
        //设置sel变量,用于保存其选择符的字符串值
        var sel;
        //获取script标签上的data-selector值
        Array.prototype.forEach.call(aEle,function(item,index,array){
            sel = item.getAttribute(‘data-selector‘);
            if(sel) return;
        })
        //默认参数为h3标签
        if(sel == undefined){
            sel =‘h3‘;
        }
        //选取文章中所有的章节标题
        var tempArray = document.querySelectorAll(sel);
};

目录连接

  目录如何与章节进行对应呢,最常用的就是使用锚点。以基于文章中的<h3>标签生成目录为例,为每一个<h3>标签按照顺序添加锚点(#anchor1,#anchor2...)

//为每一个章节标题顺序添加锚点标识
Array.prototype.forEach.call(tempArray, function(item, index, array) {
        item.setAttribute(‘id‘,‘anchor‘ + (1+index));
});   

目录显示

  在文章左侧显示目录,目录显示的内容就是对应章节的题目

    //设置全局变量Atitle保存添加锚点标识的标题项
    var aTitle = setCatalog();
    /*生成目录*/
    function buildCatalog(arr){
        //由于每个部件的创建过程都类似,所以写成一个函数进行服用
        function buildPart(json){
            var oPart = document.createElement(json.selector);
            if(json.id){oPart.setAttribute(‘id‘,json.id);}
            if(json.className){oPart.className = json.className;}
            if(json.innerHTML){oPart.innerHTML = json.innerHTML;}
            if(json.href){oPart.setAttribute(‘href‘,json.href);}
            if(json.appendToBox){
                oBox.appendChild(oPart);
            }
            return oPart;
        }
        //取得章节标题的个数
        len = arr.length;
        //创建最外层div
        var oBox = buildPart({
            selector:‘div‘,
            id:‘box‘,
            className:‘box‘
        });
        //创建关闭按钮
        buildPart({
            selector:‘span‘,
            id:‘boxQuit‘,
            className:‘box-quit‘,
            innerHTML:‘&times;‘,
            appendToBox:true
        });
        //创建目录标题
        buildPart({
            selector:‘h6‘,
            className:‘box-title‘,
            innerHTML:‘目录‘,
            appendToBox:true
        });
        //创建目录项
        for(var i = 0; i < len; i++){
            buildPart({
                selector:‘a‘,
                className:‘box-anchor‘,
                href:‘#anchor‘ + (1+i),
                innerHTML:‘[‘+(i+1)+‘]‘+arr[i].innerHTML,
                appendToBox:true
            });
        }
        //将目录加入文档中
        document.body.appendChild(oBox);
    }
    buildCatalog(aTitle);

目录样式

  为目录设置样式,最外层div设置最小宽度和最大宽度。当目录项太宽时,显示...。由于最终要封装为一个js文件,所以样式采用动态样式的形式

/*动态样式*/
function loadStyles(str){
    loadStyles.mark = ‘load‘;
    var style = document.createElement("style");
    style.type = "text/css";
    try{
        style.innerHTML = str;
    }catch(ex){
        style.styleSheet.cssText = str;
    }
    var head = document.getElementsByTagName(‘head‘)[0];
    head.appendChild(style);
}
if(loadStyles.mark != ‘load‘){
    loadStyles("h6{margin:0;padding:0;}        .box{position: fixed; left: 10px;top: 60px;font:16px/30px ‘宋体‘; border: 2px solid #ccc;padding: 4px; border-radius:5px;min-width:80px;max-width:118px;overflow:hidden;cursor:default;}        .boxHide{border:none;width:60px;height:30px;padding:0;}        .box-title{text-align:center;font-size:20px;color:#ccc;}        .box-quit{position: absolute; right: 0;top: 4px;cursor:pointer;font-weight:bold;}        .box-anchor{display:block;text-decoration:none;color:black; border-left: 3px solid transparent;padding:0 3px;margin-bottom: 3px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}        .box-anchor:hover{color:#3399ff;}        .box-anchorActive{color:#3399ff;text-decoration:underline;border-color:#2175bc};");
};

点击事件

  为各目录项增加点击事件,使用事件代理,增加性能

//由于点击事件和滚轮事件都需要将目录项发生样式变化,所以声明锚点激活函数
function anchorActive(obj){
    var parent = obj.parentNode;
    var aAnchor = parent.getElementsByTagName(‘a‘);
    //将所有目录项样式设置为默认状态
    Array.prototype.forEach.call(aAnchor,function(item,index,array){
        item.className = ‘box-anchor‘;
    })
    //将当前目录项样式设置为点击状态
    obj.className = ‘box-anchor box-anchorActive‘;
}

var oBox = document.getElementById(‘box‘);
//设置目录内各组件的点击事件
oBox.onclick = function(e){
    e = e || event;
    var target = e.target || e.srcElement;
    //获取target的href值
    var sHref = target.getAttribute(‘href‘);
    //设置目录项的点击事件
    if(/anchor/.test(sHref)){
        anchorActive(target);
    }
}    

隐藏功能

  目录有时是有用的,但有时又是碍事的。所以,为目录添加一个关闭按钮,使其隐藏,目录内容全部消失,关闭按钮变成“显示目录”四个字。再次点击则完全显示

  由于后续的拖拽功能需要使用点击事件。所以,重新显示目录的事件使用双击实现

var oBox = document.getElementById(‘box‘);
//设置目录内各组件的点击事件
oBox.onclick = function(e){
    e = e || event;
    var target = e.target || e.srcElement;
    //设置关闭按钮的点击事件
    if(target.id == ‘boxQuit‘){
        target.innerHTML = ‘显示目录‘;
        target.style.background = ‘#3399ff‘;
        this.className = ‘box boxHide‘;
    }
}    

//设置关闭按钮的双击事件
var oBoxQuit = document.getElementById(‘boxQuit‘);
oBoxQuit.ondblclick = function(){
    this.innerHTML = ‘&times;‘;
    this.style.background = ‘‘;
    this.parentNode.className = ‘box‘;
}

滚轮功能

  当使用滚轮时,触发滚轮事件,当前目录对应可视区内相应的文章内容

//设置滚轮事件
var wheel = function(e){
    //获取列表项
    var aAnchor = oBox.getElementsByTagName(‘a‘);
    //获取章节题目项
    aTitle.forEach(function(item,index,array){
        //获取当前章节题目离可视区上侧的距离
        var iTop = item.getBoundingClientRect().top;
        //获取下一个章节题目
        var oNext = array[index+1];
        //如果存在下一个章节题目,则获取下一个章节题目离可视区上侧的距离
        if(oNext){
            var iNextTop = array[index+1].getBoundingClientRect().top;
        }
        //当前章节题目离可视区上侧的距离小于10时
        if(iTop <= 10){
            //当下一个章节题目不存在, 或下一个章节题目离可视区上侧的距离大于10时,设置当前章节题目对应的目录项为激活态
            if(iNextTop > 10 || !oNext){
                anchorActive(aAnchor[index]);
            }
        }
    });
}
document.body.onmousewheel = wheel;
document.body.addEventListener(‘DOMMouseScroll‘,wheel,false);

拖拽功能

  由于不同计算机的分辨率不同,所以目录的显示位置也不同。为目录增加一个拖拽功能,可以把其放在任意合适的地方

//拖拽实现
oBox.onmousedown = function(e){
    e = e || event;
    //获取元素距离定位父级的x轴及y轴距离
    var x0 = this.offsetLeft;
    var y0 = this.offsetTop;
    //获取此时鼠标距离视口左上角的x轴及y轴距离
    var x1 = e.clientX;
    var y1 = e.clientY;
    document.onmousemove = function(e){
        e = e || event;
        //获取此时鼠标距离视口左上角的x轴及y轴距离
        x2 = e.clientX;
        y2 = e.clientY;
        //计算此时元素应该距离视口左上角的x轴及y轴距离
        var X = x0 + (x2 - x1);
        var Y = y0 + (y2 - y1);
        //将X和Y的值赋给left和top,使元素移动到相应位置
        oBox.style.left = X + ‘px‘;
        oBox.style.top = Y + ‘px‘;
    }
    document.onmouseup = function(e){
        //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
        document.onmousemove = null;
        //释放全局捕获
        if(oBox.releaseCapture){
            oBox.releaseCapture();
        }
    }
    //阻止默认行为
    return false;
    //IE8-浏览器阻止默认行为
    if(oBox.setCapture){
        oBox.setCapture();
    }
}    

代码展示

//事件处理程序兼容写法
function addEvent(target,type,handler){
    if(target.addEventListener){
        target.addEventListener(type,handler,false);
    }else{
        target.attachEvent(‘on‘+type,function(event){
            return handler.call(target,event);
        });
    }
}
//DOM结构稳定后,再操作
addEvent(window,‘load‘, fnCata);

function fnCata(){
    /*动态样式*/
    function loadStyles(str){
        loadStyles.mark = ‘load‘;
        var style = document.createElement("style");
        style.type = "text/css";
        try{
            style.innerHTML = str;
        }catch(ex){
            style.styleSheet.cssText = str;
        }
        var head = document.getElementsByTagName(‘head‘)[0];
        head.appendChild(style);
    }
    if(loadStyles.mark != ‘load‘){
        loadStyles("h6{margin:0;padding:0;}            .box{position: fixed; left: 10px;top: 60px;font:16px/30px ‘宋体‘; border: 2px solid #ccc;padding: 4px; border-radius:5px;min-width:80px;max-width:118px;overflow:hidden;cursor:default;background:rgba(0,0,0,0.1);}            .boxHide{border:none;width:60px;height:30px;padding:0;}            .box-title{text-align:center;font-size:20px;color:#444;}            .box-quit{position: absolute;text-align:center; right: 0;top: 4px;cursor:pointer;font-weight:bold;}            .box-quitAnother{background:#3399ff;left:0;top:0;}            a.box-anchor{display:block;text-decoration:none;color:black; border-left: 3px solid transparent;padding:0 3px;margin-bottom: 3px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}            a.box-anchor:hover{color:#3399ff;}            a.box-anchorActive{color:#3399ff;text-decoration:underline;border-color:#2175bc};");
    };
    /*设置章节标题函数*/
    function setCatalog(){
        //获取页面中所有的script标题
        var aEle = document.getElementsByTagName(‘script‘);
        //设置sel变量,用于保存其选择符的字符串值
        var sel;
        //获取script标签上的data-selector值
        Array.prototype.forEach.call(aEle,function(item,index,array){
            sel = item.getAttribute(‘data-selector‘);
            if(sel) return;
        })
        //默认参数为h3标签
        if(sel == undefined){
            sel =‘h3‘;
        }
        //选取博文
        var article = document.getElementById(‘cnblogs_post_body‘);
        //选取文章中所有的章节标题
        var tempArray = article.querySelectorAll(sel);
        //为每一个章节标题顺序添加锚点标识
        Array.prototype.forEach.call(tempArray, function(item, index, array) {
              item.setAttribute(‘id‘,‘anchor‘ + (1+index));
        });
        //返回章节标题这个类数组
        return tempArray;
    }
    //设置全局变量Atitle保存添加锚点标识的标题项
    var aTitle = setCatalog();

    /*生成目录*/
    function buildCatalog(arr){
        //由于每个部件的创建过程都类似,所以写成一个函数进行服用
        function buildPart(json){
            var oPart = document.createElement(json.selector);
            if(json.id){oPart.setAttribute(‘id‘,json.id);}
            if(json.className){oPart.className = json.className;}
            if(json.innerHTML){oPart.innerHTML = json.innerHTML;}
            if(json.href){oPart.setAttribute(‘href‘,json.href);}
            if(json.appendToBox){
                oBox.appendChild(oPart);
            }
            return oPart;
        }
        //取得章节标题的个数
        len = arr.length;
        //创建最外层div
        var oBox = buildPart({
            selector:‘div‘,
            id:‘box‘,
            className:‘box‘
        });
        //创建关闭按钮
        buildPart({
            selector:‘span‘,
            id:‘boxQuit‘,
            className:‘box-quit‘,
            innerHTML:‘&times;‘,
            appendToBox:true
        });
        //创建目录标题
        buildPart({
            selector:‘h6‘,
            className:‘box-title‘,
            innerHTML:‘目录‘,
            appendToBox:true
        });
        //创建目录项
        for(var i = 0; i < len; i++){
            buildPart({
                selector:‘a‘,
                className:‘box-anchor‘,
                href:‘#anchor‘ + (1+i),
                innerHTML:‘[‘+(i+1)+‘]‘+arr[i].innerHTML,
                appendToBox:true
            });
        }
        //将目录加入文档中
        document.body.appendChild(oBox);
    }
    buildCatalog(aTitle);

    /*事件部分*/
    (function(){
        var oBox = document.getElementById(‘box‘);
        //设置目录内各组件的点击事件
        oBox.onclick = function(e){
            e = e || event;
            var target = e.target || e.srcElement;
            //设置关闭按钮的点击事件
            if(target.id == ‘boxQuit‘){
                target.innerHTML = ‘显示目录‘;
                target.className = ‘box-quit box-quitAnother‘
                this.className = ‘box boxHide‘;
            }
            //获取target的href值
            var sHref = target.getAttribute(‘href‘);
            //设置目录项的点击事件
            if(/anchor/.test(sHref)){
                anchorActive(target);
            }
        }    

        /*设置关闭按钮的双击事件*/
        var oBoxQuit = document.getElementById(‘boxQuit‘);
        oBoxQuit.ondblclick = function(){
            this.innerHTML = ‘&times;‘;
            this.className = ‘box-quit‘;
            this.parentNode.className = ‘box‘;
        }
        //由于点击事件和滚轮事件都需要将目录项发生样式变化,所以声明锚点激活函数
        function anchorActive(obj){
            var parent = obj.parentNode;
            var aAnchor = parent.getElementsByTagName(‘a‘);
            //将所有目录项样式设置为默认状态
            Array.prototype.forEach.call(aAnchor,function(item,index,array){
                item.className = ‘box-anchor‘;
            })
            //将当前目录项样式设置为点击状态
            obj.className = ‘box-anchor box-anchorActive‘;
        }

        //设置滚轮事件
        var wheel = function(e){
            //获取列表项
            var aAnchor = oBox.getElementsByTagName(‘a‘);
            //获取章节题目项
            aTitle.forEach(function(item,index,array){
                //获取当前章节题目离可视区上侧的距离
                var iTop = item.getBoundingClientRect().top;
                //获取下一个章节题目
                var oNext = array[index+1];
                //如果存在下一个章节题目,则获取下一个章节题目离可视区上侧的距离
                if(oNext){
                    var iNextTop = array[index+1].getBoundingClientRect().top;
                }
                //当前章节题目离可视区上侧的距离小于10时
                if(iTop <= 10){
                    //当下一个章节题目不存在, 或下一个章节题目离可视区上侧的距离大于10时,设置当前章节题目对应的目录项为激活态
                    if(iNextTop > 10 || !oNext){
                        anchorActive(aAnchor[index]);
                    }
                }
            });
        }
        document.body.onmousewheel = wheel;
        document.body.addEventListener(‘DOMMouseScroll‘,wheel,false);

    //拖拽实现
    oBox.onmousedown = function(e){
        e = e || event;
        //获取元素距离定位父级的x轴及y轴距离
        var x0 = this.offsetLeft;
        var y0 = this.offsetTop;
        //获取此时鼠标距离视口左上角的x轴及y轴距离
        var x1 = e.clientX;
        var y1 = e.clientY;
        document.onmousemove = function(e){
            e = e || event;
            //获取此时鼠标距离视口左上角的x轴及y轴距离
            x2 = e.clientX;
            y2 = e.clientY;
            //计算此时元素应该距离视口左上角的x轴及y轴距离
            var X = x0 + (x2 - x1);
            var Y = y0 + (y2 - y1);
            //将X和Y的值赋给left和top,使元素移动到相应位置
            oBox.style.left = X + ‘px‘;
            oBox.style.top = Y + ‘px‘;
        }
        document.onmouseup = function(e){
            //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
            document.onmousemove = null;
            //释放全局捕获
            if(oBox.releaseCapture){
                oBox.releaseCapture();
            }
        }
        //阻止默认行为
        return false;
        //IE8-浏览器阻止默认行为
        if(oBox.setCapture){
            oBox.setCapture();
        }
    }
    })();
};

转发自::http://www.cnblogs.com/xiaohuochai/

时间: 2024-10-04 22:50:50

自动生成博客目录的相关文章

构建基于Javascript的移动CMS——生成博客(二).路由

在有了上部分的基础之后,我们就可以生成一个博客的内容--BlogPosts Detail.这样就完成了我们这个移动CMS的几乎主要的功能了,有了上节想必对于我们来说要获取一个文章已经不是一件难的事情了. 获取每篇博客 于是我们照猫画虎地写了一个BlogDetail.js define([ 'jquery', 'underscore', 'mustache', 'text!/blog_details.html' ],function($, _, Mustache, blogDetailsTempl

android学习常用资料博客目录

android 技术总结 1.android 系统签名 介绍如何使用源码中的  (testkey:普通APK,默认情况下使用,platform:该APK完成一些系统的核心功能,经过对系统中存在的文件夹的访问测试,这种方式编译出来的APK所在进程的UID为system,shared:该APK需要和home/contacts进程共享数据,media:该APK是media/download系统中的一环.)4种密钥公钥进行签名. 2.MVC ListView 介绍什么是MVC以及MVC在SDK中的使用,

SQL Server博客目录

SQL Server博客目录 1 SQL Server数据库学习 2 GUID全局唯一标识符 3 SQL Server Profiler使用方法 4 Sql Server数据的加密与解密 5 SQL注入式攻击 6 SQL Server基本操作积累 7 Transact-SQL 语句 8 关系型数据库与非关系型数据库 9 在SQL Server 2012中新建用户 10 SQL Server基础 11 数据库表的设计 12 模糊查询&&日期时间操作 13 存储过程 14 Union-SQL

使用函数自动生成n层目录

先检查是否已经存在该目录了,如果存在,则不做任何处理,如果不存在则创建. 希望对各位快速开发有用. CheckFolder.asp <% '*********************************************************************************************************** '作 者: 赵敏 [email protected] '页面名称: CreateFolder.asp '页面功能: 生成n层目录的文件夹 '使用

LINQ博客目录

LINQ博客目录 1 LINQ to SQL 2 LINQ to DataSet 3 LINQ to XML

Helper博客目录

Helper博客目录 1 注释习惯 2 配置信息 3 MySQLHelper 4 UserLogin 5 UnitTest 6 MyException 7 WCF-Configuration

构建基于Javascript的移动CMS——生成博客(一)

在墨颀 CMS中的动态的文章是从我博客的API加载过来的,因为当前没有其他好的CMS当接口.之前直接拿博客的DB文件+Nodejs+RESTify生成了一个博客的API,而且可以支持跨域请求. 简单的博客构成 这次我们可以简单的做一个可以供移动平台阅读的博客,除了不能写作以外(ps:不能写作还能叫博客么).对于写博客的人来说更多的只是写,而对于读者来说,他们只需要读,所以在某种意义上可以将博客的写和读分离开来. 对于用户来说,博客是由两个页面构建的: 博文列表(blogposts list) 博

ASP.NET博客目录

ASP.NET博客目录 1 ASP.NET基础笔记 2 ASP.NET 学习笔记 3 [1]验证适配器 4 Web服务中的Session对象-学习

【博客目录】

这里是个人的学习笔记,主要目标是数学和算法.内容比较紧凑,仅供学习交流使用,并无意展开为详细教程. 因为有大量的数学公式,建议使用Chrome浏览器,公式上右键可设置放大的方法(比如单击放大). 数学_代数 [集合论] 01 - 集合还要论? 02 - 集合与自然数 03 - 序集和序数 [初等数论] 01 - 数学的皇后 02 - 整除与公约数 03 - 同余和剩余系 04 - 同余方程 05 - 指数和原根 06 - 不定方程 数学_分析 [实数系统] 01 - 万物皆数 02 - 实数构造