首页 > 其他分享 >CSS之定位Position

CSS之定位Position

时间:2024-04-22 13:00:13浏览次数:29  
标签:定位 盒子 元素 sticky relative Position absolute CSS left

前言

之前在《CSS之浮动》中,我当时是想一起说说定位的,因为我在很多地方看到有把float和position放在一起讲的,说它们的一些属性值可以使元素脱离文档流,但是没想到在准备内容的时候,发现浮动的内容有点多,就先把浮动的内容单独整了一篇。本文就继续来说说定位吧。

基本信息

Name: position
Value: static | relative | absolute | sticky | fixed
Initial: static
Applies to: all elements except table-column-group and table-column
Inherited: no
Percentages: N/A
Computed value: specified keyword
Canonical order: per grammar
Animation type: discrete

尽管在规范文档中我们能看到Position可设置的值有5个,但一般我们不太会去手动设置static,static代表按照普通文档流布局,默认情况下position的值为static。所以我们现在主要关注后面几个值,relative、absolute、sticky和fixed,他们都是按照某个参照物来定位,区别就在于参照物的不同。因此这四个值都会使选中元素的定位算法不同于正常文档流的布局计算;其中sticky是CSS3新增的内容。

relative

从文档中的描述可以看出,relative这个属性值,表示选中的元素将根据原本自身在普通流中的位置来计算定位,是一个相对于自身的偏移。虽然这个元素的位置发生了偏移,但这只是视觉效果并不会改变其他盒子的尺寸或位置,也就是说假设给一个DIV设置了relative,它后面的盒子在计算位置的时候并不会考虑这个DIV产生的偏移。也就是不管这个DIV是不是设置了relative,都不会影响它后面盒子的位置计算。比如我们来看这个例子:

.nav {
    position: relative;
    /*top: -50px;*/
    width: 500px;
    height: 300px;
    background: orange;
}
.section {
    width: 500px;
    height: 300px;
    background: aquamarine;
}
<div class="nav"></div>
<div class="section"></div>
const rect = document.querySelector('.section').getBoundingClientRect();
console.log(rect.top);

在脚本中log出来的值,不管是否给.nav设置了top: -50px;,还是不设置postion这个属性时,结果都是一样的。因此可以看出,虽然设置了position: relative;之后当前元素的定位可以改变,但这个元素原本的几何属性还是会影响文档流其他元素的布局计算。

absolute

absolute属性值会使元素被移出正常的文档流,因此这个元素对同级和祖先元素的尺寸和位置不会产生影响,这就和relative不一样了。并且当盒子设置了absolute,它的定位和尺寸将根据它的包含块来计算。因此盒子的位置和尺寸可以通过toprightbottomleft属性来指定,这些值是相对于包含块的:

the box is positioned and sized solely in reference to its absolute positioning containing block

包含块

那么哪个是绝对定位元素的包含块呢?假设有以下代码:

<div class="container">
    <div class="title">我是标题1</div>
    <div class="content">我是内容我是内容我是内容。天街小雨润如酥,草色遥看近却无,最是一年春好处,绝胜烟柳满皇都。</div>
</div>

单看HTML的话,我们会觉得.title这个元素的包含块是.container这个盒子建立的,但是如果设置了以下CSS,我们会看到页面效果和预期的并不一致。

.container {
    background: orange;
    width: 500px;
}
.title {
    position: absolute;
    top: 0;
    background: aquamarine;
}

image

如果说.title的包含块是.container,那么此时.title这个盒子的顶部应该和.container齐平,但我们看到的页面效果并不是这样的,所以也就是说.title的包含块并不是.container,那么absolute positioning containing block是指什么呢?这在CSS3的文档中直接给出了定义:

Values other than static make the box a positioned box, and cause it to establish an absolute positioning containing block for its descendants.

这句话的意思是,在position的可选属性值中除了static之外的其他值,都会使盒子成为一个被定位的盒子,并且为其后代元素建立绝对定位包含块(absolute positioning containing block)。

