CSS解析:定位和层叠上下文
许多开发人员对定位的理解很粗略,如果不完全了解定位,就很容易给自己挖坑。有时候可能会把错误的元素放在其他元素前面,要解决这个问题却没有那么简单。
一般的布局方法是用各种操作来控制文档流的行为。定位则不同:它将元素彻底从文档流中移走。它允许你将元素放在屏幕的任意位置。还可以将一个元素放在另一个元素的前面或后面,彼此重叠。
默认定位
<style>
div {
/* 初始值静态定位static,未被定位 */
postion:static;
}
</style>
使用静态定位,称之为未被定位。
改成其他值后,说明元素被定位了。
定位的元素从文档流中移走了,意味着允许将元素放在屏幕任意位置,还可以放在另一个元素的前面或后面彼此重叠。
一、固定定位
给一个元素设置position: fixed
就能将元素放在视口的任意位置。这需要搭配四种属性一起使用:top、right、bottom和left
。这些属性的值决定了固定定位的元素与浏览器视口边缘的距离。
比如,top: 3em表示元素的上边缘距离视口顶部3em。
<style>
div {// 可以将元素放在视口任意位置postion:fixed;//通过四个值控制元素与浏览器视口边缘的位置//同时四个值隐式制定元素的大小top:0;left:0;right:0;bottom:0;
}
</style>
(一)用固定定位创建模态框
我们要用这些属性创建一个如图所示的模态框。该模态框会在网页内容前弹出来,它会挡住网页内容,直到关闭该弹窗。
通常情况下,模态框用于要求用户阅读一些内容或者在下一步操作之前输入一些内容。比如,如图的模态框展示了一个表单,用户可以注册一个时事通讯。初始状态下用display:none隐藏弹窗,然后用JavaScript将display改成block以显示弹窗。
<body><header class="top-banner"><div class="top-banner-inner"><p>前往注册账号 <button id="open">注册</button></p></div></header><div id="modal" class="modal"><div class="modal-backdrop"></div><div class="modal-body"><button class="modal-close" id="close">close</button><h2>标题栏</h2><p>请注册电子邮件!</p><form><p><label for="email">email:</label><input type="text" name="email" /></p><p><button type="submit">提交</button></p></form></div></div>
</body>
<script type="text/javascript">var button = document.getElementById('open')var close = document.getElementById('close')var modal = document.getElementById('modal')button.addEventListener('click', function (e) {e.preventDefault()modal.style.display = 'block'})close.addEventListener('click', function (e) {e.preventDefault()modal.style.display = 'none'})
</script>
代码里的第一个元素是顶部条。它包含了触发模态框的按钮。第二个元素是模态框。它包括一个空的modal-backdrop,用来遮住页面剩余部分,将用户的注意力集中到弹窗的内容。弹窗内容在modal-body里。
body {font-family: Helvetica, Arial, sans-serif;/* 设置网页高度,让页面出现滚动条(只是为了演示) */min-height: 200vh;margin: 0;
}button {padding: 0.5em 0.7em;border: 1px solid #8d8d8d;background-color: white;font-size: lem;
}
.top-banner {padding: lem 0;background-color: #ffd698;
}.top-banner-inner {width: 80%;max-width: 1000px;margin: 0 auto;
}.modal {/*默认隐藏模态框。当要打开模态框的时候,JavaScript会设置display: block*/display: none;
}.modal-backdrop {/*当打开模态框时,用半透明的蒙层遮挡网页剩余内容*/position: fixed;top: 0;right: 0;bottom: 0;left: 0;background-color: rgba(0, 0, 0, 0.5);
}.modal-body {/*给模态框的主体定位*/position: fixed;top: 3em;bottom: 3em;right: 20%;left: 20%;padding: 2em 3em;background-color: white;/*允许模态框主体在需要时滚动*/overflow: auto;
}.modal-close {cursor: pointer;
}
在这段CSS里,我们使用了两次固定定位。第一次是modal-backdrop
的蒙层,四个方向都设置为0。
这让蒙层填满整个视口。它还有一个背景色rgba(0, 0, 0, 0.5)。这个颜色符号指定了红、绿、蓝的值均为0,算出来是黑色。第四个值是“alpha”通道,它指定透明度:0是完全透明,1是完全不透明。0.5是半透明,因此该元素下面所有的网页内容就会变暗。
第二次固定定位了modal-body
。它的四条边都在视口内:顶边和底边到视口对应的边缘为3em,左边和右边距离视口对应的边缘为20%。因为它的背景色为白色,所以模态框呈现为一个在屏幕居中的白色盒子。
虽然可以随意滚动网页,但是背景和模态框主体都不会动。打开页面,我们看到屏幕上方有一个带按钮的淡黄色顶部条。点击按钮打开定位的模态框。
因为是固定定位,所以即使滚动页面,模态框的位置也不会变(为了演示,我们特地将body上的min-height值设得很大,撑出了滚动条)。点击模态框顶部的Close按钮,关闭弹窗。这个按钮现在的位置不太对,我们稍后会调整它的位置。
(二)控制定位元素的大小
定位一个元素时,不要求指定四个方向的值,可以只指定需要的方向值,然后用width和/或height来决定它的大小,也可以让元素本身来决定大小。请看如下声明。
position:fixed;
top:lem;
right:1em;
width:20%;
这段代码会将元素放在距离视口顶部和右边1em的位置,宽度为视口宽度的20%。它省略bottom和height属性,元素的高度由自身的内容决定。
例如,这可以用于将一个导航菜单固定到屏幕上。即使用户滚动网页内容,该元素的位置也不会改变。因为固定元素从文档流中移除了,所以它不再影响页面其他元素的位置。
别的元素会跟随正常文档流,就像固定元素不存在一样。也就是说它们通常会在固定元素下面排列,视觉上被遮挡。这对于模态框来说没问题,因为我们希望模态框出现在最前面的中间位置,直到用户关闭它。
而对于其他固定元素,比如侧边导航栏,就需要注意不要让其他内容出现在它下面。通常给其他内容加一个外边距就能解决该问题。比如,将所有内容放在容器里,容器设置right-margin:20%。外边距会流到固定元素下面,内容就不会跟导航栏重叠。
二、绝对定位
固定定位让元素相对视口定位,此时视口被称作元素的包含块(containing block)。
绝对定位的行为也是如此,只是它的包含块不一样。绝对定位不是相对视口,而是相对最近的祖先定位元素。
跟固定元素一样,属性top、right、bottom和left决定了元素的边缘在包含块里的位置。
<style>
div {// 可以将元素放在祖先定位元素的任意位置postion:absolute;//同样,通过四个值控制元素与浏览器视口边缘的位置top:0;left:0;right:0;bottom:0;
}
(一)让Close按钮绝对定位
为了演示绝对定位,我们重新设置Close按钮的位置,将其放在模态框的右上角,如图所示。
我们需要将Close按钮设置为绝对定位。因为它的父元素modal-body是固定定位的,所以会成为Close按钮的包含块。
.modal-close {position:absolute;top:0.3em;right:0.3em;padding:0.3em;cursor:pointer
}
这段代码将按钮放在距离modal-body顶部0.3em、右侧0.3em的位置。通常情况下,就像本例一样,包含块是元素的父元素。如果父元素未被定位,那么浏览器会沿着DOM树往上找它的祖父、曾祖父,直到找到一个定位元素,用它作为包含块。
如果祖先元素都没有定位,那么绝对定位的元素会基于初始包含块(initialcontaining block)来定位。初始包含块跟视口一样大,固定在网页的顶部。
(二)定位伪元素
Close按钮已经定位好了,只是过于简陋。对于这种Close按钮,用户通常期望看到一个类似于x的图形化显示,如图所示。
你可能首先想到将按钮里的文字close换成x,但是这会导致可访问性的问题:辅助的屏幕阅读器会读按钮里的文字。因此要给这个按钮一些有意义的提示。在使用CSS之前,HTML本身必须有意义。
相反,你可以用CSS隐藏close,并显示x。总共需要两步。首先将按钮的文字挤到外面,并隐藏溢出内容。然后将按钮的::after伪元素的content属性设置为x,并让伪元素绝对定位到按钮中间。
.modal-close {position: absolute;top: 0.3em;right: 0.3em;padding: 0.3em;cursor: pointer;font-size: 2em;/*让按钮变成方形*/height: 1em;width: 1em;/*让元素里的文字溢出并隐藏*/text-indent: 10em;overflow: hidden;border: 0;
}
.modal-close::after {position: absolute;line-height: 0.5;top: 0.2em;left: 0.1em;text-indent: 0;/* 添加 Unicode 字符 U+00D7(乘法符号)*/content: '\00D7';
}
以上代码清单明确指定按钮为1em大小的方形。text-indent属性将文字推到右边,溢出元素它的确切值不重要,只要大于按钮宽度即可。
由于text-indent是继承属性,需要在伪类元素选择器上设为0,因此x便不会缩进。伪类元素现在是绝对定位。因为它表现得像按钮的子元素一样,所以定位的按钮就成为其伪元素的包含块。设置一个较小的line-height让伪元素不要太高,用top和left属性让它在按钮中间定位。
绝对定位是定位类型里的重量级选手。它经常跟JavaScript配合,用于弹出菜单、工具提示以及消息盒子。我们将用绝对定位来构建一个下拉菜单,但在此之前,我们需要先看看它的搭档:相对定位。
三、相对定位
相对定位可能是最不被理解的定位类型。当第一次给元素加上position: relative
的时候,你通常看不到页面上有任何视觉改变。相对定位的元素以及它周围的所有元素,都还保持着原来的位置。
如果加上top、right、bottom和left属性,元素就会从原来的位置移走,但是不会改变它周围任何元素的位置
<style>
div {postion:relative;top:1em;left:2em;
}
如图所示,四个inline-block元素,给第三个元素加上三个额外的属性:position: relative、top: 1em、left: 2em
,将其从初始位置移走,但是其他元素没有受到影响。它们还是围绕着被移走元素的初始位置,跟随着正常的文档流。
设置top: 1em将元素从原来的顶部边缘向下移动了1em;设置left: 2em将元素从它来的左侧边缘向右移动了2em。这可能导致元素跟它下面或者旁边的元素重叠。在定位中,也可以使用负值,比如bottom: -1em也可以像top: 1em那样将元素向下移动1em。
跟固定或者绝对定位不一样,不能用top、right、bottom和left改变相对定位元素的大小。这些值只能让元素在上、下、左、右方向移动。可以用top或者bottom,但它们不能一起用(bottom会被忽略)。同理,可以用left或right,但它们也不能一起用(right会被忽略)。
有时可以用这些属性调整相对元素的位置,把它挤到某个位置,但这只是相对定位的一个冷门用法。更常见的用法是使用position: relative
给它里面的绝对定位元素创建一个包含块。
实际应用:
由于绝对定位的最近父元素必须是定位元素,所以将父元素设置为相对定位。既不影响父元素,又可以实现绝对定位。
(一)创建一个下拉菜单
接下来我们用相对和绝对定位创建一个下拉菜单。它的初始状态是一个简单的矩形,当用户鼠标悬停到上面时,会弹出一个链接列表。
<div class="container"><naV><div class="dropdown"><div class="dropdown-label">下拉菜单</div><div class="dropdown-menu"><ul class="submenu"><li>首页</li><li>新闻中心</li><li>服务支持</li><li>关于我们</li></ul></div></div></nav><h1>这是一个h1标题</h1></div>
将它添加到HTML中,放在<div class="modal">
的结束标签后面。这段代码包含了一个容器元素,之后我们会将它的内容居中,并让它跟顶部条的内容对齐。我还在弹出列表下面放了一个<h1>
标签,以展示弹出列表如何出现在其他网页内容前面。
下拉菜单容器包含两个子元素:一个始终显示的灰色矩形标签以及一个下拉菜单。下拉菜单用显示和隐藏表示菜单展开和收起。因为它会是绝对定位的,所以当下拉菜单显示时不会改变网页的布局,这意味着它显示时会出现在其他内容前面。
.container {width: 80%;max-width: 1000px;margin: 1em auto;
}.dropdown {display: inline-block;position: relative;
}.dropdown-label {padding: .5em 1.5em;border: 1px solid #ccc;background-color: #eee;
}.dropdown-menu {display: none;position: absolute;left: 0;top: 2.1em;min-width: 100%;background-color: #eee;
}.dropdown:hover .dropdown-menu {display: block;
}.submenu {padding-left: 0;margin: 0;list-style-type: none;border: 1px solid #999;
}.submenu > li + li {border-top: 1px solid #999;
}.submenu > li >a {display: block;padding: .5em 1.5em;background-color: #eee;color: #369;text-decoration: none;
}.submenu > li > a:hover {background-color: #fff;
}
当移动鼠标指针到主菜单标签时,下拉菜单就会从下面弹出。请注意,这里是在整个容器上设置:hover状态来打开菜单。也就是说只要鼠标停在它的任何内容上,无论是dropdown-label还是dropdown-menu,菜单都会保持打开状态。
绝对定位的dropdown-menu设置了left: 0,让其左边和整个容器的左侧对齐。然后它使用top: 2.1em将其顶部边缘放在标签下面(算上内边距和边框,标签高2.1em)。
min-width为100%,保证它至少等于容器的宽度(容器宽度由dropdown-label决定)。之后用submenu类给下来菜单内的菜单加上样式。
(二)创建CSS三角形
下拉菜单距离完美还差一步。现在它已能正常工作,但用户无法一眼察觉到主菜单标签下面还有更多内容。我们来给标签加上一个小的向下箭头,告诉用户还有更多内容。
我们可以用边框画一个三角形当作向下箭头。这里用标签的::after伪元素来画三角形,然后使用绝对定位将它放到标签的右边。大多数情况下,我们会给一个元素加上较细的边框,通常1px或者2px就够了,但如果把边框变得像图那样粗呢?图中给每条边都加了独特的颜色,用来标出每条边的起始位置。
注意观察角上两条边的边缘接触的地方:它们形成了一个对角边。再观察一下将元素的宽和高缩小到0时会发生什么。所有的边都汇聚到一起最后在中间连接起来了。
元素四周的边都变成了三角形。顶部的边箭头指向下边,右边的边指向左边,以此类推。基于这个现象,可以用一条边作为三角形,然后将剩下的边设置为透明。元素的左右边都透明,而顶部边可见,就会如图所示,形成一个简单的三角形。
我们给dropdown-label::after伪元素加上样式,做一个三角形,并让它绝对定位。
.dropdown-label {/*增加右侧内边距给箭头留位置*/padding: .5em 2em .5em 1.5em;border: 1px solid #ccc;background-color: #eee;}.dropdown-label::after {content: "";position: absolute;right: 1em;/*在标签的右边定位元素*/top: 1em;border: 0.3em solid;border-color: black transparent transparent;}.dropdown:hover .dropdown-label::after {top: 0.7em;border-color: transparent transparent black;}
伪元素因为没有内容,所以没有宽或高。然后用border-color简写属性设置上边框为黑色,左右和下面的边框为透明,构造一个向下的箭头。dropdown-label右边用内边距留出了空间,用来放三角形。最后的效果如图所示。
打开菜单,箭头方向反转,朝向上面,表示菜单可以被关闭。微调top值(从1em到0.7em),让向上的箭头看起来跟向下的箭头处于相同的位置。
另外你也可以用一个图片或者背景图来实现箭头,但是用短短几行CSS代码就可以为用户免去不必要的网络请求。加上这个小小的箭头,能给网站或应用程序增色不少。
这项技术还可以用来构建其他复杂形状,比如梯形、六边形和星形。查看用CSS构建的各种形状,可以访问css-tricks网站上的文章The Shapes of CSS。
四、层叠上下文
定位非常有用,但也需要弄清楚它会带来什么后果。当把一个元素从文档流中移除时,我们就需要管理之前由文档流处理的所有事情了。
首先要确保元素不会不小心跑到浏览器视口之外,导致用户会看不到元素。
其次要保证元素不会不小心挡住重要内容。
最后还有层叠的问题。在同一页面定位多个元素时,可能会遇到两个不同定位的元素重叠的现象。有时我们会发现“错误”的元素出现在其他元素之前。
(一)层叠顺序
浏览器将HTML解析为DOM的同时还创建了另一个树形结构,叫作渲染树(render tree)。它代表了每个元素的视觉样式和位置。同时还决定浏览器绘制元素的顺序。
顺序很重要,因为如果元素刚好重叠,后绘制的元素就会出现在先绘制的元素前面。通常情况下(使用定位之前),元素在HTML里出现的顺序决定了绘制的顺序。考虑以下代码里的三个元素:
<div>one</div>
<div>two</div>
<div>three</div>
它们的层叠行为如图所示。这里使用了负的外边距让元素重叠,但并未使用任何定位。后出现在标记里的元素会绘制在先出现的元素前面。
定位元素时,这种行为会改变。浏览器会先绘制所有非定位的元素,然后绘制定位元素。默认情况下,所有的定位元素会出现在非定位元素前面。如图所示,给前两个元素加了position: relative
,它们就绘制到了前面,覆盖了静态定位的第三个元素,尽管元素在HTML里的顺序并未改变。
注意,在定位元素里,第二个定位元素还是出现在第一个定位元素前面。定位元素会被放到前面,但是基于源码的层叠关系并没有改变。
也就是说在上述网页里,模态框和下拉菜单都会出现在静态内容之前(符合预期),但是源码里后出现的元素会绘制在先出现的元素之前。
解决这个问题的一个办法是在源码里将<div class="modal">
及其内容移到下拉菜单后面。通常情况下,模态框要放在网页内容的最后,</body>
关闭标签之前。大多数构建模态框的JavaScript库会自动这样做。因为模态框使用固定定位,所以不必关心它的标记出现在哪里,它会一直定位到屏幕中间。
改变固定定位元素的标记位置不会产生不好的影响,但是对相对定位或绝对定位的元素来说,通常无法用改变标记位置的方法解决层叠问题。相对定位依赖于文档流,绝对定位元素依赖于它的定位祖先节点。这时候需要用z-index属性来控制它们的层叠行为。
(二)使用Z-INDEX控制层叠顺序
z-index属性的值可以是任意整数(正负都行)。z表示的是笛卡儿x-y-z坐标系里的深度方向。拥有较高z-index的元素出现在拥有较低z-index的元素前面。拥有负数z-index的元素出现在静态元素后面。
使用z-index是解决网页层叠问题的第二个方法。该方法不要求修改HTML的结构。将modal-backdrop的z-index设置为1,将modal-body的z-index设置为2(确保模态框的主体在蒙层前面)。
.modal-backdrop {/*当打开模态框时,用半透明的蒙层遮挡网页剩余内容*/position: fixed;top: 0;right: 0;bottom: 0;left: 0;background-color: rgba(0, 0, 0, 0.5);z-index: 1;}.modal-body {/*给模态框的主体定位*/position: fixed;top: 3em;bottom: 3em;right: 20%;left: 20%;padding: 2em 3em;background-color: white;/*允许模态框主体在需要时滚动*/overflow: auto;z-index: 2;}
z-index的行为很好理解,但是使用它时要注意两个小陷阱。
第一,z-index只在定位元素上生效,不能用它控制静态元素。
第二,给一个定位元素加上z-index可以创建层叠上下文。
(三)层叠上下文
一个层叠上下文包含一个元素或者由浏览器一起绘制的一组元素。其中一个元素会作为层叠上下文的根,比如给一个定位元素加上z-index的时候,它就变成了一个新的层叠上下文的根。所有后代元素就是这个层叠上下文的一部分。
实际上将层叠上下文里的所有元素一起绘制会造成严重的后果:层叠上下文之外的元素无法叠放在层叠上下文内的两个元素之间。
换句话说,如果一个元素叠放在一个层叠上下文前面,那么层叠上下文里没有元素可以被拉到该元素前面。同理,如果一个元素被放在层叠上下文后面,层叠上下文里没有元素能出现在该元素后面。
<div class="box one positioned">one<div class="absolute">nested</div>
</div>
<div class="box two positioned">two</div>
<div class="box three">three</div>
这段代码包含了三个盒子,其中两个被定位,并且z-index为1,第一个盒子里面有一个绝对定位的元素,它的z-index为100。
body {margin: 40px;
}
.box {display: inline-block;width: 200px;line-height: 200px;text-align: center;border: 2px solid black;background-color: #ea5;margin-left: -60px;vertical-align: top;
}
.one {margin-left: 0;
}
.two {margin-top: 30px;
}
.three {margin-top: 60px;
}
.positioned {position: relative;background-color: #5ae;z-index: 1;
}
.absolute {position: absolute;top: 1em;right: 1em;height: 2em;background-color: #fff;border: 2px dashed #888;z-index: 100;line-height: initial;padding: 1em;
}
虽然第一个盒子里绝对定位的子元素nested的z-index很高,但还是出现在第二个盒子后面,因为它的父元素,即第一个盒子形成的层叠上下文在第二个盒子后面
叠放在第二个盒子后面的第一个盒子是一个层叠上下文的根。因此,虽然nested所在div的z-index值很高,但是它内部的绝对定位元素不会跑到第二个盒子前面。在浏览器开发者工具里试验一下,感受这种关系,改变每个元素的z-index看看会发生什么。
给一个定位元素加上z-index是创建层叠上下文最主要的方式,但还有别的属性也能创建,比如小于1的opacity属性,还有transform、filter属性。由于这些属性主要会影响元素及其子元素渲染的方式,因此一起绘制父子元素。文档根节点(
<html>
)也会给整个页面创建一个顶级的层叠上下文。
所有层叠上下文内的元素会按照以下顺序,从后到前叠放:
❑ 层叠上下文的根
❑ z-index为负的定位元素(及其子元素)
❑ 非定位元素
❑ z-index为auto的定位元素(及其子元素)
❑ z-index为正的定位元素(及其子元素)
如果不根据组件的优先级定义清晰的层叠顺序,那么一个样式表很容易演变成一场z-index大战。如果没有清晰的说明,开发人员在给一个模态框之类的元素添加样式时,为了不被其他元素遮挡,就会设置一个高得离谱的z-index,比如999999。
这样的事情重复几次后,大家就只能凭感觉给一个新的组件设置z-index。如果你使用预处理器,比如LESS或SASS,或者你支持的所有浏览器都支持自定义属性,就能很方便地处理这个问题。将所有的z-index都定义为变量放到同一个地方,如下代码片段所示。这样就能清晰地看到哪些元素在前哪些元素在后。
--z-loading-indicator:100;
--2-nav-menu:200;
--z-dropdown-menu:400;
--z-modal-backdrop:300;
--z-modal-body:410;
将增量设为10或者100,这样就能在需要的时候往中间插入新值。
如果发现z-index没有按照预期表现,就在DOM树里往上找到元素的祖先节点,直到发现层叠上下文的根。然后给它设置z-idnex,将整个层叠上下文向前或者向后放。还要注意多个层叠上下文嵌套的情况。
网页很复杂时,很难判断是哪个层叠上下文导致的问题。因此,在创建层叠上下文的时候就一定要多加小心,没有特殊理由的话不要随意创建,尤其是当一个元素包含了网页很大一部分内容的时候。
尽可能将独立的定位元素(比如模态框)放到DOM的顶层,结束标签</body>
之前,这样就没有外部的层叠上下文能束缚它们了。
有些开发人员会忍不住给页面的大量元素使用定位。一定要克制这种冲动。定位用得越多,网页就越复杂,也就越难调试。如果你定位了大量元素,就回头评估一下现在的情况,尤其是当你发现很难调试出自己想要的布局时,一定要反思。
如果可以用别的方法实现某个布局,应该优先用那些方法。如果能够依靠文档流,而不是靠明确指定定位的方式实现布局,那么浏览器会帮我们处理好很多边缘情况。记住,定位会将元素拉出文档流。
一般来说,只有在需要将元素叠放到别的元素之前时,才应该用定位。
五、粘性定位
人们已经用四种主要的定位类型(静态、固定、绝对以及相对)很长时间了,不过现在浏览器还提供了一种新的定位类型:粘性定位(sticky positioning)。
它是相对定位和固定定位的结合体:正常情况下,元素会随着页面滚动,当到达屏幕的特定位置时,如果用户继续滚动,它就会“锁定”在这个位置。最常见的用例是侧边栏导航。
网页刚加载的时候,侧边栏的位置一切正常。网页滚动,它也跟着滚动直到滚到快要离开视口的时候,它会锁定在那个位置。当网页的剩余部分继续滚动时,它却好像固定定位的元素一样停留在屏幕上。
接下来修改网页结构,定义两栏。在HTML里将容器改成如代码所示的代码。把之前的内容(下拉菜单和网页标题)放在左边栏,再添加一个右边栏放“affix”菜单。
<div class="container"><main class="col-main"><nav><div class="dropdown"><div class="dropdown-label">下拉菜单</div><div class="dropdown-menu"><ul class="submenu"><li>首页</li><li>新闻中心</li><li>服务支持</li><li>关于我们</li></ul></div></div></nav><h1>这是一个h1标题</h1></main><aside class="col-sidebar"><div class="affix"><ul class="submenu"><li>首页</li><li>新闻中心</li><li>服务支持</li><li>关于我们</li></ul></div></aside>
</div>
接下来更新CSS,将容器设为弹性容器,设置两栏的宽度。本例复用了下拉菜单的子菜单的样式,当然你也可以给侧边栏添加其他的元素和样式。
.container {display: flex;width: 80%;max-width: 1000px;margin: lem auto;min-height: 100vh;
}
.col-main {flex: 1 80%;
}
.col-sidebar {flex: 20%;
}
.affix {position: sticky;top: 1em;
}
以上代码主要用来设置两栏布局。最后只用了两句声明来给affix元素定位。
top值设置了元素最终固定的位置:距离视口的顶部1em。因为粘性元素永远不会超出父元素的范围,所以本例中affix不会超出col-sidebar的范围。
当滚动页面的时候,col-sidebar会一直正常滚动,但是affix会在滚动到特定位置时停下来。如果继续滚动得足够远,粘性元素还会恢复滚动。这种情况只在父元素的底边到达粘性元素的底边时发生。注意,只有当父元素的高度大于粘性元素时才会让粘性元素固定,因此这里特意给弹性容器加上min-height,以便让父元素足够高。
.sticky {//粘性定位元素的表现与其父元素有关。position: sticky;//必须指定 top、right、bottom、left 四个阈值的其中之一,粘性定位才会生效top: 10px;
}
使用粘性定位时要注意浏览器的兼容性。
相关文章:

CSS解析:定位和层叠上下文
许多开发人员对定位的理解很粗略,如果不完全了解定位,就很容易给自己挖坑。有时候可能会把错误的元素放在其他元素前面,要解决这个问题却没有那么简单。 一般的布局方法是用各种操作来控制文档流的行为。定位则不同:它将元素彻底…...

无名管道与有名管道的区别(C语言)
目录 一、引言 二、无名管道(匿名管道) 1.概念 2.特点 3.使用方法 三、有名管道(命名管道) 1.概念 2.特点 3.使用方法 四、总结 本文将详细介绍在C语言中无名管道(匿名管道)与有名管道(命名…...

Vue+Nginx前端项目多种方式部署一文搞定(练习源码自取)
目录 介绍 本地项目部署 nginx部署 云端服务器部署 介绍 对于Vue项目而言,Nginx可以轻松地配置来处理SPA的路由问题,即对于所有未定义的路径请求返回index.html,这样前端路由机制就可以接管URL的处理。此外,Nginx支持反向代理设…...

MATLAB 中双引号 ““ 和单引号 ‘‘ 的区别详解
在 MATLAB 中,双引号 "" 和单引号 都可以用来表示字符串,但它们的作用和底层类型是不同的。理解它们之间的区别,对于正确使用字符串处理功能非常重要。本文将深入探讨 MATLAB 中 "" 和 的区别,以及在实际编…...

Linux概述、远程连接、常用命令
Linux介绍 Linux操作系统介绍 Linux操作系统的特点 开源免费安全稳定可移植性好 Linux可以安装在不同的设备上 高性能 Linux的使用领域 应用服务器数据库服务器网络服务器虚拟化云计算嵌入式领域个人PC移动手机 Linux文件系统和目录 /:根目录,唯一/h…...

闯关leetcode——1.Two Sum
大纲 题目地址内容 解题 题目 地址 https://leetcode.com/problems/two-sum/description/ 内容 Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. You may assume that each input would hav…...

02 Shell Script注释和debug
Shell Script注释和debug 一、ShellScript注释 # 代表不解释不执行 语法:# # 创建myshell.sh文件 [rootlocalhost ~]# vi myshell.sh # 写入内容 #!/bin/bash# 打印hello world(正确) echo "hello world" echo "he…...

如何设计项目架构
设计项目架构是软件开发过程中的关键步骤之一,它涉及到如何组织系统的各个部分以及它们之间的交互方式。良好的架构设计能够提高系统的可维护性、扩展性和性能。以下是设计C项目架构的一些基本原则和步骤: 1. 明确需求 在设计任何系统之前,…...

OTG配置为USB盘之二
在前面一节中,描述了otg配置U盘时涉及到的知识点,本篇用统一的脚本完成。 目标 将3588开发板子配置为一个U盘。 配置文件和脚本 配置文件 说明: 1)USB_FUNCS是将开发板配置为什么类型的设备,例如u盘,配置um…...

如何使用 PHP 函数与其他 Web 服务交互?
在 PHP 中,我们可以使用 cURL 或者 file_get_contents 函数与其他 Web 服务进行交互。 使用 cURL 函数 cURL 是一个库,它允许你使用各种类型的协议来发送数据,并从服务器获取数据。 $curl curl_init(‘http://example.com/api’); curl_s…...

.NetCore+vue3上传图片 Multipart body length limit 16384 exceeded.
实现目标。点击图片上传头像 效果图 前端部分图片上传关键代码 <div class"avatar-wrap"><el-imagestyle"width: 154px; height: 154px":src"form.headPic":fit"fit"/></div><div class"upload-box"…...

机器学习如何用于音频分析?
机器学习如何用于音频分析? 一、说明 近十年来,机器学习越来越受欢迎。事实上,它被用于医疗保健、农业和制造业等众多行业。随着技术和计算能力的进步,机器学习有很多潜在的应用正在被创造出来。由于数据以多种格式大量可用&…...

适合程序员在周末阅读的历史书籍:理解人性和世界
一、《人类简史:从动物到上帝》 这本书提供了对人类历史和社会发展的深刻洞察,帮助读者理解人类过去、现在和可能的未来。 《人类简史:从动物到上帝》是以色列历史学家尤瓦尔赫拉利(Yuval Noah Harari)创作的一部极具影…...

探索Mem0:下一代人工智能与机器学习内存管理基础设施(二)Mem0+Ollama 部署运行
探索Mem0:下一代人工智能与机器学习内存管理基础设施(二) Mem 0(发音为“mem-zero”)通过智能记忆层增强AI助手和代理,实现个性化的AI交互。Mem 0会记住用户偏好,适应个人需求,并随着时间的推移不断改进,使其成为客户支持聊天机器人,AI助手和自治系统的理想选择。 …...

C++入门10——stack与queue的使用
目录 1.什么是stack? stack的使用 2.什么是queue? queue的使用 3.priority_queue 3.1 什么是priority_queue? 3.2 priority_queue的使用 1.什么是stack? 在官网中,对stack有这样的介绍: Stacks are a type o…...

詳細解析軟路由與代理爬蟲池-okeyproxy
什麼是軟路由? 軟路由,顧名思義,就是通過軟體實現的路由器功能。與傳統的硬體路由器不同,軟路由通常是基於PC或單板電腦(如樹莓派)運行的路由器軟體。 靈活性高:可以根據需求安裝各種插件和服…...

视频监控管理平台LntonAIServer视频智能分析噪声检测应用场景
在视频监控系统中,噪声问题常常影响到视频画面的清晰度和可用性。噪声可能由多种因素引起,包括但不限于低光环境、摄像机传感器灵敏度过高、编码压缩失真等。LntonAIServer通过引入噪声检测功能,旨在帮助用户及时发现并解决视频流中的噪声问题…...

技术分享-商城篇-用户中心-注销修改(二十三)
前言 上一篇文章技术分享-商城篇-用户中心(二十二)在构建高效、安全的商城用户中心时,确保用户账户的安全性与便捷性至关重要。本文将重点阐述三大核心功能——修改密码、协议授权及注销账户的业务逻辑、设计思路及注意事项,旨在…...

Linux-实用指令
目录 前言 指定运行级别 基本介绍 切换运行级别 指令类 帮助指令 man 获得帮助信息 help指令 文件目录类 pwd指令 ls指令 cd指令 mkdir命令 rmdir指令删除空目录 touch指令 cp指令 rm指令 mv指令 cat指令 more指令 less指令 echo指令 head指令 tail指令…...

【MySQL00】【 杂七杂八】
文章目录 一、前言二、MySQL 文件1. 参数文件2. 日志文件3. 套接字文件4. pid 文件5. 表结构定义文件6. InnoDB 存储引擎文件 二、BTree 索引排序三、InnoDB 关键特性1. 插入缓冲1.1 Insert Buffer 和 Change Buffer1.1 缓冲合并 2. 两次写2. 自适应哈希索引3. 异步IO4. 刷新邻…...

计算机网络 第2章 物理层
文章目录 通信基础基本概念信道的极限容量编码与调制常用的编码方法常用的调制方法 传输介质双绞线同轴电缆光纤以太网对有限传输介质的命名规则无线传输介质物理层接口的特性 物理层设备中继器集线器一些特性 物理层任务:实现相邻节点之间比特(0或1&…...

解决:Module build failed (from ./node_modules/sass-loader/dist/cjs.js)问题
一、问题 Module build failed (from ./node_modules/sass-loader/dist/cjs.js): Error: Cannot find module sass 二、解决方法 1.清除缓存 npm cache clean --force2.重构项目 npm install 3.更新(获取最新的)node-sass和sass-loader依赖包 npm …...

【 html+css 绚丽Loading 】 000041 三才移形三角
前言:哈喽,大家好,今天给大家分享htmlcss 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕…...

ASP.NET Core 入门教学十六 防止常见的Web攻击
在ASP.NET Core中,防止常见的Web攻击是非常重要的,以确保应用程序的安全性。以下是一些常见的Web攻击类型及其防范措施: 1. 跨站脚本攻击(XSS) 跨站脚本攻击(XSS)是一种通过在网页中注入恶意脚…...

单刀直入@ComponentScan之 资源加载
欢迎大家入坑,所谓师傅领进坑爬出去靠个人,首先我要说的是这个是上一篇《单刀直入ComponentScan》的姊妹篇哈,接着把没聊透的事说明白,咱不是虎头蛇尾的人。 资源加载是啥意思 scan ,都认识吧,小学词汇连…...

SAPUI5基础知识25 - 聚合绑定(Aggregation Binding)
1. 背景 Aggregation Binding 是 SAPUI5 中的一种数据绑定方式,用于将数据模型中的集合(如数组)绑定到 UI 控件的聚合(如列表项、表格行等)。 常见的场景包括将一个数组绑定到 sap.m.List 的 items 聚合,…...

【Python 千题 —— 算法篇】寻找两个正序数组的中位数
Python 千题持续更新中 …… 脑图地址 👉:⭐https://twilight-fanyi.gitee.io/mind-map/Python千题.html⭐ 题目背景 在处理大规模数据时,我们经常需要对数据进行排序和分析。一个常见问题是如何高效地从两个正序数组中找出它们的中位数。…...

Autoware 定位之初始姿态输入(九)
0. 简介 这一讲按照《Autoware 技术代码解读(三)》梳理的顺序,我们来说一说Autoware中的初始化操作,这个软件包当中完成了ekf_localizer发送初始姿态的包。它接收来自GNSS/用户的粗略估计的初始姿态。将姿态传递给ndt_scan_match…...

C# 自定义传值窗体-适合多参数传值
将子窗体的值回传到父窗体中,或者最简单的需要一个设置参数的对话框,其作用也就是得到其中的参数。下面我们详细介绍实现的过程。 文章目录 一、定义一个事件类二、在参数窗体中定义事件三、订阅事件消息 一、定义一个事件类 首先,我们必须…...

Ubuntu20.04+ros-noetic配置Cartographer
一、概述 因为要配置激光SLAM,Cartographer属于激光雷达SLAM 中比较经典的一款,在学习之前先将其在Ubuntu20.04首先配置出来并成功运行demo。 二、具体操作 (一)概述 使用平台是Windows的wsl2上的Ubuntu20.04子系统,…...