一,index.html文件
<!DOCTYPE html> <html lang="utf-8"> <head> <meta charset="UTF-8"> <title>下拉菜单</title> <link rel="stylesheet" href="css/base.css"> <link rel="stylesheet" type="text/css" href="css/common.css"> <link rel="stylesheet" type="text/css" href="css/index.css"> </head> <body> <div class="nav-site"> <div class="container"> <ul class="fl"> <li class="fl"><a href="javascript:;" class="nav-site-login">亲,请登录</a></li> <li class="fl"><a href="javascript:;" class="nav-site-item link">免费注册</a></li> <li class="fl"><a href="javascript:;" class="nav-site-item link">手机逛慕淘</a></li> </ul> <ul class="fr"> <li class="dropdown fl" data-active='site'> <a href="javascript:;" class="dropdown-toggle link">我的慕淘<i class="dropdown-arrow"></i></a> <ul class="dropdown-layer dropdown-left"> <li><a href="javascript:;" class="dropdown-item">已买到宝贝</a></li> <li><a href="javascript:;" class="dropdown-item">我的足迹</a></li> </ul> </li> <li class="dropdown fl" data-active='site'> <a href="javascript:;" class="dropdown-toggle link">收藏夹<i class="dropdown-arrow"></i></a> <ul class="dropdown-layer dropdown-left"> <li><a href="javascript:;" class="dropdown-item">收藏的宝贝</a></li> <li><a href="javascript:;" class="dropdown-item">收藏的店铺</a></li> </ul> </li> <li class="dropdown fl" data-active='site'> <a href="javascript:;" class="dropdown-catorage link">商品分类</a> </li> <li class="dropdown fl" data-active='site'> <a href="javascript:;" class="dropdown-toggle link">卖家中心<i class="dropdown-arrow"></i></a> <ul class="dropdown-layer dropdown-left" data-url='dropdown.json'> <li class="loading"></li> <!-- <li><a href="javascript:;" class="dropdown-item">免费开店</a></li> <li><a href="javascript:;" class="dropdown-item">已卖出的宝贝</a></li> <li><a href="javascript:;" class="dropdown-item">出售中的宝贝</a></li> <li><a href="javascript:;" class="dropdown-item">卖家服务市场</a></li> <li><a href="javascript:;" class="dropdown-item">卖家培训中心</a></li> <li><a href="javascript:;" class="dropdown-item">体验中心</a></li> --> </ul> </li> <li class="dropdown fl" data-active='site'> <a href="javascript:;" class="dropdown-toggle link">联系客服<i class="dropdown-arrow"></i></a> <ul class="dropdown-layer dropdown-right"> <li><a href="javascript:;" class="dropdown-item">消费者咨询</a></li> <li><a href="javascript:;" class="dropdown-item">卖家服务</a></li> </ul> </li> </ul> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script type="text/javascript"> window.jQuery || document.write('<script src="js/jquery.js"></script>'); </script> <script type="text/javascript" src="js/transition.js"></script> <script type="text/javascript" src="js/showHide.js"></script> <script type="text/javascript" src="js/dropdown.js"></script> <script type="text/javascript" src="js/index.js"></script> </body> </html>
二, common.css文件
/* ¹«¹²Ñùʽ */ .container { 1200px; } .link { color: #4d555d; } .link:hover { color: #f01414; } /* showHide */ .transition { -webkit-transition: all .5s; -moz-transition: all .5s; -ms-transition: all .5s; -o-transition: all .5s; transition: all .5s; } .fade { opacity: 0 !important; visibility: hidden !important; } .slideUpDown { height: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; } .slideLeftRight { 0 !important; padding-left: 0 !important; padding-right: 0 !important; } /* dropdown */ .dropdown{ position: relative; } .dropdown-toggle { display: inline-block; } .dropdown-arrow { display: inline-block; background-repeat: no-repeat; } .dropdown-layer { display: none; position: absolute; top: 100%; overflow: hidden; } /* .dropdown-item { display: block; white-space: nowrap; overflow: hidden; } */ .dropdown-left { left: 0; right: auto; } .dropdown-right { left: auto; right: 0; }
三, index.css文件
.nav-site{ 100%; background: #f3f5f7; } .nav-site > .container{ height: 44px; line-height: 44px; margin: 0 auto; border-bottom: 1px solid #cdd0d4; } .nav-site-login { margin-left: 15px; color: #f01414; } .nav-site-item { margin-left: 10px; } .nav-site .nav-site-item:nth-child(1) { margin-right: 16px; } /*dropdown*/ /* .dropdown{ position: relative; } */ .dropdown-catorage, .dropdown-toggle { /*display: inline-block;*/ padding: 0 16px 0 12px; border-left: 1px solid #f3f5f7; border-right: 1px solid #f3f5f7; } /* .dropdown-toggle:hover { background: #fff; border-left: 1px solid #cdd0d4; border-right: 1px solid #cdd0d4; } */ .dropdown-arrow { /*display: inline-block;*/ 8px; height: 6px; /*background: url('../img/dropdown-arrow.png') no-repeat;*/ background-image: url('../img/dropdown-arrow.png'); margin-left: 8px; } .dropdown-layer { /*display: none;*/ /*position: absolute;*/ /*top: 100%;*/ background: #f3f5f7; border: 1px solid #cdd0d4; border-top: none; } /* .dropdown:hover .dropdown-layer, .dropdown:hover .dropdown-toggle { background: #fff; } .dropdown:hover .dropdown-layer { display: block; } */ .dropdown-item { display: block; height:30px; line-height: 30px; padding-left: 12px; padding-right: 12px; white-space: nowrap; overflow: hidden; color: #4d555d; } /* .dropdown-left { left: 0; right: auto; } .dropdown-right { left: auto; right: 0; } */ .dropdown-item { background: #fff; } .dropdown-item:hover { background: #f3f5f7; } .site-active .dropdown-toggle { background: #fff; border-left: 1px solid #cdd0d4; border-right: 1px solid #cdd0d4; } .site-active .dropdown-layer { /*display: block;*/ background: #fff; }
四,导入jquery.js
五,transitionend.js文件
(function(){ // 'use strict';//在IE浏览器下,使用严格模式是不能定义多个变量的,所以不能使用严格模式 //判断样式是否支持该浏览器,可以在控制台输出 document.body.style[属性名] = ""; 则支持 //如果返回的 undefined 则不支持。 //因为调用未定义的对象,报错;使用未定义的属性,返回undefined var transitions = { '-webkit-transition':'webkitTransitionEnd', '-moz-transition':'mozTransitionEnd', '-webkit-transition':'webkitTransitionEnd', '-o-transition':'oTransitionEnd', 'transition':'transitionend' }; var value = ''; var isSupport = false; for(var p in transitions){ if (document.body.style[p] !== undefined) { value = transitions[p]; isSupport = true; break; } }; //暴露一个借口对象给外界调用 // window.mt = "" window.mt = undefined 进行隐式转换 都是false // 所以window.mt 只能是一个对象时才返回真 // 与找false,或找true window.mt = window.mt || {}; window.mt.transitionend = { transition:value, isSupport:isSupport }; })(jQuery)
六, showHide.js文件
(function(){ 'use strict'; //引用 transitionend 动画结束后执行的事件 var transitionend = window.mt.transitionend.transition; //所有的兼容性写法都可以添加一个附带属性,即表示浏览器是否支持 var isSupport = window.mt.transitionend.isSupport; //公共方法,高内聚低耦合,方法提取最小,所有都有 //初始化$ele对象,判断是否显示并设置对应的状态 function init($ele,callback){ if ($ele.is(':hidden')) { $ele.data('status','hidden'); typeof callback=== 'function' && callback(); }else{ $ele.data('status','shown'); } } //公共 show 设置事件开始前的状态并trigger其他事件 function show($ele,callback){ if ($ele.data('status')==='show' || $ele.data('status')==='shown') return; $ele.data('status','show').trigger('show'); typeof callback === 'function' && callback(); } //公共 hide 设置事件触发前的状态,并触发其他事件 function hide($ele,callback){ if ($ele.data('status')==='hide' || $ele.data('status')==='hidden') return; $ele.data('status','hide').trigger('hide'); typeof callback === 'function' && callback(); } //静静的显示隐藏,没有动画 var silent = { init:function($ele){ //初始化 init($ele); }, show:function($ele){ show($ele,function(){ $ele.show(); $ele.data('status','shown').trigger('shown'); }); }, hide:function($ele){ hide($ele,function(){ $ele.hide(); $ele.data('status','hidden').trigger('hidden'); }); } } //使用css3显示隐藏的动画 var css3 = { fade:{// fadeOut fadeIn 淡入淡出 init:function($ele){ css3._init($ele,'fade'); }, show:function($ele){ css3._show($ele,'fade'); }, hide:function($ele){ css3._hide($ele,'fade'); } }, slideUpDown:{ //slideUp slideDown 上下拉动 init:function($ele){ //千万记住如果$ele的宽高是由内容撑开,而没有设置固定宽高, //如果设置其中一个属性进行过度,那么一定要在初始化时先获取,且设置其宽高, //才能进行上下拉动或左右拉动的过渡效果,否则是没有过渡效果的显示隐藏 $ele.height($ele.height()); css3._init($ele,'slideUpDown'); }, show:function($ele){ css3._show($ele,'slideUpDown'); }, hide:function($ele){ css3._hide($ele,'slideUpDown'); } }, slideLeftRight:{ //jquery中没有定义,左右拉动 init:function($ele){ //千万记住如果$ele的宽高是由内容撑开,而没有设置固定宽高, //如果设置其中一个属性进行过度,那么一定要在初始化时先获取,且设置其宽高, //才能进行上下拉动或左右拉动的过渡效果,否则是没有过渡效果的显示隐藏 $ele.width($ele.width()); css3._init($ele,'slideLeftRight'); }, show:function($ele){ css3._show($ele,'slideLeftRight'); }, hide:function($ele){ css3._hide($ele,'slideLeftRight'); } }, fadeSlideUpDown:{ // fadeOut fadeIn + slideUp = slideDown 淡入淡出 + 上下拉动 init:function($ele){ //千万记住如果$ele的宽高是由内容撑开,而没有设置固定宽高, //如果设置其中一个属性进行过度,那么一定要在初始化时先获取,且设置其宽高, //才能进行上下拉动或左右拉动的过渡效果,否则是没有过渡效果的显示隐藏 $ele.height($ele.height()); css3._init($ele,'fade slideUpDown'); }, show:function($ele){ css3._show($ele,'fade slideUpDown'); }, hide:function($ele){ css3._hide($ele,'fade slideUpDown'); } }, fadeSlideLeftRight:{ //fadeOut fadeIn + 左右拉动 init:function($ele){ //千万记住如果$ele的宽高是由内容撑开,而没有设置固定宽高, //如果设置其中一个属性进行过度,那么一定要在初始化时先获取,且设置其宽高, //才能进行上下拉动或左右拉动的过渡效果,否则是没有过渡效果的显示隐藏 $ele.width($ele.width()); css3._init($ele,'slideLeftRight fade'); }, show:function($ele){ css3._show($ele,'slideLeftRight fade'); }, hide:function($ele){ css3._hide($ele,'slideLeftRight fade'); } } } /*谨记:下面的方法一定要写在 css3对象后面,因为使用的字面的方式定义函数, 浏览器在预解析时,css3._init = null; 如果css3对象不存在就会报错 即:不存在的对象报错,不存在的属性返回undefined*/ // _init 下划线的方法表示,css3对象内部使用的 css3._init = function($ele,className){ $ele.addClass('transition'); init($ele,function(){ $ele.addClass(className); }); } // _show 下划线的方法表示,css3对象内部使用的 css3._show = function($ele,className){ show($ele,function(){ $ele.show(); setTimeout(function(){ $ele.removeClass(className); },20); $ele.off(transitionend).one(transitionend,function(){ $ele.data('status','shown').trigger('shown'); }); }); } // _hide 下划线的方法表示,css3对象内部使用的 css3._hide = function($ele,className){ hide($ele,function(){ $ele.addClass(className); $ele.off(transitionend).one(transitionend,function(){ $ele.hide(); $ele.data('status','hidden').trigger('hidden'); }); }); } /*谨记:下面的方法一定要写在 css3对象后面,因为使用的字面的方式定义函数, 浏览器在预解析时,css3._init = null; 如果css3对象不存在就会报错 即:不存在的对象报错,不存在的属性返回undefined*/ //使用原生js进行显示隐藏 var js = { fade:{// fadeOut fadeIn 淡入淡出 init:function($ele){ js._init($ele,{ 'opacity':'0', 'visibility':'hidden' }); }, show:function($ele){ js._show($ele,{ 'opacity':'1', 'visibility':'visible' }); }, hide:function($ele){ js._hide($ele,{ 'opacity':'0', 'visibility':'hidden' }); } }, slideUpDown:{//slideUp slideDown 上下拉动 init:function($ele){ js._init($ele,{ 'height':'0', 'padding-top':'0', 'padding-bottom':'0' }); }, show:function($ele){ var styles = $ele.data('styles'); js._show($ele,{ 'height':styles['height'], 'padding-top':styles['padding-top'], 'padding-bottom':styles['padding-bottom'] }); }, hide:function($ele){ js._hide($ele,{ 'height':'0', 'padding-top':'0', 'padding-bottom':'0' }); } }, slideLeftRight:{//jquery中没有定义,左右拉动 init:function($ele){ js._init($ele,{ 'padding-left':'0', 'padding-right':'0' }); }, show:function($ele){ var styles = $ele.data('styles'); js._show($ele,{ 'width':styles['width'], 'padding-left':styles['padding-left'], 'padding-right':styles['padding-right'] }); }, hide:function($ele){ js._hide($ele,{ 'width':'0', 'padding-left':'0', 'padding-right':'0' }); } }, fadeSlideUpDown:{// fadeOut fadeIn + slideUp = slideDown 淡入淡出 + 上下拉动 init:function($ele){ js._init($ele,{ 'height':'0', 'opacity':'0', 'padding-top':'0', 'padding-bottom':'0' }); }, show:function($ele){ var styles = $ele.data('styles'); js._show($ele,{ 'height':styles['height'], 'opacity':styles['opacity'], 'padding-top':styles['padding-top'], 'padding-bottom':styles['padding-bottom'] }); }, hide:function($ele){ js._hide($ele,{ 'height':'0', 'opacity':'0', 'padding-top':'0', 'padding-bottom':'0' }); } }, fadeSlideLeftRight:{ //fadeOut fadeIn + 左右拉动 init:function($ele){ js._init($ele,{ 'width':'0', 'opacity':'0', 'padding-left':'0', 'padding-right':'0' }); }, show:function($ele){ var styles = $ele.data('styles'); js._show($ele,{ 'width':styles['width'], 'opacity':styles['opacity'], 'padding-left':styles['padding-left'], 'padding-right':styles['padding-right'] }); }, hide:function($ele){ js._hide($ele,{ 'width':'0', 'opacity':'0', 'padding-left':'0', 'padding-right':'0' }); } } } // _init 下划线的方法表示,js对象内部使用的 js._init = function($ele,options){ //这里特别注意,如果$ele标签中已经添加 transition 过渡类, //那么使用js时,显示效果就会有异常,所以一定记住这里要移除 $ele.removeClass('transition'); var styles = {}; for(var p in options){ styles[p] = $ele.css(p); } //把值存放在$ele对象中,在显示时可以提取出来使用 $ele.data('styles',styles); init($ele,function(){ $ele.animate(options); }) } // _show 下划线的方法表示,js对象内部使用的 js._show = function($ele,options){ show($ele,function(){ $ele.show(); setTimeout(function(){ $ele.stop().animate(options,function(){ $ele.data('status','shown').trigger('shown'); }); },20); }) } // _hide 下划线的方法表示,js对象内部使用的 js._hide = function($ele,options){ hide($ele,function(){ $ele.stop().animate(options,function(){ $ele.hide(); $ele.data('status','hidden').trigger('hidden'); }); }); } // 思路: // 1,返回一个对象 // showHide(options) // silent{show,hide} // css3.animation{show,hide} // js.animation{show,hide} // 2, showHide('string'); var defaults = { css3:false, js:false, animation:'fade' } function showHide($ele,option){ var mode = {};//模式 //如果css3=true ,且 支持 transitionend事件才执行 使用css3执行动画 if (option.css3 && isSupport) { //如果css3[options.animation]存在,则mode=css3[options.animation],否则等于默认值 mode = css3[option.animation] ? css3[option.animation] : css3[defaults.animation]; }else if (option.js) { mode = js[option.animation] ? js[option.animation] : js[defaults.animation]; }else{ mode = silent; } //初始化 mode.init($ele); return { show:$.proxy(mode.show,this,$ele), hide:$.proxy(mode.hide,this,$ele) } } window.mt = window.mt || {}; window.mt.showHide = showHide; //写成插件进行调用 $.fn.extend({ showHide:function(options){ //插件对象一定返回的是一个对象 //传对象进来时,一定记得遍历,因为有可能是一个数组对象 return $(this).each(function() { //注意:这里mode存取值,使用的是单例存取值, var mode = $(this).data('mode'); //$.extend({},a,b,c);处理参数,参数扩展, //后面对象的属性覆盖前面的属性(包括属性值),最后覆盖空值得到一个新对象 //即:c 覆盖 b 覆盖 a 覆盖 {} 最后得到一个新的对象参数 //typeof options === 'object' && options 如果参数options 为对象,则返回 options, //否则返回false,即:使用defaults 默认值 var option = $.extend({},defaults,typeof options ==='object' && options); //注意:这里mode存取值,使用的是单例存取值, //1,一开始就获取mode=$(this).data('mode'), //2,中间执行一段代码获取 mode //3,如果mode为空,则执行存值,并赋值,否则继续执行 if (!mode) { $(this).data('mode',mode = showHide($(this),option)); } //不要使用 typeof options === 'string',因为options[options] 可能是未定义的属性, //所以这里使用 typeof mode[options] === 'function' options是否是 mode 的函数属性 if (typeof mode[options] === 'function') { //执行 mode[options](); } }); } }) })(jQuery)
七, dropdown.js文件
(function($){ function Dropdown(ele,options){ //因为这里的参数在下面 init 方法中访问不到, //所以这里把options参数也设置为Dropdown的一个属性,那么就可以通过this进行访问了。 this.options = options; this.$ele = $(ele); //这里不能直接使用 this.eleLayer = $('.dropdown-layer'),这里获取的是所有的下拉层。 this.eleLayer = this.$ele.find('.dropdown-layer'); //判断是否有设定默认值 active ,如果有设定则使用设定好的值,否则使用下拉菜单对象中 data-actvie 的值 //使用下面长句也可以,只是处理参数,都把它们放到了,插件中的 $.extend 方法中一起处理了。这种方法优秀 // this.activeName = options.active !== undefined || this.$ele.data('active') + '-active'; this.activeName = options.active; //除了Dropdown属性外,其他所有的操作都应该放到init()函数中去,代表初始化 this.init(); } Dropdown.prototype.init = function(){ //初始化 this.eleLayer.showHide(this.options); var self = this; if (this.options.event === 'click') { this.$ele.on('click',function(e){ self.show(); //this.$ele 对象有单击事件,而 $(document)也绑定了单击事件,如果没有阻止事件冒泡, //那么会从this.$ele一直父级元素冒泡单击事件,直到window对象, //this.$ele的父级以上只要有单击事件都会触发。所以这里必须阻止事件冒泡 e.stopPropagation(); }) //单击除了 this.$ele对象外的所有地方都会触发 self.hide()事件 $(document).on('click',function(){ self.hide(); }) }else{//除了单击事件其他值默认都是hover事件 this.$ele.hover($.proxy(this.show,this), $.proxy(this.hide,this)); } /* this.$ele.hover(function(){ self.show(); },function(){ self.hide(); })*/ } //Dropdown本身就是一个对象,所以可以设置属性, //默认参数,都是一个一个添加上去的,只要会变的参数都暴露到插件中给用户自定义。 //在添加参数的思路可以是,从函数里面一步一步向外边推,或者也可以从用户调用参数时开始一步一步传递。 Dropdown.DEFAULTS = { css3:false, js:false, animation:'fade', event:'hover', active:'dropdown' } //面向对象编程时,一定先找出该对象的方法,然后使用原型链的方式进行声明 //因为在创建多个对象后,原型不会在堆内存中开辟空间存放 方法了, //而是是在原型中向上查找其他对象的方法,进而引用它们的方法。性能最佳。 //所以方法都是存放在原型中,属性都是放在构造函数中。 Dropdown.prototype.show = function(){ //注意点:本来this.activeName = 'site-active' 即:.site-active中 //使用 display:block;控制显示隐藏的,如果使用了showHide 显示隐藏插件, //一定记住把里面 display 的属性删除掉,这里的类只用于控制样式。 //显示隐藏的操作交给插件 showHide 来控制 this.$ele.addClass(this.activeName); this.eleLayer.showHide('show'); } //同上 Dropdown.prototype.hide = function(){ this.$ele.removeClass(this.activeName); this.eleLayer.showHide('hide'); } /* //这里是思路方法写的函数 ,上面面向对象是根据这个函数构造出来的下拉对象 function dropdown(ele) { var $ele = $(ele), $eleLayer = $ele.find('.dropdown-layer'); //初始化 $eleLayer.showHide({ css3:true, js:false, animation:'slideLeftRight' }); $ele.hover(function() { $ele.addClass('site-active'); $eleLayer.showHide('show'); // console.log($eleLayer.showHide('show')); }, function() { $ele.removeClass('site-active'); $eleLayer.showHide('hide'); }); }*/ //把面向对象Dropdown 写成插件的形式被调用 $.fn.extend({ dropdown:function(option){ return this.each(function(){ // dropdown(this); //$.extend({},a,b,c);处理参数,参数扩展, //后面对象的属性覆盖前面的属性(包括属性值),最后覆盖空值得到一个新对象 //即:c 覆盖 b 覆盖 a 覆盖 {} 最后得到一个新的对象参数 var options = $.extend({},Dropdown.DEFAULTS,$(this).data('active'),option); new Dropdown(this,options); }) } }) })(jQuery)
八,index.js文件
//这是最后指定$('.dropdown')对象调用插件 $('.dropdown').dropdown({ //指定使用哪种方式进行显示隐藏 css3:false, js:true, //具体怎么显示隐藏 animation:'fadeSlideLeftRight', /*默认只给了两个事件,如果不是click,那么其他值默认为hover事件,容错率高,即使传错参数也可以正常运行*/ event:'cdlick', /*index.html中鼠标悬浮在 $('.dropdown')标签添加的是 site-acitve,如果不是该类 那么就不会在 .dropdown-toggle 添加白色背景,或者可能出现其他异常, 如:使用dropdown整个导航在鼠标一进一出时就会左右移动一点点 ,请注意*/ active:'site' }); //获取下拉菜单jquery对象 var $dropdown = $('.dropdown') html = ''; //给下拉菜单绑定显示之前要触发的 dropdown-show 事件 $dropdown.on('dropdown-show',function(){ //在当前下拉菜单 $dropdown 下面找到下拉层 .dropdown-layer var $layer = $(this).find('.dropdown-layer'); var width = $(this).width()-2; //如果data-url 为 未定义,则不需要获取数据,即不需要加载下拉菜单, //需要手动在index.html中 卖家中心添加 data-url="需要加载的文件路径" if (!$layer.data('url')) return; //如果下拉层已经加载过了,有缓存,所以不需要继续加载。 if ($layer.data('loaded')) return; //这里使用异步调用模仿从服务器获取数据的延迟。 //$.getJSON 谷歌,火狐等主流浏览器不能在本地获取数据。 //但是IE11及以下是可以使用 $.getJSON 方法的,可以使用IE浏览器测试 //这里好像无法获取数据,思路是正确的,不知道哪里有问题。 setTimeout(function(){ $.getJSON($layer.data('url'),function(data){console.log(1); for (var i = 0; i < data.length; i++) { html += '<li><a href="' + data[i].url + ':;" class="menu-item link">' + data[i].name+ '</a></li>' } $layer.width(width); $layer.html(html); $layer.data('loaded',true); }) },1000); })
九,dropdown.json文件
[ { "name":"免费开店", "href":"javascript:;" }, { "name":"已卖出的宝贝", "href":"javascript:;" }, { "name":"出售中的宝贝", "href":"javascript:;" }, { "name":"卖家服务市场", "href":"javascript:;" }, { "name":"卖家培训中心", "href":"javascript:;" }, { "name":"体验中心", "href":"javascript:;" } ]