不受控制的 position:fixed

探讨了position:fixed在某些情况下的失效原因,特别是在存在transform、perspective或will-change属性时。文章通过实验验证了不同内核浏览器下的表现,并总结了可能导致fixed定位基准变化的因素。

大家都知道,position:fixed 在日常的页面布局中非常常用,在许多布局中起到了关键的作用。它的作用是:

position:fixed 的元素将相对于屏幕视口(viewport)的位置来指定其位置。并且元素的位置在屏幕滚动时不会改变。

但是,在许多特定的场合,指定了 position:fixed 的元素却无法相对于屏幕视口进行定位。这是为何呢?

xx

失效的 position:fixed

在许多情况下,position:fixed 将会失效。MDN 用一句话概括了这种情况:

当元素祖先的 transform 属性非 none 时,定位容器由视口改为该祖先。

What!还有这种操作?可能有部分同学还没 get 到上面这句话的意思,通俗的讲就是指定了 position:fixed 的元素,如果其祖先元素存在非 none 的 transform 值 ,那么该元素将相对于设定了 transform 的祖先元素进行定位。

那么,为什么会发生这种情况呢?说好的相对视口(Viewport)定位呢?

这个问题,就牵涉到了 Stacking Context ,也就是堆叠上下文的概念了。解释上面的问题分为两步:

  1. 任何非 none 的 transform 值都会导致一个堆叠上下文(Stacking Context)和包含块(Containing Block)的创建。
  2. 由于堆叠上下文的创建,该元素会影响其子元素的固定定位。设置了 position:fixed 的子元素将不会基于 viewport 定位,而是基于这个父元素。

Stacking Context — 堆叠上下文

好的嘛,好的嘛,又冒出新的名词了,堆叠上下文(又译作层叠上下文),又是什么?

堆叠上下文(Stacking Context):堆叠上下文是 HTML 元素的三维概念,这些 HTML 元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的 z 轴上延伸,HTML 元素依据其自身属性按照优先级顺序占用层叠上下文的空间。

概念比较抽象,简单理解,记住 生成了 Stacking Context 的元素会影响该元素的层叠关系与定位关系

关于 生成了 Stacking Context 的元素会影响该元素的层叠关系 这一点,具体可以看看这篇文章 层叠顺序(stacking level)与堆栈上下文(stacking context)知多少?

而本文提到了生成了 Stacking Context 的元素会影响该元素定位关系 。按照上面的说法,堆叠上下文的创建,该元素会影响其子元素的固定定位。设置了 position:fixed 的子元素将不会基于 viewport 定位,而是基于这个父元素。

那么问题来了,是否所有能够生成堆叠上下文的元素,都会使得其子元素的 position:fixed 相对它,而不是相对视口(Viewport)进行定位呢?

创建堆叠上下文的方式

为此,首先要找到所有能够使元素生成堆叠上下文的方法。

So,如何触发一个元素形成 堆叠上下文 ?方法如下(参考自 MDN):

  1. 根元素 (HTML),
  2. z-index 值不为 “auto”的 绝对/相对定位,
  3. 一个 z-index 值不为 “auto”的 flex 项目 (flex item),即:父元素 display: flex|inline-flex,
  4. opacity 属性值小于 1 的元素(参考 the specification for opacity),
  5. transform 属性值不为 “none”的元素,
  6. mix-blend-mode 属性值不为 “normal”的元素,
  7. filter值不为“none”的元素,
  8. perspective值不为“none”的元素,
  9. isolation 属性被设置为 “isolate”的元素,
  10. position: fixed
  11. 在 will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值
  12. -webkit-overflow-scrolling 属性被设置 “touch”的元素

接下来,我们要验证,是否所有设置了上面属性样式之一的元素,都有使其子元素的 position: fixed 失效的能力?

为此我做了下面一个小实验,基于最新的 Blink 内核。可戳:

层叠上下文对 fixed 定位的影响(不同内核下表现可能不一致)

