Bootstrap col-*类致AngularJS日历弹窗被遮挡的解决问询
我来帮你搞定这个头疼的问题!你遇到的其实是Bootstrap布局里的**堆叠上下文(stacking context)**问题,不是z-index没用,而是col-*类的默认样式在搞鬼。
问题根源
Bootstrap的col-*类默认设置了position: relative,而z-index的生效规则是:只有当元素是非static定位(比如relative/absolute/fixed)时,z-index才会起作用,而且它的层级是相对于父级的堆叠上下文的。如果你的日历弹窗的父容器是static定位,或者父容器的z-index比下方col-*元素低,哪怕你给日历设了很高的z-index,也会被下方的col-*元素盖住。
具体解决方案
这里给你几个可行的办法,按推荐程度排序:
1. 把日历弹窗挂载到body下(最稳妥的方案)
把日历从当前的容器里移出来,直接挂载到<body>标签下,这样它的堆叠上下文是整个页面,不会被其他col-*元素的上下文影响。在你的AngularJS指令里可以这么写:
angular.module('yourModule').directive('datePicker', function($compile) { return { link: function(scope, element, attrs) { // 动态创建日历元素 var calendar = angular.element('<div class="calendar-popup" ng-show="isOpen"></div>'); // 编译绑定scope $compile(calendar)(scope); // 添加到body angular.element(document.body).append(calendar); // 点击输入框显示日历并定位 element.on('click', function() { scope.isOpen = true; scope.$apply(); // 获取输入框的位置,定位日历 var rect = element[0].getBoundingClientRect(); calendar.css({ position: 'fixed', top: rect.bottom + 'px', left: rect.left + 'px', z-index: 9999 }); }); // 点击页面其他地方关闭日历 angular.element(document).on('click', function(e) { if (!element[0].contains(e.target) && !calendar[0].contains(e.target)) { scope.isOpen = false; scope.$apply(); } }); } }; });
这种方式完全避开了父容器和其他col-*元素的堆叠上下文冲突,是最不容易出问题的方案。
2. 确保日历弹窗的父容器有足够高的z-index
如果日历是放在输入框的父容器里,那先给父容器加上position: relative和足够高的z-index,再给日历弹窗设置绝对定位和更高的z-index:
/* 输入框的父容器 */ .date-input-wrapper { position: relative; z-index: 100; /* 这个值要比下方col-*元素的z-index高 */ } /* 日历弹窗 */ .calendar-popup { position: absolute; top: 100%; left: 0; z-index: 9999; /* 比父容器更高,确保在父上下文里最上层 */ /* 其他样式 */ }
同时要注意:父容器不能有overflow: hidden,不然日历会被裁剪掉!
3. 强制修改col-*元素的z-index(不推荐,万不得已用)
如果上面两种方法都没法用,可以强制把下方col-*元素的z-index改成auto或者更低的值,但要注意这可能影响页面其他布局:
.col-* { z-index: auto !important; }
这个方法尽量少用,因为Bootstrap的col-*设置relative是有布局意义的,乱改可能引发其他问题。
总结
优先尝试第二种方案(挂载到body),其次是调整父容器的z-index,最后才考虑修改col-*的样式。这样就能让你的日历弹窗始终显示在最上层啦!
内容的提问来源于stack exchange,提问作者user441521

