学习从实践开始之jQuery插件开发 菜单插件开发


Posted in Javascript onMay 03, 2012

虽然这不是多么高深的技术,但对于新手来说还是颇有难度。如果你是一个新手,我希望你能从本文中学到东西;如果你是高手,我希望你能留下你宝贵的意见和建议

一.要做什么插件?

我想要实现一个可以在网站或WEB应用系统中使用,可以灵活的定制外观、简单、易于使用、方便扩展、稳定的菜单插件。它可以被用在网站主导航条上,亦可以用在管理后台。

二.想要的效果是什么?

平时菜单处于收起状态,当鼠标移入显示其下级菜单,以此类推;可以方便的使用html标签设置菜单的结构,也可以使用数组动态生成。

三.设计一下功能

学习从实践开始之jQuery插件开发 菜单插件开发

对图片的说明
菜单项默认的状态。
拥有下级菜单且鼠标移入时的状态。
间隔(起到分组的效果)
拥有下级菜单,鼠标未移入时的状态。
竖排拥有下级菜单且鼠标移入时的状态。
获得焦点时的状态。
其他功能
菜单所有状态的样式均通过CSS控制,可以根据需要灵活修改。
通过HTML和javascript两种方式生成菜单。
为菜单项指定点击回调函数和跳转地址(当指定回调函数时,不设置URL地址,而是将URL地址传入回调函数)。
四.如何实现功能?

1.使用CSS样式控制外观。

*为了避免CSS命名冲突,我们需要为插件确定一个名字空间,其下所有样式都在该命名空间下。

2.菜单标签的选择

*一般来说实现菜单的标签多数会选择列表标签<ul><li></li></ul>来实现,我们也不例外。

菜单项:<li><a href="链接地址"><span>菜单项显示名称</span></a></li>

3.控制UL标签的显示方式

*使用CSS去掉符号和缩进

*使用CSS横向排列,横向排列有两种方法:

(1).用的比较多的是浮动排列(float:left;);但是这种方式有个最大的问题是会破坏页面结构,我不是很喜欢这种方式。

(2).使用内联(display:inline-block)的方式;目前已知的问题是低版本浏览器可能支持的不太好,这个问题网络上有专门的文章讨论,这里我就不再赘述了。

*当我在使用这种方式是出现了一个小问题,就是块与块之间有大概10px的空隙。我删除掉HTML代码里标签之间的空隙(换行)后,这些空隙消失了;这虽然可以解决问题,但是缺破坏了代码的结构,可读性差;如果是动态生成的还能接受。所以我想到了另一种解决办法,那就是设置每一个块(<li>标签)的的左边距为-10px;同时设置<ul>的左内距为10px,perfect!!!
五.浏览器兼容

未在IE6和IE7下进行相关测试。
六.功能实现和调用
样式控制

View Code 
/*为了避免命名冲突,我们将该插件所有样式都放在该类之下*/
.ctcx-menu
{
    font-size:14px;
}
.ctcx-menu ul
{
    list-style-type:none;
    margin:0;
    padding:0;
}
/*设置偏移量*/
.ctcx-menu ul.offset
{
    position:relative;
    top:-32px;
    left:100px;
}
.ctcx-menu ul li /*菜单项样式*/
{
    width:100px;
    height:30px;
    line-height:30px;
    text-align:center;
    vertical-align:top;
    margin:0;
    padding:0;
}
/*菜单项样式*/
.ctcx-menu a
{
    display:block;
    height:100%;
    border:1px solid #999;
    background-color:#FFF;
    text-decoration:none;
    color:#000;
}
.ctcx-menu a:hover
{
    background-color:#999;
    color:#FFF;
}
.ctcx-menu a:active{}
/*横向菜单*/
.ctcx-menu .horizontal
{
    padding-left:7px;
}
.ctcx-menu .horizontal li
{
    display:inline-block;
    margin-left:-7px;
}
.ctcx-menu .horizontal li.item-has-children > a /*拥有子菜单的菜单项样式*/ 
{
}
.ctcx-menu .horizontal li.spacing /*横向间隔*/
{
    height:30px;
    width:10px;
    background-color:#000;
}
/*竖向菜单*/
.ctcx-menu .vertical
{
}
.ctcx-menu .vertical li
{
    margin-left:0px;
}
.ctcx-menu .vertical li.item-has-children > a /*拥有子菜单的菜单项样式*/ 
{
}
.ctcx-menu .vertical li.spacing /*纵向间隔*/
{
    height:10px;
    width:100px;
    background-color:#000;
}

