jQuery UI的ui.helper对象被挡住的处理办法和zIndex介绍

标签:CSS, jQuery

做游戏王卡片拖拽效果时,已经2次遇到ui.helper对象被挡住的情况了,所以不得不想办法处理了。

第一次的问题如下:
所有的droppable对象都是position为absolute的div,而draggable对象隶属于其中一个div。
由于在IE中,后创建的div比先创建的同级div的堆叠级别(stack level)更高,而子元素的堆叠顺序(stack order)会根据父元素的堆叠级别来排列,于是无论怎么设置子元素的zIndex,都比后创建的父元素的兄弟结点的堆叠顺序低。
简单来说,只要从先创建的div拖动到后创建的div,拖动对象就会被后创建的div挡住。
这个现象只在IE中存在,Chrome和Firefox中,zIndex相同的兄弟结点是同级别的,不存在这种问题。
当时的解决办法就是不设置该元素的背景属性,内容也为空,这样即使挡住,也是完全透明的,这样就看不出来了。

第二次的问题如下:
需要一个scrollable对象,里面放置draggable对象。由于卷动时只想显示部分droppable对象,所以设置了overflow: hidden这个属性。
于是问题来了,当拖动draggable对象的ui.helper对象时,由于draggable对象隶属于scrollable对象,因此ui.helper对象也隶属于它(我设置的是helper: "clone")。这样一来,当ui.helper对象离开scrollable对象的区域时,就会因为overflow: hidden这个属性而被hidden了。
于是想到了改为overflow-x: hidden,可y轴就出现滚动条了,这是完全无法接受的。即使让滚动条宽度为0,其内容仍会改变,所以放弃。
冥思苦想了一晚(实际上是想了半分钟就睡着了),终于想到了解决办法:
在body元素上创建一个position为absolute的div,并定位到draggable对象上,再把ui.helper对象append到这个div,就不会被hidden了。
这个方法同样适用于第一种情况,不过需要让这个div在其他元素之后创建(最简单的办法就是每次都新创建一个)。
但如果要让draggable对象能通过设置revert: "invalid"回到原位置,且不设置helper: "clone"的话,就必须找到原位置来插入了(貌似非常麻烦,droppable对象和ui.helper对象都要处理),好在我不存在这种需求。

放上解决代码,很奇怪为什么ui.position和ui.offset都拿不到top和left值,只好用draggable对象的position和offset方法了:
$(".card").draggable({ //这个选择器就按照自己的需求写吧,我只是给个例子
	zIndex: 2000, //随便设置一个比较大的数就行了,主要是不让其他元素挡住ui.helper拖动
	opacity: .5, //透明度也是随便设置
	revert: "invalid",
	containment: "document",
	helper: "clone", //如果不是clone的话,就要在stop事件中处理回来的代码了
	cursor: "move",
	start: function(e, ui) {var self = $(this); tempArea.show().css({left: self.offset().left - self.position().left, top: self.offset().top - self.position().top}).append(ui.helper);} // 注意位置的设置,我是测试出来的,不清楚原因。tempArea则是用于临时存放ui.helper的那个div对象。
	stop: function(e, ui) {tempArea.hide();} //如果不hide的话,这个位置就会被挡住了,无法再次拖动。
});

最后说下zIndex这个属性。
如果没设置position属性(即position为static),则文档里的所有DOM元素都按照创建顺序依次向后排列,子元素则排在父元素的上面。
当position属性设置为absolute、relative或fixed时,这些定位元素就不符合依次向后排列这个规定了。如果位置(偏移量)相同或有重叠,则堆叠顺序更高的元素将挡住堆叠顺序较低的元素。
而堆叠顺序的高低并不是很简单就能决定的,总结下来有这3点:
  1. 每个定位元素都有个stacking context(堆叠上下文),所有定位元素的stacking context都在父元素的stacking context上产生,而根元素则形成root stacking context。
    简单来说,子元素比父元素的stack level更高,兄弟元素的stack level相同。
  2. 比较堆叠顺序时,先比较stacking context的stack level。如果一方的stack level更高,则它的堆叠顺序也更高。
    因此,如果A的stack level比B的stack level更高,则A的所有子元素都比B和B的子元素的堆叠顺序高。反过来,如果A的父元素比B的父元素的stack level更高,则A的堆叠顺序比B高。
  3. 当2个元素的stack level相同时(兄弟结点),则比较其zIndex值,较高一方的堆叠顺序更高。如果没设zIndex值,或zIndex相同,则后创建的更高。
  4. 当2个元素的父元素的stack level相同时,则比较其zIndex值,较高一方的子元素堆叠顺序更高。
    如果没设zIndex值,或zIndex相同,在Chrome和Firefox等浏览器中中会比较子元素的zIndex值,较高一方的堆叠顺序更高,若相同,则后创建的更高。而在IE中,父元素后创建的一方堆叠顺序更高,而跳过了子元素的zIndex比较(这就是我遇到的第一个问题)。
    也就是说,IE中2个分别隶属于2个兄弟结点的子结点是不可能通过甚至自身的zIndex值来覆盖另一方的,它们的堆叠顺序完全取决于父结点。
可以看出,zIndex确实是个很尴尬的东西,完全受制于stack level了。

最后说下怎么设置它。
在CSS里直接写z-index: 数值(可以为负数)即可。也可以设为auto,这样就自动取父元素的zIndex。
JavaScript里则是设置style.zIndex这个属性。
此外重申一下,为position为static的元素设置zIndex是无效的,原因是不会用它来做比较:static的同级元素都是依次向后排列的,不存在堆叠现象。

4条评论 你不来一发么↓ 顺序排列 倒序排列

    向下滚动可载入更多评论,或者点这里禁止自动加载

    想说点什么呢?