image

我们设置两个父子 div,子元素 fixed 定位,通过修改父元素生成层叠上下文,观察子元素的 fixed 定位是否不再相对视口。

XHTML

 

1

2

3

<div class="container">

  <div class="fixed"> </div>

</div>

最初的 CSS :

CSS

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

.container {

    width:10vw;

    height: 10vw;

    background: rgba(255, 100, 100, .8);

}

 

.fixed {

    position: fixed;

    top: 1vw;

    left: 1vw;

    right: 1vw;

    bottom: 1vw;

    background: rgba(100, 100, 255, .8);

}

 

一探 position:fixed 失效的最终原因

通过上面的试验,在最新的 Blink 内核下,发现并不是所有能够生成层叠上下文的元素都会使得 position:fixed 失效,但也不止 transform 会使 position:fixed 失效。

所以,MDN 关于 position:fixed 的补充描述不够完善。下述 3 种方式目前都会使得 position:fixed 定位的基准元素改变(本文重点):

  1. transform 属性值不为 none 的元素
  2. perspective 值不为 none 的元素
  3. 在 will-change 中指定了任意 CSS 属性

不同内核的不同表现

完了吗?没有!我们再看看其他内核下的表现。
image

上面也谈到了,上述结论是在最新的 Chrome 浏览器下(Blink内核),经过测试发现,在 MAC 下的 Safari 浏览器(WebKit内核,Version 9.1.2 (11601.7.7))和 IE Trident/ 内核及 Edge 浏览器下,上述三种方式都不会改变 position: fixed 的表现!

所以,当遇到 position: fixed 定位基准元素改变的时候,需要具体问题具体分析,多尝试一下,根据需要兼容适配的浏览器作出调整,不能一概而论。

position:fixed 的其他问题

当然,position: fixed 在移动端实现头部、底部模块定位。或者是在 position: fixed 中使用了 input 也会存在一些问题,这个有很多文章都描述过并且存在很多解决方案,本文不讨论这块问题。

这方面的问题,可以看看这篇文章:移动端web页面使用position:fixed问题总结

转自:http://web.jobbole.com/92121/

