违反 z-index,并强制元素在前面(或假装它)

Violate z-index, and force element to front anyway (or fake it)

在我的问题开头,我会说我并没有特别选择这里的 "z" 解决方案,但我不确定还有什么可行的。

任务: 在页面顶部附加一个遮罩元素(固定、不透明度 .5 等...),然后定位页面中的单个元素并强制它显示在该面具的顶部。这个想法是突出显示页面中集成的特定元素以供展示。

限制:没有提前定义强制置顶的元素,页面上的任何元素都应该是一个可行的目标。

目标元素的父元素将有几个可能的 z-index 值,所有这些值都将低于掩码的 z,并且可以更改 none。递归遍历所有父项并设置临时 css 样式是行不通的,因为它会在此过程中破坏很多其他内容。

Css-only 解决方案是可取的,尽管需要一点 js 的东西也可以。

想法:我最有希望的想法是使用 translateZ(或其他 3dTransform 类型样式)强制单个元素覆盖所有未转换内容的顶部(包括面具),但到目前为止还没有让它发挥作用。

我还考虑过克隆目标元素、从目标收集位置和样式值并将克隆渲染到遮罩顶部。这看起来有点笨拙(而且有点恶心)而且我不确定它的防弹程度。


到目前为止我读过的所有其他解决方案都涉及重新排序 dom、更正父级的位置或 z-index 样式,或者只是向目标添加缺失的位置样式。 .. none 其中适用于此处的解决方案。

我选择了克隆选项,并找到了一个我不太喜欢但似乎很实用的解决方案。这是我用来创建和定位克隆的方法:

var renderClonedEl = function($el, childrenToCopy) {
    var $clone = $($el[0].cloneNode(true)),
        offset = $el.offset(),
        styles = window.getComputedStyle($el[0], null).cssText,
        correction = {
            left: parseInt($el.css('margin-left')) + parseInt($el.css('border-left-width')),
            top: parseInt($el.css('margin-top')) + parseInt($el.css('border-top-width'))
        },
        $child, $cloneChild;

    childrenToCopy = childrenToCopy || []; // Use array of selector strings to copy addional styles between children elements
    $clone[0].style.cssText = styles;

    for (var i = 0; i < childrenToCopy.length; i++) {
        $child = $el.find(childrenToCopy[i]);
        $cloneChild = $clone.find(childrenToCopy[i]);

        _.each($cloneChild, function(item) {
            item.style.cssText = window.getComputedStyle($child[0], null).cssText;
        });
    };

    $clone.addClass('el-clone').appendTo('body').css({
        top: offset.top - correction.top,
        left: offset.left - correction.left
    });

    return $clone;
}

...克隆的其他辅助样式:

.el-clone {
    position: absolute;
    z-index: 9999;
    pointer-events: none;
}

像这样调用方法...

var $newClone = renderClonedEl($('.el-to-clone'), ['.header > .title', '.header .btn']);

我将原来的 el 作为 jQuery 选择传递(因为我有 jQuery 可供我使用)。 .offset() 方法不考虑 margin/padding/border-width,所以我正在对此进行调整。

childrenToCopy 是一个选择字符串数组,我可以传递它以将计算样式从子元素额外复制到克隆(当克隆具有很多子元素的复杂 el 时,其中一些会丢失样式)。

我还将返回克隆,以便启动代码可以在我们完成后正确销毁它。

我有点 jQuery 并在香草的东西中加了下划线……我只是碰巧为这个项目提供了两个库。

我非常害怕克隆必须公开的节点,因为 CSS 选择器或事件处理程序可能损坏。自然地,如果您了解您的环境并且确定没有任何东西可以制动,那么这可能是个好方法。

但如果您感兴趣的元素恰好是普通矩形,您可以使用由四个较小的矩形组成的叠加层来非常安全地模糊其周围环境。我做了一个快速的 VanillaJS 概念验证(点击周围):

function Obscurer() {
  this.cont = document.createElement('div')
  this.e1 = this.cont.appendChild(this.cont.cloneNode())
  this.e1.className = 'cover'
  this.e2 = this.cont.appendChild(this.e1.cloneNode())
  this.e3 = this.cont.appendChild(this.e1.cloneNode())
  this.e4 = this.cont.appendChild(this.e1.cloneNode())
}
Obscurer.prototype.obscureSurroundingsOf = function(el) {
  if (el.parentNode === this.cont) {
    document.body.removeChild(this.cont)
    return
  }
  var b = el.getBoundingClientRect()
  this.e1.style.width = b.left + b.width + 'px'
  this.e1.style.height = b.top + 'px'
  this.e2.style.left = this.e1.style.width
  this.e2.style.height = b.top + b.height + 'px'
  this.e3.style.top = this.e2.style.height
  this.e3.style.left = b.left + 'px'
  this.e4.style.top = b.top + 'px'
  this.e4.style.width = b.left + 'px'
  document.body.appendChild(this.cont)
}

var o = new Obscurer()

document.addEventListener('click', function(ev) {
  o.obscureSurroundingsOf(ev.target)
})
.cover {
  position: absolute;
  background-color: #000;
  opacity: 0.5;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}
<p>foo baz</p>
<p>baz <b>gazonk</b> <i>something</i></p>
<p>anything</p>