固定定位存在的问题:
固定定位可以使得一个元素固定在页面上,哪怕出现滚动条也无法滚动这个元素。但固定定位在ie6及以下的浏览器和移动端的浏览器往往会失效(如下)。所以,现在就非常需要一种方案使得可以兼容这些浏览器。接下来的笔记将一步一步介绍解决问题的方案。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
body{
height: 3000px;
}
#box{
position: fixed;
width: 200px;
height: 200px;
background-color: pink;
margin-top: 20px;
margin-left: 20px;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
方案就是使用绝对定位来实现固定定位的效果,因为绝对定位是每个浏览器都兼容的 。接下来就来一步一步介绍这个方案的实现过程以及原因。
首先我们先来思考一个问题:当内容高度超过视口,出现系统滚动条时,滚动条属于谁?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#box{
height: 3000px;
background-color: lightgoldenrodyellow;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
上面的代码中,box的高度被设为3000px,那么body元素的高度会被撑开到3000px,接着html元素的高度会被body撑开到3000px,这时出现了滚动条:
那么这个滚动条是属于谁的呢?我们知道,只有当后代元素的高度超出了祖先元素的高度时,祖先元素为了能够向用户展示自己内部元素的全部内容,才会出现滚动条。此时我们已知是box的高度3000px太高导致祖先元素出现了滚动条,所以现在只要知道这个祖先元素是谁,也就会知道滚动条是属于谁的。
祖先元素会是body或者html吗?我们先来将这两者的高度变小一点再加个边框来看看滚动条会不会出现在它们身上:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html{
height: 80%;
margin: 30px;
border: 1px solid;
}
body{
height: 80%;
margin: 30px;
border: 1px solid red;
}
#box{
height: 3000px;
background-color: lightgoldenrodyellow;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
很明显,滚动条并没有出现在body或者是html身上。所以祖先元素并不是它们俩。那到底是谁呢?原来,祖先元素是文档(document),也就是说,系统默认的滚动条是属于html的上一层document的,也可称之为窗口。
先停一下……
现在我们先回到最开始提出的方案身上:使用绝对定位来实现固定定位的效果。让我们来看看如果页面出现系统滚动条,滚动滚动条时绝对定位的元素是怎样的:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#wrap{
height: 3000px;
}
#inner{
position: absolute;
width: 200px;
height: 200px;
background-color: pink;
margin: 100px 0 0 100px;
}
</style>
</head>
<body>
<div id="wrap">
<div id="inner"></div>
</div>
</body>
</html>
很明显啊,元素被滚动走了。
那么元素为什么会被滚动走呢?到这里,我们先来学习两个概念:
(1)视口:可视的窗口,视口就是我们眼睛可见的浏览器的宽高(如下红方框)所表现出来的窗口。
(2)初始包含块:初始包含块是一个视口大小的矩形。初始包含块跟视口没有关系,只是一开始大小和位置都跟视口一样而已。并且当系统滚动条滚动时,初始包含块也会跟着走。
有了这两个概念,就不难理解为什么被设置成绝对定位的元素会跟着滚动条跑了。inner被设置成绝对定位后,就需要找到它的包含块(对于开启了定位的元素来说包含块是离它最近的开启了定位的祖先元素)来定位自己的位置,那么它的包含块是body或者html吗?明显不是,因为body和html都没有开启定位。那么它的包含块就只能是初始包含块了。前面在讲初始包含块的概念时提到过:当系统滚动条滚动,初始包含块也会跟着走。所以,我们滚动系统滚动条时,初始包含块会跟着走,初始包含块一走,被设置成绝对定位的inner也会走,这才会出现了inner会随着滚动条一起走的场景。
了解了inner会随着滚动条走的原因后,不难想到,只要在滚动滚动条时不带着初始包含块走,那么inner也就不会跟着走,这样也就达到了利用绝对定位来实现固定定位效果的目的。
那要如何做才能做到在滚动滚动条时不带着初始包含块走呢?在前面我们停一下的时候已经知道了一件事情:系统默认的滚动条是属于html的父元素document的。也就是说,我们现在之所以滚动滚动条时会带着初始包含块一起走,是因为我们滚动的这个滚动条是系统默认的滚动条,而且这个系统滚动条是属于document的。那也就是说,当我们滚动的滚动条不是属于document的系统默认的滚动条时,就不会带着初始包含块走了。
那要怎么做才能使得我们滚动的滚动条不是属于document的系统默认的滚动条呢?换句话说,要怎么做才能换掉滚动条的主人呢?
回到这个代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html{
height: 80%;
margin: 30px;
border: 1px solid;
}
body{
height: 80%;
margin: 30px;
border: 1px solid red;
}
#box{
height: 3000px;
background-color: lightgoldenrodyellow;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
这时滚动条还是属于document的。我们来为html或者body元素分开加上 overflow: auto; 这句话看看。这句话的意思就是当html或者body有超过自己高度的后代元素时,自己就出现滚动条,没有超过滚动条就不出现。来看看有没有用:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html{
height: 80%;
margin: 30px;
border: 1px solid;
overflow: auto;
}
body{
height: 80%;
margin: 30px;
border: 1px solid red;
}
#box{
height: 3000px;
background-color: lightgoldenrodyellow;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html{
height: 80%;
margin: 30px;
border: 1px solid;
}
body{
height: 80%;
margin: 30px;
border: 1px solid red;
overflow: auto;
}
#box{
height: 3000px;
background-color: lightgoldenrodyellow;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
看来单独为html或body设置 overflow: auto; 这句话都没办法拿走滚动条。那如果同时为它们俩设置 overflow: auto; 呢?来看看:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html{
height: 80%;
margin: 30px;
border: 1px solid;
overflow: auto;
}
body{
height: 80%;
margin: 30px;
border: 1px solid red;
overflow: auto;
}
#box{
height: 3000px;
background-color: lightgoldenrodyellow;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
滚动条出现在了body身上。也就是说,当我们同时为html和body设置 overflow: auto; 时,滚动条会出现在body身上。
那滚动条可以出现在html身上吗?我们来强制给html的overflow改成scroll试试,这句话的意思就是无论有没有后代元素超出,自己都出现滚动条:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html{
height: 80%;
margin: 30px;
border: 1px solid;
overflow: scroll;
}
body{
height: 80%;
margin: 30px;
border: 1px solid red;
overflow: auto;
}
#box{
height: 3000px;
background-color: lightgoldenrodyellow;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
正如红方框所圈出,尽管这样做,html仍然也无法获得滚动条,滚动条只加到了body和document身上。overflow的其他属性值也是一样的,这里不再赘述。总而言之,不管使用哪个属性值html都不会拥有滚动条;而只要html有overflow属性,同时body的overflow属性值不为hidden,那么body就会拥有滚动条。
现在来总结一下:
(1)html身上永远都不会出现滚动条;
(2)html和body其中有一个身上有或没有overflow属性,滚动条都会出现在文档(document)身上;
(3)如果html和body都设置了overflow属性,那么滚动条就会出现在body身上(body的overflow属性值不为hidden)。
现在,我们已经使得原本属于document的滚动条变成了body的滚动条,也就初步使得滚动条的主人换了。但是这还不够,因为我们一般是不愿意通过body或者html来操作页面的,它们俩的问题有很多(这里不多说)。我们一般会先禁用系统默认的滚动条,然后新建一个wrap页面容器,把滚动条给wrap,再在wrap里面写页面上其他的东西:
禁用系统默认的滚动条:下面这么写的原因请参考同级笔记本下的“禁用系统默认滚动条”的笔记
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html{
height: 80%;
margin: 30px;
border: 1px solid;
overflow: hidden;
}
body{
height: 80%;
margin: 30px;
border: 1px solid red;
overflow: hidden;
}
#box{
height: 3000px;
background-color: lightgoldenrodyellow;
}
</style>
</head>
<body>
<div id="box"></div>
</body>
</html>
此时系统默认的滚动条已经禁用了,接下来就创建一个供我们画页面的页面容器wrap,把滚动条给wrap:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html{
height: 80%;
margin: 30px;
border: 1px solid;
overflow: hidden;
}
body{
height: 80%;
margin: 30px;
border: 1px solid red;
overflow: hidden;
}
#wrap{
height: 80%;
margin: 30px;
border: 10px solid blue;
overflow: auto;
}
#box{
height: 3000px;
background-color: lightgoldenrodyellow;
}
</style>
</head>
<body>
<div id="wrap">
<div id="box">这里面可以画页面!</div>
</div>
</body>
</html>
此时,wrap里面就可以继续写我们需要的其他元素,并且我们也成功地将滚动条的主人换成了wrap(这时再滚动滚动条就不会带着初始包含块走了),不用再操作document,也不用再操作body。就是wrap有点小,并且上面的样子看着有点丑,所以为了让wrap看起来更像我们平常写代码时一开始看到的界面(什么元素都没有),我们可以这样改改:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html,body{
height: 100%;
overflow: hidden;
}
#wrap{
height: 100%;
overflow: auto;
}
#box{
height: 3000px;
}
</style>
</head>
<body>
<div id="wrap">
<div id="box"></div>
</div>
</body>
</html>
上面这些代码所干的事情就是【禁用系统默认滚动条+使用wrap模拟视口】,当wrap内部有内容高出视口时,wrap会出现滚动条:这是初始化画页面的时候经常要做的一件事情。
这个时候再怎么滚动滚动条也不会带着初始包含块走了,也就不会带着开启了绝对定位的元素走了。这样我们就达到了利用绝对定位代替固定定位的目的,并且这个方案的兼容性还特别地好:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
html,body{
height: 100%;
overflow: hidden;
}
#wrap{
height: 100%;
overflow: auto;
}
#test{
position: absolute;
width: 200px;
height: 200px;
background-color: pink;
margin: 40px;
}
#box{
height: 3000px;
}
</style>
</head>
<body>
<div id="wrap">
<div id="test">我开启了绝对定位,但滚动滚动条时我不会走哦!!!包含块不走,我就不走!!!</div>
<div id="box"></div>
</div>
</body>
</html>
总结:所以怎么解决ie6下固定定位失效的问题?
办法:用绝对定位来模拟固定定位。
(1)禁止系统滚动条;
(2)将滚动条作用在最外层的包裹器上或者在body上;
(3)因为移动包裹器或者body身上的滚动条并不会影响初始包含块的位置,所以一个按照初始包含块定位的元素就不会产生移动。
以上内容均是在B站看尚硅谷css3免费网络课程时学来的。欢迎大家提出问题,希望能给大家带来帮助。侵删。