实现下拉列表,点击其他位置自动隐藏效果的三种方式比较

目录

  • 实现效果:
  • 实现环境
  • 实现
    • 方法一:
      • 基本思路
      • 主要代码
        • JavaScript代码
      • 总结
    • 方法二
      • 主要代码
      • 总结
    • 方法三
      • 基本思路
      • 主要代码
        • JavaScript
        • HTML
      • 总结

实现效果:

1.点击按钮展开下拉列表
2.点击下拉列表中的选项进行选择,随后收起下拉列表
3.点击除下拉列表外的位置(包括按钮),收起下拉列表

效果图如下所示:
实现下拉列表,点击其他位置自动隐藏效果的三种方式比较_第1张图片

完整代码请查看:
https://github.com/wolf-wolf/pullDownList.git

实现环境

1.angular
2.sublime

#基本结构:

组件的基本HTML结构如下所示:


选择列表3

实现

方法一:

基本思路

在document上监听click事件,进而关闭打开的下拉列表

主要代码

JavaScript代码

//列表是否打开的判断标志
$scope.flag = {
	showList: false
};
//在document上监听click函数
$document.bind('click', function(event) {
	_closeList();//关闭下拉列表
    $scope.$apply();//为了保证angular的数据同步
});
/**
* 关闭列表函数
*/
function _closeList() {
	$scope.flag.showList = false;
}

总结

上述方法实现简单,理解容易,但存在问题,如果在同一页面中存在多个组件可供点击,并且存在部分组件针对click事件使用event.stopPropagation()event.stopImmediatePropagation()方法阻止事件冒泡,则点击事件无法到达document,进而无法达到自动关闭下拉列表的效果。

方法二

###基本思路
在列表打开时,在body上添加遮罩层,并将其z-index属性设置为最高,进而遮挡所有该页面的其他组件,同时监听发生在遮罩层上的点击事件,来达到自动关闭下拉列表的效果。

主要代码

####JavaScript

/**
* 切换列表关闭和打开状态
* @param  {Object} event 事件参数,用于阻止事件冒泡
*/
$scope.toggleList = function(event) {
	if ($scope.flag.showList) { //如果列表打开则关闭
		_closeList();
	} else { //如果列表是关闭状态则,创建遮罩层,并打开列表
		var _mask = document.createElement('div');
		_mask.className = 'drop-down-mask-' + _uniquePrefix;
		_mask.style.position = 'absolute';//使用绝对性为
		_mask.style.top = '0';
		_mask.style.left = '0';
		_mask.style.width = '100%';
		_mask.style.height = '100%';
		_mask.style.zIndex = '99998';
		_mask.style.background = 'rgba(255, 255, 255, 0)';//设置遮罩层背景颜色为透明
		
		//为遮罩层添加click方法
		_mask.addEventListener('click', function(event) {
			_closeList();
			$scope.$apply();
		 });
	
		$document[0].body.append(_mask);
			$scope.flag.showList = true;
		}
	};
/**
* 关闭列表函数,将列表是否打开的判断标志设置为false,并且清除遮罩层
*/
function _closeList() {
	$scope.flag.showList = false;
	//获取遮罩层元素
	var _mask = angular.element(document.getElementsByClassName('drop-down-mask-' + _uniquePrefix));
	
	if (_mask) {
		_mask.remove();//移除遮罩层
	}
}

HTML

<div class="main">
    <button class="open-btn" ng-click="toggleList($event)">选择列表1button>
    <div class="list-wrapper" ng-show="flag.showList">
        <ul class="list">
            <li class="item" ng-repeat="item in list" ng-click="selectItem($event,item)">
                <span class="item-name" ng-bind="item.name">span>
            li>
        ul>
    div>
div>

总结

  1. 下拉列表打开后添加遮罩层
  2. 遮罩层插入到body下,bodyposition属性为relative,遮罩层的position属性为absolute,以确保遮盖整个页面
  3. 在创建遮罩层时,需要判断是否已经存在遮罩,在这里,因为打开下拉列表表示遮罩层已经创建,所以判断下拉列表是否打开和判断遮罩层是否已经存在具有等同效果。如果未做判断,则会出现多次点击生成多个遮罩层的效果,从而在点击遮罩层时,需要多次点击才能保证其他组件的可用性
  4. 在关闭下拉列表的同时,一定要移除遮罩层的存在

缺陷:因为有遮罩层的存在所以在点击非下拉列表的位置时,点击事件发生在遮罩层上,如果想要在下拉列表打开的情况下,将焦点定位到其他组件,则需要二次点击。

TIP:
此方法是select2组件所采用的方式,可通过链接查看官网。

方法三

基本思路

div添加tabindex属性,使其可以获得和失去焦点。监听blur事件做到自动关闭下拉列表的效果

主要代码

JavaScript

/**
* 关闭列表
* @param  {Object} event 事件参数,用于阻止事件冒泡
*/
$scope.closeList = function(event) {
	event.stopPropagation();
	_closeList();
}

/**
* 切换列表关闭和打开状态
* @param  {Object} event 事件参数,用于阻止事件冒泡
*/
$scope.toggleList = function(event) {
	event.stopImmediatePropagation();
	if ($scope.flag.showList) {
		_closeList();
	} else {
		$scope.flag.showList = true;
	}
};

/**
* 关闭列表函数
*/
function _closeList() {
	$scope.flag.showList = false;
}

HTML

<div class="main" tabindex="-1" ng-blur="closeList($event)" ng-click="toggleList($event)">
    <h4 class="open-btn">选择列表2h4>
    <div class="list-wrapper" ng-show="flag.showList">
        <ul class="list">
            <li class="item" ng-repeat="item in list" ng-click="selectItem($event,item)">
                <span class="item-name" ng-bind="item.name">span>
            li>
        ul>
    div>
div>

总结

  1. 在最外层包裹层,添加tabindex属性,这里将其值置为-1,目的是保证在使用tab键进行组件焦点切换时不会切换到本组件
  2. 监听blur事件而非click事件
  3. 对按钮部分监听click事件,以实现点击按钮切换下拉列表打开状态的效果

TIP:
为什么不监听focus事件:因为在获取焦点后,focus事件步不再会被触发,则无法通过点击按钮来关闭下拉列表。同时focus事件发生在click事件之前,如果两个事件同时绑定打开下拉列表的处理函数,则会产生闪烁效果,具体情况,看官可自行尝试~~


tabindex属性:请查看W3CSchool

如有错误请多多包涵,敬请反馈,期待一起进步~~~

再见,祝好~~!

你可能感兴趣的:(前端)