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
2、直接在Dropdown的这个原型链上的都可以说过this,因为这里this都是指向实例化对象的,所以可以通过this来访问属性,而这里是在_show这个方法中,所以不能直接使用this了哦。
希望能帮助到你,祝学习愉快!
相似问题
回答 2
回答 2