dropdown中的this

来源:3-3 用构造函数的形式重写dropdown模块--触发方式和延迟显示

soso_crazy

2019-04-04 12:03:43

<!DOCTYPE html>

<html lang="zh-CN">


<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<link rel="stylesheet" href="../../作业素材/code/css/base.css">

<title>下拉菜单</title>

<style>

/* 下拉菜单的共性 */

.dropdown {

position: relative;

}

.dropdown-toggle {

position: relative;

/* 有position才能生效z-index */

z-index: 2;

/* dropdown-toggle的曾经比dropdown-layer低,想遮住底部边框,需要将dropdown-toggle层级高过dropdownlayer */

}

.dropdown-arrow {

display: inline-block;

background-repeat: no-repeat;

vertical-align: middle;

}

.dropdown-layer {

overflow: hidden;

display: none;

position: absolute;

z-index: 1;

}

.dropdown-left {

left: 0;

/* 设置left和right 会有一个无法实现 */

right: auto;

}

.dropdown-right {

right: 0;

left: auto;

}

/* 下拉菜单的特性 */

.menu .dropdown-toggle {

display: block;

height: 100%;

/* 继承父容器 */

padding: 0 16px 0 12px;

/* 没有设置固定宽度,设置padding值,宽度由内容和padding撑开 */

border-left: 1px solid #f3f5f7;

border-right: 1px solid #f3f5f7;

}

.menu .dropdown-arrow {

/* width: 8px;

           height: 6px;

           background-image: url('../img/dropdown-arrow.png'); 箭头由css画不需引入图片减少http请求*/

margin-left: 8px;

/* 箭头和文字的距离 */

}

.menu .dropdown-layer {

top: 100%;

/* 延伸出来的层移上一点遮住dropdown-toggle底部边框 */

background-color: #ffffff;

border: 1px solid #cdd0d4;

}

.menu-item {

display: block;

height: 30px;

line-height: 30px;

color: #4d555d;

padding: 0 12px;

white-space: nowrap;

/* 强制文字不换行 */

}

.menu-item:hover {

background-color: #f3f5f7;

}

/* IE6只有a标签有hover,如果不用兼容IE6可以直接用伪类hover */

.menu-active .dropdown-toggle

/* 用js添加.dropdown-active是和.menu同级,所以.menu.dropdown-active既有.menu也有.dropdown-active */

{

background-color: #ffffff;

border-color: #cdd0d4;

}

.menu-active .dropdown-arrow {

/* background-image: url(../img/dropdown-arrow-active.png); */

}

.icon-triangle-down {

/* 只能画实心下拉箭头 */

width: 0;

height: 0;

border: 4px solid #535b62;

border-right-color: transparent;

border-left-color: transparent;

border-bottom: none;

}

.menu-active .icon-triangle-down {

border-top: none;

border-bottom: 4px solid #f11c1c;

}

/* 使用图标字体做下拉箭头 */

/* 图标字体优点:1.矢量图 不失真 2.减少http请求 3.兼容性好 4.操作简单 */

/* 图标字体缺点:1.基本用作小图标 2.有时无法还原设计稿 */

/* 下拉箭头旋转 */

[class*="-active"].dropdown-arrow {

transform: rotate(180deg);

-webkit-transform: rotate(180deg);

-moz-transform: rotate(180deg);

-ms-transform: rotate(180deg);

-o-transform: rotate(180deg);

}

.transition {

transition: all 0.5s;

-webkit-transition: all 0.5s;

-moz-transition: all 0.5s;

-ms-transition: all 0.5s;

-o-transition: all 0.5s;

}

.fadeOut {

visibility: visible;

opacity: 0;

}

.slideUpDownCollapse {

/* 当移除了这个类,高度就会显示为原本高度 */

height: 0 !important;

/* id的优先级比class高,所以#box的高度会覆盖这里的类高度,加上!important使得优先级最高 */

padding-top: 0 !important;

/* 如果是靠内容和padding撑开高度,就设置padding-top和padding-bottom */

padding-bottom: 0 !important;

}