插件代码
View Code 
(function ($) {
    $.fn.menu = function (options) {
        if (typeof options != 'undefined' && options.constructor === Array) options = { data: options };
        var opts = $.extend({}, $.fn.menu.defaults, options);
        var _tempMenuData = [];
        //返回数据级别
        function getLevel(id) {
            var _level = 0;
            var _o = getMenuData(id);
            while (_o != null) {
                _level++;
                _o = getMenuData(_o.pid);
            }
            return _level;
        }
        //返回数据对象
        function getMenuData(id) {
            for (var i = 0; i < opts.data.length; i++) {
                if (opts.data[i].id == id)
                    return opts.data[i];
            }
            return null;
        }
        //返回生成的HTML
        function getHtml(pid) {
            var _li_data = getData(pid);
            if (_li_data.length == 0) return null;
            var _ul = $('<ul></ul>');
            $.each(_li_data, function (i, _d) {
                var _children = getHtml(_d.id);
                var _li = $('<li></li>').appendTo(_ul);
                if (_d.n == null || _d.n.length == 0) {
                    _li.addClass('spacing');
                } else if (typeof _d.fn === 'function') {
                    $('<a href="javascript:;"></a>').html(_d.n)
                    .click(function () {
                        _d.fn(_d.url);
                    }).appendTo(_li);
                } else if (_d.url.length > 0) {
                    $('<a href="' + _d.url + '"></a>').html(_d.n).appendTo(_li);
                }
                if (_children != null) {
                    _li.addClass('item-has-children');
                    _children.appendTo(_li);
                    _li.bind({
                        mouseover: function () {
                            _children.show();
                        },
                        mouseout: function () {
                            _children.hide();
                        }
                    });
                }
            })
            if (pid == null && opts.type == 1) {
                _ul.addClass('horizontal');
            } else {
                var _level = getLevel(pid);
                _level > 0 && _ul.hide();
                _ul.addClass('vertical');
                if (_level > opts.type)
                    _ul.addClass('offset');
            }
            return _ul;
        }
        //返回下级数据数组
        function getData(pid) {
            var _data = [];
            _tempMenuData = $.grep(_tempMenuData, function (_d) {
                if (_d.pid == pid) {
                    _data.push(_d);
                    return true;
                }
                return false;
            }, true);
            return _data;
        }
        return this.each(function () {
            var me = $(this);
            me.addClass('ctcx-menu');
            if (opts.data != null && opts.data.length > 0) {
                $.merge(_tempMenuData, opts.data);
                me.append(getHtml(null));
            } else {
                me.find('.item-has-children').each(function () {
                    var self = $(this);
                    var _ul = self.children('ul');
                    _ul.hide();
                    self.bind({
                        mouseover: function () {
                            _ul.show();
                        },
                        mouseout: function () {
                            _ul.hide();
                        }
                    });
                });
            }
        });
    }
    //设置默认参数
    $.fn.menu.defaults = {
        type: 1, //菜单的显示方式(主要是指第一级是横向还是纵向,默认横向1,纵向0)
        /*
        data:动态生成菜单的数组数据,如果指定的此数据则会以此数据填充菜单(菜单内原有数据被替代)
        数据格式:[menu,menu,...]
        menu对象格式:{ id: 1, pid: null, n: '菜单名称1', url: '#', fn:回调函数 }
        */
        data: null
    }
})(jQuery);