<think> 首先,用户的问题是关于在 UniApp 中,当组件 `mytoast` 和其父容器都设置了 `position: fixed` 时,如何让 `mytoast` 左右居中显示,并且不受父容器 `position: fixed` 的影响。 用户提供了一些引用内容,但这些引用似乎来自不同的上下文,如 Vue.js、CSS、Ionic 等。我需要聚焦在用户的具体问题上:UniApp 中的居中布局问题。 关键点: - 组件:`mytoast` - 父容器:也设置为 `position: fixed` - 需求:让 `mytoast` 左右居中,不受父容器影响。 在 CSS 中,当父元素是 `position: fixed` 时,子元素 `mytoast` 也是 `position: fixed`,子元素的定位是相对于视口(viewport)的,除非父元素设置了定位上下文。但由于父元素也是 `fixed`,它本身相对于视口固定。 如果想让子元素居中而不受父容器影响,我们应该确保子元素直接相对于视口定位,而不是相对于父容器。 可能的解决方案: 1. **使用绝对定位或固定定位相对于视口**:既然 `mytoast` 已经是 `position: fixed`,它默认相对于视口定位。要让它左右居中,可以使用 CSS 属性: - `left: 50%` - `transform: translateX(-50%)` 这样,它会水平居中于视口。 2. **避免嵌套定位上下文**:如果父容器是 `position: fixed`,并且设置了位置(如 `top`, `left`),那么子元素 `fixed` 可能仍相对于视口定位。但在某些情况下,如果父容器创建了新的包含块,可能会影响子元素。为了确保不受影响,最好让 `mytoast` 直接作为 body 或其他根元素的子元素。 在 UniApp 中,`mytoast` 组件可能被嵌套在某个父组件中。用户提到父容器也是 `position: fixed`,所以我们需要确保 `mytoast` 的定位独立。 通用方法居中 `fixed` 元素: ```css .mytoast { position: fixed; left: 50%; /* 移动到屏幕中间 */ transform: translateX(-50%); /* 向左移动自身宽度的一半 */ top: 50%; /* 如果需要垂直居中,类似处理 */ transform: translateY(-50%); /* 但注意 transform 只能有一个,所以用 translate(-50%, -50%) */ } ``` 对于一个 toast 组件,通常只需要水平居中,垂直位置可能固定在顶部或底部。用户指定了左右居中,所以我们关注水平居中。 在用户提供的引用中: - 引用[1] 提到了位置控制类,如 `.position-fixed + .top-0 + .end-0` 等,这表明了使用 CSS 类来控制位置。但这主要是 Bootstrap 的思路。 - 其他引用是关于 Vue 缓存、Ionic 模态框等,不直接相关。 UniApp 是基于 Vue.js 的框架,所以 CSS 和 Vue 组件结构是核心。 可能的实现: - 在 `mytoast` 的样式中,直接设置: ```css position: fixed; left: 50%; transform: translateX(-50%); /* 注意兼容性,可能需要自动前缀 */ /* 例如,设置一个固定宽度或使用文本居中 */ ``` - 如果 `mytoast` 有宽度,可以使用: ```css left: calc(50% - width/2); ``` 但宽度可能需要动态计算。 为了确保不受父容器影响: - 确认 `mytoast` 没有被父容器包裹得太深;理想情况下,它应该靠近根元素放置。 在 UniApp 中,组件通常通过模板渲染。如果 `mytoast` 是内嵌在另一个 `fixed` 元素中,它的定位上下文可能被改变。但在 CSS 中,`position: fixed` 总是相对于视口,除非祖先元素有 `transform`、``perspective` 或 `filter` 属性设置(这可能会导致包含块的改变)。 引用 MDN:如果一个元素设置了 `position: fixed`,它是相对于视口定位的,但如果其祖先元素有 `transform`, `perspective`, 或 `filter` 属性不为 `none`,则该元素相对于该祖先定位(而不是视口)。 因此,问题可能在于父容器设置了某些属性导致 `mytoast` 的定位上下文变化。 解决方案: 1. 确保父容器没有设置 `transform`, `perspective`, 或 `filter` 属性,这些可能改变包含块。 2. 将 `mytoast` 移到文档根部,避免任何可能创建新包含块的祖先。 3. 在 CSS 中,直接使用视口相关的居中。 在 UniApp 中,如何实现: - 在 `mytoast` 组件的样式中,添加: ```css position: fixed; left: 50%; transform: translateX(-50%); /* 添加其他样式,如 z-index, background, 等 */ ``` - 如果父容器有 `transform` 等,考虑移除或调整。 用户提到“不受父容器position:fixed影响”,所以可能需要确认组件的挂载位置。 在 Vue 中,组件可以通过 `teleport` 或类似功能挂载到 body 上,以确保独立定位。UniApp 可能支持类似功能。 在 UniApp 的 H5 端,可以使用 Vue 的 `<teleport>`(Vue 3)或 `<portal>`(Vue 2 插件)。但 UniApp 跨平台,需要检查兼容性。 UniApp 提供了 `uni.showToast` API 用于显示 toast,但如果用户自定义组件 `mytoast`,我们需要手动处理。 建议步骤: 1. 在 `mytoast` 组件中使用 CSS 固定定位并居中。 2. 确保组件挂载在根节点附近。 例如,在 App.vue 中放置 `mytoast`,或者使用动态挂载。 结构化回答: - 解释问题:当父容器 fixed 时,子 fixed 元素可能被影响。 - 提供 CSS 解决方案。 - 建议组件位置优化。 - 给出代码示例。 最终回答要用中文。 </think> 在 UniApp 中,当组件 `mytoast` 和其父容器都设置为 `position: fixed` 时,实现 `mytoast` 左右居中且**不受父容器影响**的核心是确保 `mytoast` 的定位直接相对于视口(viewport),而非父容器。以下是分步解决方案和原理说明: ### 解决方案原理 - **问题根源**:如果父容器设置了 `position: fixed` 或其他属性(如 `transform`),会创建新的“包含块”(containing block),导致子元素 `fixed` 定位不再相对于视口[^1][^4]。 - **关键目标**:强制 `mytoast` 脱离父容器上下文,直接基于视口定位。 - **实现方法**: 1. **CSS 定位技巧**:使用 `left: 50%` 结合 `transform: translateX(-50%)` 实现水平居中。 2. **组件挂载优化**:将 `mytoast` 挂载到根组件(如 `App.vue`),避免嵌套在父容器中。 3. **避免冲突属性**:移除父容器中可能改变定位上下文的属性(如 `transform`)。 ### 具体实现步骤 #### 1. 修改 `mytoast` 组件的样式 在 `mytoast` 组件的 CSS 中,添加以下样式: ```css /* 在 mytoast 组件的样式文件中 */ .mytoast { position: fixed; /* 固定定位 */ left: 50%; /* 移动到视口水平中心 */ transform: translateX(-50%); /* 向左偏移自身宽度的50%,实现居中 */ /* 其他样式(如宽度、背景等) */ width: 200rpx; /* 示例宽度,可根据需求调整 */ background-color: rgba(0, 0, 0, 0.7); color: white; text-align: center; padding: 20rpx; border-radius: 10rpx; z-index: 9999; /* 确保显示在最顶层 */ } ``` - **为什么工作**: - `left: 50%` 将元素左侧移动到视口中心。 - `transform: translateX(-50%)` 将元素向左平移自身宽度的50%,达到完美居中。 - `position: fixed` 确保直接相对于视口定位,不受父容器影响[^1][^4]。 #### 2. 确保 `mytoast` 挂载在根节点 在 UniApp 中,将 `mytoast` 组件放置在根组件(如 `App.vue`)中,而非嵌套在父容器内: ```vue <!-- 在 App.vue 中 --> <template> <view> <!-- 其他内容 --> <mytoast v-if="showToast" /> <!-- 确保 mytoast 在根层级 --> </view> </template> ``` - **优势**:避免任何父容器创建定位上下文,确保 `fixed` 始终基于视口。 #### 3. 检查并清理父容器样式 如果父容器使用了以下属性,需移除或重置: ```css /* 父容器应避免的设置 */ .parent-container { /* 移除以下属性以防止创建包含块 */ transform: none; perspective: none; filter: none; /* position: fixed 可保留,但需确保 mytoast 不在其内部 */ } ``` - **原因**:`transform`、`perspective` 或 `filter` 会强制子元素基于父容器定位[^4]。 #### 4. 完整组件示例 ```vue <!-- mytoast.vue 组件文件 --> <template> <view class="mytoast"> {{ message }} </view> </template> <script> export default { props: { message: String } } </script> <style scoped> .mytoast { position: fixed; left: 50%; transform: translateX(-50%); top: 100rpx; /* 垂直位置(可选),例如距顶部100rpx */ width: 60%; background: rgba(0, 0, 0, 0.8); color: white; padding: 30rpx; border-radius: 16rpx; text-align: center; z-index: 10000; } </style> ``` ### 注意事项 - **跨平台兼容性**:在 UniApp 中,`rpx` 单位可自动适配不同屏幕;H5 和小程序均支持 `transform`。 - **垂直居中**:如果需要垂直居中,添加 `top: 50%` 和 `transform: translate(-50%, -50%)`。 - **性能优化**:频繁显示的 `mytoast` 可使用 Vue 的 `<keep-alive>` 缓存组件状态(参考引用[2]),避免重复渲染。 通过以上方法,`mytoast` 将无视父容器的 `position: fixed`,始终在视口水平居中显示[^1][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值