.slideLeftRightCollapse {

/* 当移除了这个类,高度就会显示为原本宽度 */

width: 0 !important;

/* id的优先级比class高,所以#box的宽度会覆盖这里的类宽度,加上!important使得优先级最高 */

padding-left: 0 !important;

/* 如果是靠内容和padding撑开宽度,就设置padding-left和padding-right */

padding-right: 0 !important;

}

</style>

</head>


<body>

<!-- 最基本的下拉菜单结构 -->

<!-- <div class="menu dropdown">

       <div class="dropdown-toggle">我的慕多多<i class="dropdown-arrow"></i></div>

       <div class="dropdown-layer"></div>

   </div> -->


<div class="menu dropdown fl" data-active="menu">

<a href="javascript:;" class="dropdown-toggle link">收藏夹<i class="dropdown-arrow icon-triangle-down transition"></i></a>

<ul class="dropdown-layer dropdown-left">

<li><a href="###" target="_blank" class="menu-item">收藏的宝贝</a></li>

<li><a href="###" target="_blank" class="menu-item">收藏的店铺</a></li>

</ul>

</div>


<div class="menu dropdown fl" data-active="menu">

<a href="javascript:;" class="dropdown-toggle link">收藏夹<i class="dropdown-arrow icon-triangle-down"></i></a>

<ul class="dropdown-layer dropdown-left">

<li><a href="###" target="_blank" class="menu-item">收藏的宝贝</a></li>

<li><a href="###" target="_blank" class="menu-item">收藏的店铺</a></li>

</ul>

</div>

<script src="../js/jquery-3.3.1.js"></script>

<script src="../js/dropdown.js"></script>

<script src="../js/transition.js"></script>

<script src="../js/showhide模块.js"></script>


<!-- <script>

       $('.dropdown').hover(function() {

           var $this = $(this);

           $this.addClass($this.data('active') + '-active');

       }, function() {

           var $this = $(this);

           $this.removeClass($this.data('active') + '-active');

       });


       function dropdown(elem) {

           var $elem = $('elem'),

               activeClass = $elem.data('active') + '-active';

           $elem.hover(function() {

               $elem.addClass(activeClass);

           }, function() {

               $elem.removeClass(activeClass);

           });

       }


       $fn.extend({

           dropdown:function(){

               return this.each(function(){

                   dropdown(this);

               })

           }

       })


       $('dropdown').dropdown();

   </script> -->

<script>

$('.dropdown').dropdown({

event: 'click',

css3: true,

js: true,

// animation: 'fade' 如果忘记传入参数,有defaults默认参数

active: 'menu'

}); //封装成模块,通过属性名dropdown调用

</script>


</body>


</html>


//dropdown.js

//封装成模块 ()()即是自我执行的匿名函数