调用JS代码
View Code 
$(function () {
                var _menuData = [
                    { id: 1, pid: null, n: '菜单名称1', url: '#' },
                    { id: 2, pid: null, n: '菜单名称2', url: '#' },
                    { id: 3, pid: null, n: '菜单名称3', url: '#' },
                    { id: 4, pid: null, n: '菜单名称4', url: '#' },
                    { id: 5, pid: null, n: '菜单名称5', url: '#' },
                    { id: 6, pid: 3, n: '菜单名称6', url: '#' },
                    { id: 7, pid: 3, n: '菜单名称7', url: '#' },
                    { id: 8, pid: 3, n: '菜单名称8', url: '#' },
                    { id: 9, pid: 3, n: '菜单名称9', url: '#' },
                    { id: 10, pid: 9, n: '菜单名称10', url: '#' },
                    { id: 11, pid: 9, n: '菜单名称11', url: '#' },
                    { id: 12, pid: 9, n: '菜单名称12', url: '#' },
                    { id: 13, pid: 9, n: '菜单名称13', url: '#' },
                    { id: 14, pid: 13, n: '菜单名称14', url: '#' },
                    { id: 15, pid: 1, n: '菜单名称15', url: '#' }
                ];
                $('#dynamic-menu1').menu({ type: 0, data: _menuData });
                $('#dynamic-menu2').menu();
                $('#dynamic-menu3').menu();
            });

HTML
View Code 
<div id="dynamic-menu3" class="ctcx-menu">
                <ul class="horizontal">
                    <li><a href="#"><span>一级菜单1</span></a></li>
                    <li><a href="#"><span>一级菜单2</span></a></li>
                    <li class="item-has-children">
                        <a href="#"><span>一级菜单3</span></a>
                        <ul class="vertical">
                            <li><a href="#"><span>二级菜单1</span></a></li>
                            <li><a href="#"><span>二级菜单2</span></a></li>
                            <li><a href="#"><span>二级菜单3</span></a></li>
                            <li class="item-has-children">
                                <a href="#"><span>二级菜单4</span></a>
                                <ul class="vertical offset">
                                    <li><a href="#"><span>三级菜单1</span></a></li>
                                    <li><a href="#"><span>三级菜单2</span></a></li>
                                    <li><a href="#"><span>三级菜单3</span></a></li>
                                    <li><a href="#"><span>三级菜单4</span></a></li>
                                    <li><a href="#"><span>三级菜单5</span></a></li>
                                </ul>
                            </li>
                            <li><a href="#"><span>二级菜单5</span></a></li>
                        </ul>
                    </li>
                    <li><a href="#"><span>一级菜单4</span></a></li>
                    <li><a href="#"><span>一级菜单5</span></a></li>
                </ul>
            </div>
            <div id="dynamic-menu1" class="ctcx-menu" style="margin-top:30px;"></div>
            <div id="dynamic-menu2" class="ctcx-menu" style="margin-top:60px;">
                <ul class="vertical">
                    <li><a href="#"><span>一级菜单1</span></a></li>
                    <li><a href="#"><span>一级菜单2</span></a></li>
                    <li class="item-has-children">
                        <a href="#"><span>一级菜单3</span></a>
                        <ul class="vertical offset">
                            <li><a href="#"><span>二级菜单1</span></a></li>
                            <li><a href="#"><span>二级菜单2</span></a></li>
                            <li><a href="#"><span>二级菜单3</span></a></li>
                            <li class="item-has-children">
                                <a href="#"><span>二级菜单4</span></a>
                                <ul class="vertical offset">
                                    <li><a href="#"><span>三级菜单1</span></a></li>
                                    <li><a href="#"><span>三级菜单2</span></a></li>
                                    <li><a href="#"><span>三级菜单3</span></a></li>
                                    <li><a href="#"><span>三级菜单4</span></a></li>
                                    <li><a href="#"><span>三级菜单5</span></a></li>
                                </ul>
                            </li>
                            <li><a href="#"><span>二级菜单5</span></a></li>
                        </ul>
                    </li>
                    <li><a href="#"><span>一级菜单4</span></a></li>
                    <li><a href="#"><span>一级菜单5</span></a></li>
                </ul>
            </div>

