原稿:语雀 · CSS 题目 · 原目录:面向面经学习 › CSS 题目
绝对定位元素塌陷
三个盒子自外而内嵌套,position属性依次为,fixed, absolute,relative(顺序可能不一样,记不太清了)。如果给最里面的盒子添加padding,问三个盒子的高度会如何变化
.fixed-box {
position: fixed;
margin: 0 auto;
background-color: hotpink;
}
.absolute-box{
position: absolute;
width: 120px;
background-color: indianred;
}
.relative-box{
position: relative;
width: 100px;
height: 80px;
background-color: khaki;
padding: 10px;
}
经过实测,这道题中,relative-box 的 padding 修改时会影响 absolute-box 的高度,而最外层 fixed-box 若不设置高度宽度,则始终保持 0 0
原因:W3 10.6.7 给出说明:对于 BFC 来说
- 绝对定位的子元素会被忽略,无法撑起高度
- float 元素可以撑起其高度
因此最外层盒子的高度不会改变,而绝对定位盒子的高度会随相对定位盒子高度改变
滑动按钮
实现点击右滑动按钮,同时要求 button 自适应父元素宽度
- 滑动使用 transition 即可
- 自适应,可以为 button 设置 width:100%,因为 button 是行内块元素,宽度有效,或者使用 flex 布局
.container {
width: 150px;
}
.btn {
width: 100%;
/* transition:属性名 持续事件 时间函数 推迟变化时间 */
transition: margin-left 1s;
}
.btn:hover {
margin-left: 50px;
}
递归判断 body 结构是否相同
let body1 = document1.body;
let body2 = document2.body;
const compareDOM = function (elem1, elem2) {
let [children1, children2] = [elem1.children, elem2.children];
if (children1.length != children2.length) return false;
for (let i=0; i<children1.length; i++) {
if (!compareDOM(children1[i], children2[i])) return false;
}
if (elem1.tagName != elem2.tagName || elem1.innerText != elem1.innerText)
return false;
return true;
};
console.log(compareDOM(body1, body2));
借助 Element.children() 可获取当前元素的子元素列表(动态的 HTMLCollection),递归的获取子元素即可,到达最底层子节点后比较节点的 tagName 和 innerText,然后返回至上一层——先序遍历
负 maring 意义
- 元素自身无宽度,设置负值水平 maring:增加元素宽度。当设置 margin-left:-100px 元素会在填充完父元素宽度后向左溢出 100px 宽度

如何减少回流重绘
- 使用 transform 实现样式效果:transform 基于合成过程重新生成,不涉及主线程
- 使用 display:none 隐藏元素避免回流重绘、使用 visiablity:hidden 只是避免回流
- 当要有大量新增样式的操作时,可以使用 document fragment 将最终更新的结果插入,这样只触发一次回流重绘
- 使用 rAF 调节渲染:很多情况下频繁的重新渲染无法避免,比如页面滚动与动画,这些场景下通常要保证每秒 60帧的渲染速度,一帧的生成开始于系统的垂直同步信号->处理 IO,调用处理程序-> 执行 rAF() 回调 -> 重新渲染。如果将所有动画函数放在 rAF 回调中,那它就会被放在下一帧渲染前执行,这样将动画效果集中在一次回流重绘中就能渲染,避免了 DOM 修改时再额外导致回流重绘。
动画:淡出-平移-放大
一个三角形从透明到实体,持续一秒,然后匀速前进五秒,然后变大持续两秒
.triangle { width: 0px; animation: change 8s linear 1 forwards; } @keyframes change { 0% { transform: translateX(0) scale(1); border-left: 50px solid transparent; border-right: 50px solid transparent; border-bottom: 85px solid transparent; } 12.5% { transform: translateX(0) scale(1); border-left: 50px solid transparent; border-right: 50px solid transparent; border-bottom: 85px solid orange; } 75% { transform: translateX(300px) scale(1); border-left: 50px solid transparent; border-right: 50px solid transparent; border-bottom: 85px solid orange; } 100%{ transform: translateX(300px) scale(2); border-left: 50px solid transparent; border-right: 50px solid transparent; border-bottom: 85px solid orange; } } </style>
- forwards 表示动画结束时保持结尾帧
- 注意每一个关键帧中要声明不变的属性,假如只在 100% 中声明 scale(2),这个放大会开始于 0%
动画:遮罩 + 弹窗
写css、HTML,完成一个弹窗,弹窗自底部往上弹出,弹窗垂直水平居中,长宽视内容的长宽而定,外部为灰色蒙版

<style>
.cover {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgb(128, 128, 128, 0.7);
z-index: 0;
}
.popup {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background-color: tomato;
padding: 50px 80px;
z-index: 1;
animation: goAbove 0.5s;
}
@keyframes goAbove {
0% {margin-top: 100%;}
100% {margin-top: 0;}
}
</style>
<div>被遮罩掩盖</div>
<div class="popup">这是一个弹窗</div>
- 遮罩的实现需要靠 fixed 布局,指定 left top 为 0,声明颜色时需要使用 rgb(),第四个代表透明度
往 ul 里面插入10000个 li
let list = document.getElementById("list");
let frag = document.createDocumentFragment();
for(let i=0; i<10000; i++){
let li = document.createElement("li");
li.innerText = i;
frag.appendChild(li);
}
list.appendChild(frag);
使用 document.createDocumentFragment() API,创建一个单独的文档对象(一个微型 document),先向该对象上插入 10000 个 <li>,之后将该片段插入 <ul>,其中的 10000 个节点被一次性插入,只触发一次重新渲染过程;如果每次都直接向文档中插入 <li>,就会触发 10000 次重新渲染
判断元素是否在可视区

方法一:
el.offsetTop - document.documentElement.scrollTop <= document.documentElement.clientHeight
offsetTop 是元素盒子边缘距离其 offsetParent padding 的距离,scrollTop 是元素被卷起来的顶部距离当前视口顶部的距离,当 offsetParent 就是 body 时,二者差值就是元素距离当前视口顶端的距离,当这个差值大于 body 的 clientHeight(元素非滚动区域的 padding+content),说明元素还在可视区下方

function checkInView(el) {
// viewPortHeight 兼容所有浏览器写法
const viewPortHeight = document.documentElement.clientHeight
const offsetTop = el.offsetTop
const scrollTop = document.documentElement.scrollTop
const top = offsetTop - scrollTop
return top <= viewPortHeight
}
方法二:
判断 elem.getBoundingClientRect().top >= top 说明未进入可视区,一般认为 gBCR right 和 left 的差值与 offsetWidth 的值相等(content+padding+border+scrollY),而 bottom 和 top 的差值与 offsetHeight 相等(content+padding+border+scrollX)
window.addEventListener("scroll", () => {
checkInView();
});
const checkInView = function () {
const children = Array.from(document.getElementById("list").children);
children.forEach((elem) => {
const viewportHeight = document.documentElement.clientHeight;
const top = elem.getBoundingClientRect().top;
if (top <= viewportHeight) console.log(`${elem.id} is in view`);
else console.log(`${elem.id} is out of view`);
});
};