(function($) {

'use strict';


function Dropdown(elem, options) {

this.$elem = $(elem), //传入的参数是变量,使用的时候不能添加引号 this相当于dropdown

this.options = options, //options是Dropdown的参数,相当于Dropdown里的局部变量,无法在Dropdown.prototype.show里引用,所以要保存Dropdown对象下的属性以便能够公共使用

this.$layer = this.$elem.find('.dropdown-layer'), //获取到下拉的层

this.activeClass = options.active + '-active';

this.$layer.showHide(options);


// this.$layer.on('show shown hide hidden',function(){


// });


var self = this; //Dropdown就是this,将Dropdown保存在self

if (options.event === 'click') {

this.$elem.on('click', function(e) { //e代表事件对象

self.show();

e.stopPropagation(); //阻止事件冒泡 因为a标签也是在document里,点击事件会冒泡到document,使得一点击就消失(冒泡到document,等于点击了document,即相当于点击了空白地方)

});

$(document).on('click', $.proxy(this.hide, this));

} else { //不使用else if的原因是以防传错options.event的参数,使得程序出错

this.$elem.hover($.proxy(this.show, this), $.proxy(this.hide, this));

//$.proxy(第一个参数是Dropdown.show方法,第二个是Dropdown) 改变this的指向为Dropdown  $.proxy(this.show,this)执行结果仍然是函数,只是this指向为Dropdown

}

}


//$('#box').click(function(){}) this的指向是box

// this.$elem.hover(this.show,this.hide) this的指向是$elem而不是Dropdown,没有了Dropdown的show和hide方法 报错,所以要用$.proxy()


Dropdown.DEFAULTS = {

event: 'hover', //event有hover或者click选择

css3: false,

js: false,

animation: 'fade',

delay: 0,

active: 'dropdown',


};


Dropdown.prototype.show = function() {

var self = this;

if (this.options.delay) {

this.timer = setTimeout(function() { //为了能够在Dropdown.prototype.hide清楚定时,所以要放在this.timer公共使用

_show();

}, this.options.delay);

} else { //如果delay是0,不会有任何延迟,立刻执行

_show();

}


function _show() {

self.$elem.addClass(this.activeClass); //用self保存Dropdown,this就是Dropdown。直接用this就指向window

self.$layer.showHide('show');

}

};


Dropdown.prototype.hide = function() {

if (this.options.delay) {

clearTimeout(this.timer);

}

this.$elem.removeClass(this.activeClass);

this.$layer.showHide('hide');

};


// var dropdown = new Dropdown(); //new 构造函数,就是生成一个对象dropdown


// 当不断实例化后,生成不同的对象,但是拥有共同的方法,在堆内存中占用内存影响性能,所以用原型的方法

//无论实例化多少次,都只是执行Dropdown Dropdown的原型是不会重新执行


// dropdown.show(); //对象有两个方法,show hide

// dropdown.hide();


// function dropdown(elem, options) {

//     var $elem = $(elem), //传入的参数是变量,使用的时候不能添加引号

//         $layer = $elem.find('.dropdown-layer'), //获取到下拉的层

//         activeClass = $elem.data('active') + '-active';

//     // $elem.data('active')获取的是data-active属性中值menu

//     //active是data-后面的名称,data-active这种形式的,可以直接使用data方法通过-后面的active名字获取属性值。    data-active="menu"

//     // 后面的+ '-active'是拼接字符串,也就是将获取的menu与active拼接:menu-active


//     $layer.showHide(options);


//     $elem.hover(function() {

//         $elem.addClass(activeClass);

//         $layer.showHide('show');

//     }, function() {

//         $elem.removeClass(activeClass);

//         $layer.showHide('hide');

//     });

// }


// var defaults = {

//     css3: false,

//     js: false,

//     animation: 'fade'

// };



$.fn.extend({ //插件形式

// $.fn.extend() 函数为jQuery扩展一个或多个实例属性和方法(主要用于扩展方法)。

// jQuery.fn是jQuery的原型对象,其extend()方法用于为jQuery的原型添加新的属性和方法,这些方法可以在jQuery实例对象上调用。


dropdown: function(option) { //option参数传入到dropdown中

//dropdown插件名  将上面写的dropdown方法扩展到jQuery原型上,属性名是dropdown,也可定义其他

// 为了能连缀. .链接下去,要return this

return this.each(function() { // 这里的this是获取到的所有元素,例如$('.dropdown') 是jQuery对象

//调用方法: $('dropdown').dropdown(); $('dropdown')就是this可能是单数,也可能是复数,所以要遍历


var options = $.extend({}, Dropdown.DEFAULTS, $(this).data(), option) //option覆盖defaults中相同的属性,放到空对象中用options变量保存数据 $(this).data()放在option前,如果option中没有传active,默认值就是$(this).data()的值


// $(this).data()的值就是html标签中data的值  $(this).data()也没有就是Dropdown.DEFAULTS默认参数


new Dropdown(this, options); //这里的this是each的时候每一个元素 每一个DOM元素

// 将获取到的元素做一个遍历,然后将遍历的结果执行dropdown的方法

});

}

});


})(jQuery); //(jquery)用$接收 防止引入第三方库或自己讲$覆盖掉,用$出现错误



//transition.js