七.下载
点击这里 下载使用例子,和所有文件。
Javascript 相关文章推荐
js修改table中Td的值(定义td的双击事件)
Jan 10 Javascript
初步认识JavaScript函数库jQuery
Jun 18 Javascript
JS显示日历和天气的方法
Mar 01 Javascript
BootStrap使用file-input插件上传图片的方法
Sep 05 Javascript
微信小程序 wx.uploadFile在安卓手机上面the same task is working问题解决
Dec 14 Javascript
JavaScript实现无刷新上传预览图片功能
Aug 02 Javascript
动态统计当前输入内容的字节、字符数的实例详解
Oct 27 Javascript
vue-router配合ElementUI实现导航的实例
Feb 11 Javascript
vue 监听屏幕高度的实例
Sep 05 Javascript
js根据json数据中的某一个属性来给数据分组的方法
Oct 08 Javascript
layui switch 开关监听 弹出确定状态转换的例子
Sep 21 Javascript
ant design实现圈选功能
Dec 17 Javascript
Firefox中beforeunload事件的实现缺陷浅析
May 03 #Javascript
统计jQuery中各字符串出现次数的工具
May 03 #Javascript
JQuery插件Style定制化方法的分析与比较
May 03 #Javascript
拉动滚动条加载数据的jquery代码
May 03 #Javascript
基于jquery的固定表头和列头的代码
May 03 #Javascript
一个基于jQuery的树型插件(OrangeTree)使用介绍
May 03 #Javascript
ASP.NET中AJAX 调用实例代码
May 03 #Javascript
You might like
三国漫画《火凤燎原》宣布动画化PV放出 预计2020年播出
2020/03/08 国漫
PHP数据缓存技术
2007/02/14 PHP
PHP性能优化 产生高度优化代码
2011/07/22 PHP
PHP中if和or运行效率对比
2014/12/12 PHP
PHP编程开发怎么提高编程效率 提高PHP编程技术
2015/11/09 PHP
php简单统计中文个数的方法
2016/09/30 PHP
PHP截取发动短信内容的方法
2017/07/04 PHP
PHP unlink与rmdir删除目录及目录下所有文件实例代码
2018/02/07 PHP
Laravel5.5以下版本中如何自定义日志行为详解
2018/08/01 PHP
PHP使用 Imagick 扩展实现图片合成,圆角处理功能示例
2019/09/09 PHP
PHP通过文件保存和更新信息的方法分析
2019/09/12 PHP
juqery 学习之三 选择器 可见性 元素属性
2010/11/25 Javascript
JQuery学习笔记 nt-child的使用
2011/01/17 Javascript
css transform 3D幻灯片特效实现步骤解读
2013/03/27 Javascript
解析js中获得父窗口链接getParent方法以及各种打开窗口的方法
2013/06/19 Javascript
JavaScript改变HTML元素的样式改变CSS及元素属性
2013/11/12 Javascript
JavaScript中使用document.write向页面输出内容实例
2014/10/16 Javascript
javascript小数精度丢失的完美解决方法
2016/05/31 Javascript
预防网页挂马的方法总结
2016/11/03 Javascript
浅谈使用mpvue开发小程序需要注意和了解的知识点
2018/05/23 Javascript
React Hooks的深入理解与使用
2018/11/12 Javascript
js比较两个单独的数组或对象是否相等的实例代码
2019/04/28 Javascript
JQuery属性操作与循环用法示例
2019/05/15 jQuery
[01:02:00]DOTA2-DPC中国联赛 正赛 Elephant vs IG BO3 第三场 1月24日
2021/03/11 DOTA
python统计文本文件内单词数量的方法
2015/05/30 Python
Flask框架的学习指南之制作简单blog系统
2016/11/20 Python
Python实现扣除个人税后的工资计算器示例
2018/03/26 Python
调用其他python脚本文件里面的类和方法过程解析
2019/11/15 Python
美国和加拿大房车出售在线分类广告:RVT.com
2018/04/23 全球购物
澳大利亚天然护肤品、化妆品和健康产品一站式商店:Nourished Life
2018/12/02 全球购物
数据库笔试题
2013/05/09 面试题
网球场地租赁协议范本
2014/10/07 职场文书
个人政治思想总结
2015/03/05 职场文书
创业计划书之暑假培训班
2019/11/09 职场文书
react 项目中引入图片的几种方式
2021/06/02 Javascript
html2 canvas svg不能识别的解决方案
2021/06/03 HTML / CSS