因此.title元素的包含块是由祖先元素中离.title最近的一个positioned box所建立的,如果找不到这样的元素,就会去找初始包含块,也就是我们在页面上看到的html根节点,所以我们就在页面中看到了以上效果。

因此如果我们想要改变.title元素的包含块,就需要给它的祖先元素设置postion属性,比如:

.container {
    position: relative;
    background: orange;
    width: 500px;
}

这样我们就能看到如下效果:

image

居中布局

说到这我想到了一个词——”子绝父相“,写过居中布局的小伙伴可能会对这个词有印象,在这种布局方案中,子元素的position会被设置为absolute,父元素的position会被设置为relative。在父元素宽度已知(或隐式声明)或父子元素宽度都已知的情况下,我们可以通过定位相关的属性,使子元素在父元素内部水平居中。可以来看下面这个例子,首先是父子宽度都已知:

<div class="h-c1">
    <div class="h-c1-child">
        <span>谈笑有鸿儒,往来无白丁。谈笑有鸿儒,往来无白丁。</span>
    </div>
</div>

我们可以通过以下CSS样式使子元素居中:

.h-c1 {
    position: relative;
    width: 500px; /*或者宽度隐式声明*/
    height: 100px;
    background: #00bd7e;
}

.h-c1-child {
    position: absolute;
    width: 300px;
    left: 50%; /* 相对于父元素的宽度 */
    margin-left: -150px; /* 子元素宽度的一半的负数 */
    background-color: #fbf;
}

absolute元素的位置将根据它的包含块来计算,关于这个计算可以参考嵌入性属性inset properties的说明

The interpretation of these inset properties varies by positioning scheme:

The inset is a percentage relative to the containing block’s size in the corresponding axis (e.g. width for left or right, height for top and bottom).

所以left: 50%;最终计算出的值是50% * 500px = 250px,也就是说如果没有其他设置的情况下,盒子的左边缘距离包含块的左边缘为250px。为了使盒子居中,我们继续设置margin-left属性值为子元素宽度的一半的负数,使盒子有一个负数的左外边距,这样就能使盒子向左挪动宽度的一半的距离,这样就达到了居中的效果。但这样的写法并不够灵活,每次设置居中时还要去计算盒子宽度的一半,虽然计算量不大,但每次都要计算还是显得繁琐,所以就有了另一种写法:

.h-c1-child {
    position: absolute;
    left: 50%; /* 相对于父元素的宽度 */
    transform: translateX(-50%); /* 相对于自己的宽度 */
    background-color: #fbf;
}

在这段样式中通过使用transformtranslateX函数计算并设置偏移,就能达到一样的效果,在translateX函数中-50%是相对于盒子自身的宽度,此处盒子宽度未设置,会默认为父元素宽度的一半,这与宽度的计算规则有关。

宽度

重新看回absolute,除了用于定位的计算,left/righttop/bottom的值还能决定盒子的尺寸,关于这点我们可以参考文档上关于宽度width的说明

主要关注其中这个等式:

left’ + ‘margin-left’ + ‘border-left-width’ + ‘padding-left’ + ‘width’ + ‘padding-right’ + ‘border-right-width’ + ‘margin-right’ + ‘right’ = width of containing block

这个公式是包含块的宽度等式,文档中分类为三种情况讲解了各属性值的计算:

  • 第一种情况是,leftwidthright均为auto

    If all three of 'left', 'width', and 'right' are 'auto'

  • 第二种情况是,leftwidthright均不是auto

    If none of the three is 'auto'

  • 其他情况属于第三种。

left/right决定盒子宽度的情况就属于第三种情况,也就是left和right已知:

  1. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'

因此在宽度为auto时根据已知的leftright,我们就能根据上述等式求得盒子的宽度。

溢出

对于绝对定位的元素,如果它的包含块是由可滚动盒子建立的,也就是说盒子内容有溢出,我们还需要注意的一点,就是这个绝对定位的元素会包含在滚动的溢出区域内。