(function() {

var transitionEndEventName = {

transition: 'transitionend',

MozTransition: 'transitionend',

WebkitTransition: 'WebkitTransitionEnd',

OTransition: 'oTransitionEnd otransitionend'

};


var transitionEnd = '', //在自执行的匿名函数中定义的是局部作用域

isSupport = false; //在自执行的匿名函数中定义的是局部作用域


//一般都存在body元素,如果不用body判断是否有transitionend,可以用document.create('DOM元素'),再用document.创建的DOM元素.style[name]

for (var name in transitionEndEventName) {

var ele = document.body || document.documentElement;

if (ele.style[name] !== undefined) { //存在transitionend

transitionEnd = transitionEndEventName[name];

isSupport = true;

break; //如果找到就无需继续遍历,立刻跳出

}

}


//全局作用域中暴露获得的结果

window.muduoduo = window.muduoduo || {}; //如果window存在muduoduo就用muduoduo 本身,原来不存在第一次用就是一个空对象

window.muduoduo.transition = {

end: transitionEnd,

isSupport: isSupport

};

})();



//showhide模块js

(function($) { //将showhide封装成模块:放在自执行的匿名函数中

'use strict';


var transition = window.muduoduo.transition;


function init($elem, hiddenCallback) {

if ($elem.is(':hidden')) {

$elem.data('status', 'hidden');

if (typeof hiddenCallback === 'function') {

hiddenCallback();

}

} else {

$elem.data('status', 'shown');

}

};


function show($elem, Callback) {

if ($elem.data('status') === 'show') return; //当已是显示状态,不再重复执行show的代码

if ($elem.data('status') === 'shown') return; //当已是显示状态,不再重复执行shown的代码

$elem.data('status', 'show').trigger('show'); // 第一次没执行状态,status是undefined,当执行完第一次之后,status是show 不再重复执行代码

Callback();

};


function hide($elem, Callback) {

if ($elem.data('status') === 'hide') return; //当已是隐藏状态,不再重复执行hide的代码

if ($elem.data('status') === 'hidden') return; //当已是隐藏状态,不再重复执行hidden的代码

$elem.data('status', 'hide').trigger('hide');

Callback();

};


var silent = {

init: init, //初始化函数名init,函数名等于函数本身

show: function($elem) {

show($elem, function() {

$elem.show();

$elem.data('status', 'shown').trigger('shown');

});

},

hide: function($elem) {

hide($elem, function() {

$elem.hide();

$elem.data('status', 'hidden').trigger('hidden');

})

}

};

var css3 = {

fade: {

init: function($elem) {

css3._init($elem, 'fadeOut');

},

show: function($elem) {

css3._show($elem, 'fadeOut');

},

hide: function($elem) {

css3._hide($elem, 'fadeOut');

}

},

slideUpDown: {

init: function($elem) {

$elem.height($elem.height()); /* 获取高度,并把获取到的高度设置给到jq元素的高度 */

/* 当$elem没有设置高度,由内容撑开时,slideUpDownCollapse将高度变为0有过渡效果 */

css3._init($elem, 'slideUpDownCollapse');

},

show: function($elem) {

css3._show($elem, 'slideUpDownCollapse');

},

hide: function($elem) {

css3._hide($elem, 'slideUpDownCollapse');

}

},

slideLeftRight: {

init: function($elem) {

$elem.width($elem.width()); /* 获取宽度,并把获取到的宽度设置给到jq元素的高度 */

/* 当$elem没有设置宽度,由内容撑开时,slideLeftRightCollapse将高度变为0有过渡效果 */

css3._init($elem, 'slideLeftRightCollapse');

},

show: function($elem) {

css3._show($elem, 'slideLeftRightCollapse');

},

hide: function($elem) {

css3._hide($elem, 'slideLeftRightCollapse');

}

},

fadeSlideUpDown: {

show: function() {},

hide: function() {}

},

fadeSlideLeftRight: {

show: function() {},

hide: function() {}

}

};


css3._init = function($elem, className) {

$elem.addClass('transition');

init($elem, function() { //传入参数为jq对象,回调函数

$elem.addClass(className); //当初始状态是隐藏时,添加class

});

};


css3._show = function($elem, className) {

show($elem, function() {

// 如果当前动画没有执行完毕,就执行另一个动画,那么之前的动画是不会停止的,会继续执行,直到结束,所以有的时候显示之后突然显示,就是之前消失的动画没有执行完毕。 为了解决这个问题,要将jq元素解绑过渡动画,再绑定一次过渡

$elem.off(transition.end).one(transition.end, function() { //transition.end是transition.js中的属性值

$elem.trigger('shown'); //执行自定义的shown方法

});

$elem.show(); //$elem.show();和$elem.css执行顺序一前一后,但是执行非常快,可以认为同步执行,不存在过渡效果,讲它们异步执行setTimeout

// transition过渡时遇到没有效果,可以考虑是否异步问题

setTimeout(function() {

$elem.removeClass(className);

}, 20);

});

};


css3._hide = function($elem, className) {

hide($elem, function() {

$elem.off(transition.end).one(transition.end, function() { //transition结束有transitionend事件

// 绑定事件在触发事件之前

// one()只绑定一次时间 之前解绑时间,即当快速按下显示和隐藏按钮时,show shown hide hidden不会按顺序执行,可能会没有shown

$elem.hide();

$elem.data('status', 'hidden').trigger('hidden');

});

$elem.addClass(className);

});

};


var js = {

fade: {

init: function($elem) {

js._init($elem);

},

show: function($elem) {

js._show($elem, 'fadeIn');

},

hide: function($elem) {

js._hide($elem, 'fadeOut');

}

},

slideUpDown: {

init: function($elem) {

js._init($elem);

},

show: function($elem) {

js._show($elem, 'slideDown');

},

hide: function($elem) {

js._hide($elem, 'slideUp');

}

},

slideLeftRight: {

init: function($elem) { //初始化时获取元素最初始的宽度

js._init($elem, {

'width': '0',

'padding-left': '0',

'padding-right': '0'

})


},

show: function($elem) {

js._customShow($elem);

},

hide: function($elem) {

js._customHide($elem, {

'width': 0,

'padding-left': 0,

'padding-right': 0

});

}

},

fadeSlideUpDown: {

init: function($elem) { //初始化时获取元素最初始的宽度

js._customInit($elem, {

'opacity': 0,

'height': 0,

'padding-top': 0,

'padding-down': 0

})

},

show: function($elem) {

js._customShow($elem);


},

hide: function($elem) {

js._customHide($elem, {

'opacity': 0,

'height': 0,

'padding-top': 0,

'padding-bottom': 0

});

}

},

fadeSlideLeftRight: {

init: function($elem) {

js._customInit($elem, {

'opacity': 0,

'width': 0,

'padding-left': 0,

'padding-right': 0

})

},

show: function($elem) {

js._customShow($elem);

},

hide: function($elem) {

js._customHide($elem, {

'opacity': 0,

'width': 0,

'padding-top': 0,

'padding-bottom': 0

});

}

}

};



js._init = function($elem, hiddenCallback) { //hiddenCallback做为参数,需要用时再传入,不需要用就不传入

$elem.removeClass('transition'); //transition的类和js争做动画效果,避免这种情况,需要移除transition的类

init($elem, hiddenCallback); //调用之前定义的init函数

};


js._customInit = function($elem, options) { //用于fadeSlideUpDown的自定义

var styles = {};

for (var p in options) {

styles[p] = $elem.css(p);

}

$elem.data('styles', styles); //设置数据styles的data值为styles,将局部的变量可以在其他函数中使用


js._init($elem, function() {

$elem.css(options);

})

};


js._show = function($elem, mode) { //mode用来接收淡入淡出等的模式  模式是字符串,而且是参数,不能用.要用[]

show($elem, function() {

$elem.stop()[mode](function() { //stop()暂停之前执行的动画,再开始新动画  

$elem.data('status', 'shown').trigger('shown');

})

})

};


js._customShow = function($elem) {

show($elem, function() {

$elem.show(); //执行最上边定义的show函数

$elem.stop().animate($elem.data('styles'), function() { //动画结束后

$elem.data('status', 'shown').trigger('shown');

});

});

};


js._hide = function($elem, mode) {

hide($elem, function() {

$elem.stop()[mode](function() { //如果点击了显示按钮立刻点击隐藏按钮,stop()会立刻停止显示按钮去执行隐藏按钮

$elem.data('status', 'hidden').trigger('hidden');

});

});

};


js._customHide = function($elem, options) {

hide($elem, function() {

$elem.stop().animate(options, function() { //动画结束后

$elem.hide(); //执行最上边定义的hide函数

$elem.data('status', 'hidden').trigger('hidden');

});

});

};


// 设置默认参数,值为true时,执行相应的方法

var defaults = {

css3: false,

js: false,

animation: 'fade'

};


function showHide($elem, options) {

var mode = null;


if (options.css3 && transition.isSupport) { //options.css3为真,而且浏览器支持  css3的transition过渡

mode = css3[options.animation] || css3[defaults.animation]; //如果传错参数,没有覆盖到defaults相同的属性,就用defaults的属性

} else if (options.js) { //js的animation动画

mode = js[options.animation] || js[defaults.animation];

} else { //没有动画 no animation

mode = silent;

}


mode.init($elem);

return { //返回show和hide

// show: mode.show,

show: $.proxy(mode.show, this, $elem), //mode.show是函数 第二个参数是this的指向 this 第三个参数是传入参数$elem

hide: $.proxy(mode.hide, this, $elem)

};

}


//假设外部传了options为 {css3:true} 那么就会覆盖defaults中相同的css3属性的值,将false变为true



// // 局部对象调用到全局,运用之前的全局对象window.muduoduo

// window.muduoduo = window.muduoduo || {};

// window.muduoduo.showHide = showHide;


// 使用插件形式

$.fn.extend({ //在jq原型上扩展一个方法,对外可以直接调用showHide

//方法中有一个参数

showHide: function(option) { //判断option是否是对象

return this.each(function() { //为了能连缀. .,返回this //将调用方法的元素进行循环遍历,又可能是多个元素

// 将循环的每一个元素赋值给$this变量


var $this = $(this),

options = $.extend({}, defaults, typeof option === 'object' && option),

//将外部传入的参数options覆盖defaults相同属性放到空对象中,将赋值后的对象给options


// 检测传入的参数option是否是一个对象,如果是的话,和默认的参数default合并成一个对象,再赋值给options参数,也就是下面的参数:

// $box.showHide({

//     css3: true,

//     js: false,

//     animation: 'slideUpDown'

// });


//&&是短路操作符,如果typeof option==='object'为真,则将option的值传入showHide,如果typeof option==='object'为假,不会执行&&后面的option,将false传入showHide

//如果把false传给showHide, option的值没有覆盖到defaults 就是不使用外部参数,使用defaults的值


mode = $this.data('showHide'); // 通过data方法获取showHide属性值

if (!mode) { //没有mode 是第一次执行 //如果开始mode没有值的话,也就是undefined,说明元素之前没有使用data添加过这个属性

$this.data('showHide', mode = showHide($this, options));

//使用data方法添加上showHide属性,属性值是调用showHide方法返回出来的一个对象,里面有两个方法

// return {

//     show: $.proxy(mode.show, this, $elem),

//     hide: $.proxy(mode.hide, this, $elem)

// };

}


//如果mode有值,说明已经添加过这个属性了,通过传入的参数也就是mode对象的属性名检测是否是一个函数,如果是的话调用这个方法即可,也就是下面的传参:

//  $('#btn-hide').on('click', function() {


//     $box.showHide('hide');


// });


if (typeof mode[option] === 'function') {

mode[option]();

}


})

}

})

})(jQuery);


为什么this会出现错误?this什么时候会改变而不是指dropdown呢?

写回答

1回答

好帮手慕糖

2019-04-04

同学你好,1、因为这里的this是undefined哦,而我们要访问的是Dropdown中定义的activeClass

http://img.mukewang.com/climg/5ca5b06a0001dda508320352.jpg

2、直接在Dropdown的这个原型链上的都可以说过this,因为这里this都是指向实例化对象的,所以可以通过this来访问属性,而这里是在_show这个方法中,所以不能直接使用this了哦。

http://img.mukewang.com/climg/5ca5b0390001234d09210574.jpg

希望能帮助到你,祝学习愉快!

0

0 学习 · 14456 问题

查看课程