'transform3d'无法与位置为fixed的子元素一起使用

155
我有一个情况,在正常的CSS情况下,固定的div将被定位到指定的位置(top:0px, left:0px)。但是,如果父元素有一个translate3d变换,这似乎不被尊重。我是否忽略了什么?我已经尝试过其他webkit-transform样式和transform origin选项,但没有成功。 我附上了一个带有示例的JSFiddle,在这个示例中,我希望黄色框框位于页面的顶部角而不是容器元素内部。 以下是一个简化版本的fiddle:

#outer {
    position:relative; 
    -webkit-transform:translate3d(0px, 20px , 0px); 
    height: 300px; 
    border: 1px solid #5511FF; 
    padding: 10px;
    background: rgba(100,180,250, .8); 
    width: 80%;
}
#middle{
    position:relative; 
    border: 1px dotted #445511; 
    height: 300px; 
    padding: 5px;
    background: rgba(250,10,255, .6);
}
#inner {
    position: fixed; 
    top: 0px;
    box-shadow: 3px 3px 3px #333; 
    height: 20px; 
    left: 0px;
    background: rgba(200,180,80, .8); 
    margin: 5px; 
    padding: 5px;
}
<div id="container">
    Blue: Outer, <br>
    Purple: Middle<br>
    Yellow: Inner<br>
    <div id="outer"> 
        <div id="middle">
            <div id="inner">
                Inner block
            </div>
        </div>
    </div>
</div>

我该如何让translate3d在固定位置的子元素中起作用?

同样的问题也发生在过滤器上:https://dev59.com/71QJ5IYBdhLWcg3w3pzz - Temani Afif
10个回答

228
这是因为transform会根据W3C规范创建一个新的本地坐标系:

在HTML命名空间中,除了none之外的任何值都会创建一个堆叠上下文和一个包含块。该对象作为固定定位后代的包含块。

这意味着固定定位将固定到转换元素而不是视口。目前我不知道有任何解决方法。
Eric Meyer的文章Un-fixing Fixed Elements with CSS Transforms也有相关记录。

3
@INT,我认为这个问题不太可能有解决办法。因为不允许绕过的主要原因是:用户提供的输入可能会覆盖其指定区域之外的控件(比如恶意电子邮件添加选项到Gmail工具栏)。如果你打算在"fixed"内使用转换,那么最好的解决方法就是暂时避免使用绕过方法。 - saml
1
将CSS的top属性设置为window.scrollHeight是否可行?您可能还需要绝对定位,但是这样做应该是可行的,不是吗?(现在太懒了,实际测试一下) - Brad Orego
@bradorego,你是对的,我刚刚添加了我使用的代码。 - UzumakiDev
1
据我所知,这在规范中仍然不稳定。请参见Bug 16328 - Use of "containing block" does not match CSS2.1 definition - thirdender
最简单的方法是使用一个包装 div,该 div 具有固定定位,并包含一个内部的转换 div。 - Primus202
显示剩余4条评论

14
如Bradoergo所建议,只需获取窗口的scrollTop并将其加到绝对位置的顶部,如下所示:
function fix_scroll() {
  var s = $(window).scrollTop();
  var fixedTitle = $('#fixedContainer');
  fixedTitle.css('position','absolute');
  fixedTitle.css('top',s + 'px');
}fix_scroll();

$(window).on('scroll',fix_scroll);

这对我很有效。

3
可以!但需要您提供原文所在的语言。如果原文是英语的话,翻译如下:它有效了!但是,绑定事件监听器时,我不是将其绑定到“window”,而是绑定到正在滚动的 div 上。另外,固定定位的元素闪烁。 - train
这里与jQuery有什么关系? - FlorianB
这样做是有可能的,但像@train提到的那样,它会闪烁。 - Etienne Dupuis
是的,这个更新速度不够快,无法满足我的使用需求 :-/ 不过是个好主意。 - reid
这是非常糟糕的做法,请勿使用JS进行样式更改。 - Wannes

13

当页面中的元素使用了 transform 时,我固定的顶部导航栏出现了闪烁。下面这些操作解决了跳动/闪烁的问题:

#fixedTopNav {
    position: fixed;
    top: 0;
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
}

感谢SO上的这个答案。


6

在Firefox和Safari中,您可以使用position: sticky;代替position: fixed;,但在其他浏览器中无法使用。为此,您需要使用JavaScript。