is included in the scrollable overflow area of the box that generates is containing block.

有时这可能不是我们想要达到的页面效果。比如以下例子:

.container {
    position: relative;
    background: orange;
    width: 500px;
    height: 30px;
    overflow: auto;
}
.title {
    position: absolute;
    bottom: -10px;
    background: aquamarine;
}

这是我们在使用absolute时需要考虑的一种情况。

fixed

接着来看fixed,fixed可以说和absolute是一样的,除了说它的位置和尺寸是取决于不同的包含块,也就是文档上所说的fixed positioning containing block,所以它被认为是绝对定位的子集。

fixed盒子的位置相对于参考矩形(reference rectangle)是固定的:如果盒子附加到视口(viewport),那么当文档滚动时,盒子不会移动;如果盒子附加到页面区域(page area),当对文档进行分页时,盒子会在每页上复制。

固定定位包含块的建立与绝对定位包含块的建立有所不同:

Properties that can cause a box to establish a fixed positioning containing block include transform, will-change, contain

根据文档描述,我们可以通过transformwill-changecontain属性的设置来触发固定定位包含块的建立。

如果在祖先元素中没有建立这类包含块,分为两类情况处理:

  • 在连续媒体(continuous media)中是布局视口
  • 在分页媒体(paged media)中是每一页的页面区域(固定定位元素相对于页面盒子而言是固定的)

兼容性

仅仅看文档中的描述其实并没有什么问题,但在实际运用中fixed似乎在移动端的iOS上存在一些兼容性的问题,因为不同机型表现不同,然后这些兼容性问题在网上也有不少解决方案,所以我这里就不多说了。我们就看一个例子。

假设有以下代码:

.container {
    height: calc(100vh + 300px);
    background: orange;
}
.nav-bar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    height: 50px;
    background: #3a8ee6;
    color: #fff;
    line-height: 50px;
    text-align: center;
}
input {
    border: 1px solid #ddd;
}
<div class="container">
    <input type="text" placeholder="请输入姓名">
</div>
<div class="nav-bar">提交按钮</div>

以下是fixed在安卓和iOS中的不同效果,安卓是我目前在用的一加9pro(用的是自带浏览器),苹果是一部老手机iPhon8(用的是UC浏览器)。

我处理fixed这类问题的经验并不太多,查了一下资料似乎现在有一个API叫做Visual Viewport可以处理这一类可视视口的问题,有兴趣的小伙伴可以去研究一下。

然后如果我们按照文档描述的方式去触发fixed positioning containing box,会发现它的表现变得有点像绝对定位的样子。

body {
    margin: 0;
    padding: 0;
    transform: translate(0);
}

sticky

最后我们来看sticky,sticky使用较少,因为sticky相对于前面三个是较新的一个值。

sticky类似于相对定位(relative positioning),这里是规范中给出的定义:

Identical to relative, except that its offsets are automatically adjusted in reference to the nearest ancestor scroll container’s scrollport (as modified by the inset properties) in whichever axes the inset properties are not both auto, to try to keep the box in view within its containing block as the user scrolls. This positioning scheme is called sticky positioning.

关于scrollport这个词,这是文档中给出的定义:

The visual “viewport” of a scroll container (through which the scrollable overflow area can be viewed) coincides with its padding box, and is called the scrollport.

翻译过来的意思是:

滚动容器的可视 "视口"(通过它可以查看可滚动溢出区域)与其填充盒子一致,称为滚动口。

我感觉简单理解就是滚动容器的可视部分,如果在这部分上操作触发鼠标滚动事件,可以让我们看到溢出部分的内容。

sticky会在用户滚动页面时产生作用,它的偏移量会自动调整,这是因为在滚动过程中参照点不是固定的,这是与relative、absolute和fixed最大的不同。它的偏移量是相对于最近的滚动容器的滚动口。

MDN中可以看到,sticky元素的嵌入式属性不能全是auto,否则它的效果就相当于relative:

Note: At least one inset property (top, inset-block-start, right, inset-inline-end, etc.) needs to be set to a non-auto value for the axis on which the element needs to be made sticky. If both inset properties for an axis are set to auto, on that axis the sticky value will behave as relative.

我们注意到,在MDN上还有一段描述:

The element is positioned according to the normal flow of the document, and then offset relative to its nearest scrolling ancestor and containing block (nearest block-level ancestor), including table-related elements, based on the values of top, right, bottom, and left.

是关于sticky元素的偏移,在这段内容中描述sticky元素的偏移量是相对于最近的滚动祖先和最近的块级祖先,那么问题来了,如果这两个祖先不是同一个元素,什么时候相对于滚动祖先,什么时候相对于块级祖先呢?我们先来看下面这个例子:

<div class="container">
    <div class="header"></div>
    <div class="list">
        <div class="item">
            <div class="title">列表项111</div>
        </div>
        <div class="item">
            <div class="title">列表项222</div>
        </div>
        <div class="item">
            <div class="title">列表项333</div>
        </div>
        <div class="item">
            <div class="title">列表项444</div>
        </div>
        <div class="item">
            <div class="title">列表项555</div>
        </div>
        <div class="item">
            <div class="title">列表项666</div>
        </div>
        <div class="item">
            <div class="title">列表项777</div>
        </div>
        <div class="item">
            <div class="title">列表项888</div>
        </div>
    </div>
</div>
.container {
    background: orange;
    height: 100vh;
    overflow: scroll;
}
.item {
    height: 300px;
    background: #00bd7e;
    border: 1px solid #333;
}
.title {
    position: sticky;
    top: 10px;
    height: 30px;
    line-height: 30px;
    background: #8cc5ff;
    color: #fff;
}

运行以上代码我们很快能发现,在页面初始阶段,只有第一个.title元素有明显的偏移,其他的.title元素似乎并没有产生效果,而当我们开始滚动页面后,就会发现,当.title元素的块级祖先有一部分进入滚动祖先的滚动溢出区域后,.title元素的偏移量就变成相对于滚动祖先了。

可以看到,MDN中的描述就是这样的:

  • A stickily positioned element is an element whose computed position value is sticky. It's treated as relatively positioned until its containing block crosses a specified threshold (such as setting top to value other than auto) within its flow root (or the container it scrolls within), at which point it is treated as "stuck" until meeting the opposite edge of its containing block.

在sticky元素的包含块在滚动容器内越过一个阈值后,比如上述例子中的top,sticky元素就会进入一个像是“卡住了”的状态,此时sticky距离滚动祖先的顶部距离就是top指定的值,直到sticky元素遇到这个包含块的对边才会结束这种状态。

因此sticky这个属性值可以用于实现类似于吸顶的效果。

因为sticky我个人几乎也没在实际项目中使用过,所以就先说到这吧。

总结

最后来总结一下,在我的项目经历中其实定位属性用的并不算多,absolute用的相对多一点吧,但由于以前我对CSS不太重视的原因,导致为什么居中布局是“子绝父相”以及为什么使用left和right能决定宽度,为什么能这样做在有一段时间内其实我并不清楚,我想应该有一些前端小伙伴和我是一样的情况,那么我觉得其实还是有必要去了解的,因为前端要解决的问题就是视觉和交互,而在实现视觉效果中CSS是很重要的一环,我们不应该因为UI库和框架的使用而忽略了CSS,因为只会使用UI库的话就会被限制在UI库的效果范围内,这就和提倡看技术原版书的原因一样,在搬运或者翻译的过程中,总会有信息的丢失,因此建议小伙伴们还是可以自己再去多多阅读文档。

sticky这个属性值还比较新,可以在条件允许的情况下(比如不用考虑兼容时)做一些探索使用,但由于偏移值是自动调整的,可能会存在性能方面的问题。

另外对于fixed的使用,主要的点还是在于移动端的兼容性问题。

标签:定位,盒子,元素,sticky,relative,Position,absolute,CSS,left
From: https://www.cnblogs.com/beckyyyy/p/18150425