2
粘性定位是相对定位和固定定位的混合体,它是非常实验性的。我强烈建议避免使用它,因为它还没有成为标准。 - Farside
1
据我所知,在Firefox和Safari上,您可以简单地使用position:fixed,它会按预期工作。 - oriadam
@oriadam 不,当父元素使用translate3d时,有时子元素的固定位置会飞来飞去。我将尝试使用sticky,因为它已经被主流浏览器支持:https://caniuse.com/#feat=css-sticky - Lukas Liesis
“sticky”可能是一个解决方案,它适用于现代浏览器。 - aboutqx

3

在我看来,处理这个问题的最佳方法是应用相同的翻译,但将需要修复的子元素从它们的父(已翻译)元素中分离出来;然后将翻译应用于 position: fixed 包装器内的一个 div。

结果看起来像这样(在您的情况下):

<div style='position:relative; border: 1px solid #5511FF; 
            -webkit-transform:translate3d(0px, 20px , 0px); 
            height: 100px; width: 200px;'> 

</div>
<div style='position: fixed; top: 0px; 
            box-shadow: 3px 3px 3px #333; 
            height: 20px; left: 0px;'>
    <div style='-webkit-transform:translate3d(0px, 20px, 0px);'>
        Inner block
    </div>
</div>

JSFiddle: https://jsfiddle.net/hju4nws1/

虽然这种方法可能不适用于某些情况,但通常情况下,如果你要修复一个

元素,那么你可能并不关心它的父元素是什么/在DOM中的继承层次结构中处于什么位置,而这种方法似乎可以解决大部分问题,同时仍允许translateposition: fixed相对地和谐共存。


0
尝试对子元素应用相反的变换:
<div style='position:relative; border: 1px solid #5511FF; 
            -webkit-transform:translate3d(0px, 20px , 0px); 
            height: 100px; width: 200px;'> 
    <div style='position: fixed; top: 0px; 
                -webkit-transform:translate3d(-100%, 0px , 0px); 
                box-shadow: 3px 3px 3px #333; 
                height: 20px; left: 0px;'>
        Inner block
    </div>
</div>

0
在元素变换时添加动态类。$('#elementId').addClass('transformed')。 然后继续在CSS中声明,
.translat3d(@x, @y, @z) { 
     -webkit-transform: translate3d(@X, @y, @z); 
             transform: translate3d(@x, @y, @z);
      //All other subsidaries as -moz-transform, -o-transform and -ms-transform 
}

然后

#elementId { 
      -webkit-transform: none; 
              transform: none;
}

那么

.transformed {
    #elementId { 
        .translate3d(0px, 20px, 0px);
    }
}

现在,当为子元素提供topz-index属性值时,position: fixed可以正常工作并保持固定状态,直到父元素转换。当转换被还原时,子元素会再次弹出为固定状态。如果您实际上正在使用一个在单击时切换打开和关闭的导航侧栏,并且您有一个标签集,在向下滚动页面时应该保持粘性的情况下,这应该会缓解局面。

0

我遇到了同样的问题。唯一的区别是我的带有“position: fixed”的元素是通过JS设置其“top”和“left”样式属性的。因此,我能够应用修复:

var oRect = oElement.getBoundingClientRect();

oRect 对象将包含 实际(相对于视口)的顶部和左侧坐标。因此,您可以调整实际的 oElement.style.top 和 oElement.style.left 属性。


这在IE和Chrome上可以工作,但在Android标准浏览器上无法工作。左边是一个数字,但它总是绘制在位置0上。 - DATEx2

0
我有一个使用-webkit-transform: translate3d的离屏边栏。 这导致我无法在页面上放置固定页脚。 我解决了这个问题,通过针对html页面上添加到侧边栏初始化标记的类,并编写css:not限定符来声明“-webkit-transform:none;”,当该类未存在于html标记中时。 希望这能帮助那些遇到同样问题的人!

-1
一种处理这个问题的方法是对固定元素应用相同的变换。
<br>
<div style='position:relative; border: 1px solid #5511FF; 
            -webkit-transform:translate3d(0px, 20px , 0px); 
            height: 100px; width: 200px;'> 
    <div style='position: fixed; top: 0px; 
                -webkit-transform:translate3d(0px, 20px , 0px); 
                box-shadow: 3px 3px 3px #333; 
                height: 20px; left: 0px;'>
        Inner block
    </div>
</div>

1
在IE中,首先不存在bug。 - oriadam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接