相关文章

  • Django不显示CSS的效果(基于Django模板的静态资源配置问题)
    在搞毕设过程中,习惯起见我直接在网上找了现成的前端设计页面,如图:这种前端项目的结构一般是一个login.html、一个style.css、一个背景图片即可搞定的,直接点击html,浏览器中打开的就是上图所示的界面效果。但是:当我把前端所有文件扔进DjangoApp的templates文件夹后,运行项目的效果......
  • css3多行文本多行文本缩略点击更多展开显示全部
    比如我要实现如下效果:数据集名称展示一行,超出自动省略,末尾增加编辑icon。点击编辑的icon,换成input输入框数据集描述最多展示三行,超出自动省略。末尾增加编辑icon。点击编辑的icon,换成textarea输入框展示一行省略+icon实现单行省略实现,无非是这样<div class="flex-row al......
  • 前端【小程序】14-小程序基础篇【地理位置】【腾讯定位服务】
    腾讯定位服务官网:https://lbs.qq.com位置服务(LBS)是基于用户的位置来提供服务的技术,通过要配合第三方的服务来实现,如腾讯地图、高德地图、百度地图等,享+项目采用的是腾讯的位置服务。申请使用腾讯位置服务需要按如下步骤操作:注册账号创建应用生成key小程序管理后......
  • tailwindcss
    TailwindCSS是一个为快速创建定制化UI组件而设计的实用型框架。与其他CSS框架或库不同,TailwindCSS组件没有预先设置好样式。可以使用Tailwind的低级实用类来为CSS元素设置样式,如margin、flex、color等。自从2017年发布以来,TailwindCSS越来越受欢迎,因为它允许开......
  • C#定位IP地址归属地及运营商等
    一、通过NuGet安装IP2Region(支持Framework)如果是Net6则可以使用IP2Region.Net版本1、IP2Region使用方式:using(DbSearchersearch=newDbSearcher(CommonHelper.MapPath("/Resource/ip2region.db"))){IP2Region.Models.DataBlockipModel=search.MemorySear......
  • css 动画之无缝跑马灯
    <divclass="target-top"style="overflow:hidden"><divclass="target-img"><divv-for="itemintargetFors":key="item.id">...//内容不重要</div></div>......
  • Python Flask+Pandas读取excel显示到html网页: CSS控制表格样式、表头文字居中
    前言全局说明CSS控制表格样式一、安装flask模块二、引用模块三、启动服务模块安装、引用模块、启动Web服务方法,参考下面链接文章:https://www.cnblogs.com/wutou/p/17963563Pandas安装https://www.cnblogs.com/wutou/p/17811839.htmlPandas官方API说明https://pand......
  • CSS 滚动驱动动画终于正式支持了
    在最新的Chrome115中,令人无比期待的CSS滚动驱动动画(CSSscroll-drivenanimations)终于正式支持了\~有了它,几乎以前任何需要JS监听滚动的交互都可以纯CSS实现了,就是这么强大,一起了解一下吧温馨提示:文章略长,建议收藏后反复查阅一、快速入门CSS滚动驱动动画直接介绍 API......
  • css入门
    目录CSS入门css简介导入方式选择器属性盒子模型浮动定位CSS入门css简介ps:学习此内容前,建议先过一边HTML入门,本文档的使用方法和HTML入门是一样的,就不过多赘述了CSS(CascadingStyleSheets)是一种用于定义网页样式和布局的样式表语言。(中文名是层叠样式表)HTML负责定义......
  • PostgreSql: ERROR: value too long for type character varying(1) 定位字段方法
    报错原因设置的数据库字段长度为1,但实际的值超过规定字段,导致报错。解决方案首先,需要定位字段是哪个字段出现的报错,但可惜的是,并没有报出具体是哪个字段在报错,所以只能通过检查Schema,查看哪些字段是长度为1的,然后再进行值的比较,才能锁定位置。ERROR:valuetoolongfortype......