架是将一个页面划分为若干个窗口, 每一个窗口都是独立;
要实现框架必须使用框架型的DTD;
框架就像一个窗户是由窗格和玻璃组成;
框架中不能有body及body子标记;
框架是由框架集(frameset)和框架页(frame)组成;
格式:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
</head>
<frameset>
<frame />
<frame />
</frameset>
</html>
框架标签:
<frameset></frameset> 放在一个框架文档的<body>标签之前, 也可以嵌在其他框架文档中;
框架中不能有body及body子标记, 框架是由框架集(frameset)和框架页(frame)组成;
<frameset rows="value,value"> 定义一个框架内的行数, 可用像素值或高度百分比;
<frameset cols="value,value"> 定义一个框架内的列数, 可用像素值或宽度百分比;
<frameset cols="120,*"></frameset>
<frameset cols="25%,50%,25%"></frameset>
<frameset rows="120,*"></frameset>
<frameset rows="25%,50%,25%"></frameset>
frameborder 框架边框显示属性
该属性用于指定框架周围是否显示边框, 取值有1(显示边框, 默认值)和0(不显示边框)。
framespacing 该属性用于指定框架之间的间隔, 以像素为单位。
如果不设置该属性,则框架之间没有间隔。
border 指定边框宽度属性
该属性用于指定边框的宽度, 只有在frameborder属性为1时有效。
bordercolor 指定边框颜色
<noframes></noframes> 定义在不支持框架的浏览器中显示什么提示;
<html>
<frameset cols="25%,50%,25%">
<frame src="frame_a.html"/>
<frame src="frame_b.html"/>
<frame src="frame_c.html"/>
<noframes>
<body>您的浏览器无法处理框架!</body>
</noframes>
</frameset>
</html>
<frame> 定义一个框架内的单一窗或窗区域;
<frame src="url"> 规定框架内显示的html文档;
<frame name="name"> 命名框架或区域以便别的框架可以指向它;
<frame marginwidth=""> 定义框架左右边缘的空白大小,必须大于等于1;
<frame marginheight=""> 定义框架上下边缘的空白大小,必须大于等于1;
<frame scrolling=""> 设置框架是否有滚动栏,其值可以是"yes","no",或"auto";
<frame noresize> 禁止用户调整一个框架的大小;其值只有一个"noresize"
<frame src="frame_a.htm" longdesc="w3school.txt" /> longdesc属性指向了带有框架内容长描述的页面:
<iframe></iframe> 创建一个内联的框架;
src 定义在框架中显示的内容的来源;
frameborder 规定是否显示框架周围的边框。(0或1);
align 控制对齐方式(left、right、middle、top、bottom);
height 框架的高度,
width 框架的宽度;
marginheight 定义 iframe 的顶部和底部的边距。
marginwidth 定义 iframe 的左侧和右侧的边距。
scrolling 规定是否在 iframe 中显示滚动条(yes、no、auto)。
name 规定 iframe 的名称。
bordercolor 指定边框颜色
<iframe src ="/index.html" frameborder="0" bordercolor="red">
<p>Your browser does not support iframes.</p>
</iframe>
关于框架集<frameset></frameset>中属性cols rows分割方法理解
<frameset cols="40%,2*,*"> 将窗口分为40%,40%,20%
<frameset cols="100,200,*"> 将窗口分为100像素,200像素和剩下的700像素
<frameset cols="100,*,*"> 将100像素以外的窗口平均分配
<frameset cols="*,*,*"> 将窗口分为三等份
<frameset rows="*,*,*"> 总共有三个按列排列的帧,每个帧占整个浏览器窗口的1/3
<frameset cols="40%,*,*"> 总共有三个按行排列的帧,第一个帧占整个浏览器窗口的40%,剩下的空间平均分配给另外两个帧
<frameset rows="40%,*" cols="50%,*,200">
以上"*"表示剩余部分
总共有六个帧,先是在第一行中从左到右排列三个帧,然后在第二行中从左到右再排列三个帧,即两行三列,
所占空间依据rows和cols属性的值,其中200的单位是像素
属性汇总:
frameset(框架集)
rows 它是将框架集划分为上下型
cols 它是将框架划分为左右型
border 是用于设置框架的边框粗细
frameborder 是用于设置是否显示边框, 取值:yes|no或1|0
bordercolor 用于设置框架边框的颜色
frame(框架页)
noresize 是否可以调整小窗口的大小
name 用于设置小窗口的名称
src 小窗口的url
scroll 是否显示滚动条 yes|no|auto
实例: iframe满屏方案
看个锚点定位的例子
发现页面唰的一些就到顶部了,快到我们懵逼了。。。
开始解决
CSS属性 scroll-behavior 为一个滚动框指定滚动行为,其他任何的滚动,例如那些由于用户行为而产生的滚动,不受这个属性的影响。在根元素中指定这个属性时,它反而适用于视窗。
scroll-behavior:smooth 写在滚动容器元素上,可以让容器的滚动变得平滑。
在网页默认滚动是在<html>标签上,移动端大多数在<body>标签上。
我们可以这样加:
html, body { scroll-behavior:smooth; }
加了以后的效果如下:
这是录制的GIF图,效果没那么好。 大家可以动手试一下,滑动体验非常不错。
兼容性不够好
当然我们可以通过js来做个类似
DOM元素的scrollIntoView() 方法让当前的元素滚动到浏览器窗口的可视区域内,通过触发滚动容器的定位实现。
DOM元素的scrollIntoView()方法 是原生JS 兼容到IE6,兼容性非常好。
参数如下
{ behavior: "auto" | "instant" | "smooth", // 默认 auto block: "start" | "center" | "end" | "nearest", // 默认 center inline: "start" | "center" | "end" | "nearest", // 默认 nearest }
解释一下这三个参数:
用法:
html:
<div class="wrap"> <div onClick="onScrollIntoView()">点击让黑色块到顶部</div> <ul class="body"> <li>1</li> <li>2</li> <li id="box">我是黑色</li> <li>3</li> <li>4</li> </ul> </div>
js:
function onScrollIntoView () { var element = document.getElementById("box"); element.scrollIntoView({behavior: "smooth"}); }
效果:
这回大家再也不用害怕做锚点定位啦。
最后我们在说一个关于页面滚动问题吧,那就是 返回顶部 功能实现
我们常用定时器 setInterval 来不断减去高度。
如:当前距离顶部 1000, 我们每10毫秒减50,
var timer = setInterval(function() { // 定时器 每10毫秒执行一次 // 顶部距离 document.body.scrollTop = 1000 var speed = 50 // 返回顶部速度 document.body.scrollTop = document.body.scrollTop - speed if (document.body.scrollTop === 0) { // 返回到达顶部后, 销毁定时器 clearInterval(timer) } }, 10)
效果:
大家会发现,页面返回是滚动起来很干。 没10毫秒减50. 很平均,在交互上效果并不好。
借鉴上面 scroll-behavior:smooth 的交互效果。 缓动的返回顶部。
改一下计算方式:1000/2 = 500, 500/2 =250, 250/2 = ...... 这样滑动起来是不是就平滑了呢?
换算成公式:开始位置 = 开始位置 + (结束位置 - 开始位置) / 速度
document.body.scrollTop = 1000 + (0 - 1000) / 2
公式太烦了还是上代码吧:
var onTop = function (a, b, c, d) { if (a == b || typeof a != 'number') { return } b = b || 0 c = c || 2 var speed = function () { a = a + (b - b) / c if (a < 1) { d(b, true) return } d(a, false) requestAnimationFrame(speed) } speed() }
调用:
var target = document.body.scrollTop ? document.body : document.documentElement onTop(target.scrollTop, 0, 4, function (value) { target.scrollTop = value })
效果:
Ps: gif录制效果不好,大家可以动手写一下DEMO
CSS in JS是一种解决css问题想法的集合,而不是一个指定的库。从CSS in JS的字面意思可以看出,它是将css样式写在JavaScript文件中,而不需要独立出.css、.less之类的文件。将css放在js中使我们更方便的使用js的变量、模块化、tree-shaking。还解决了css中的一些问题,譬如:更方便解决基于状态的样式,更容易追溯依赖关系,生成唯一的选择器来锁定作用域。尽管CSS in JS不是一个很新的技术,但国内的普及程度并不高。由于Vue和Angular都有属于他们自己的一套定义样式的方案,React本身也没有管用户怎样定义组件的样式,所以CSS in JS在React社区的热度比较高。
目前为止实现CSS in JS的第三方库有很多:点击这里。像JSS、styled-components等。在这里我们就不展开赘述了,这篇文章的重点是JS in CSS。
在上面我们提到CSS in JS就是把CSS写在JavaScript中,那么JS in CSS我们可以推断出就是可以在CSS中使用JavaScript脚本,如下所示。可以在CSS中编写Paint API的功能。还可以访问:ctx,geom。甚至我们还可以编写自己的css自定义属性等。这些功能的实现都基于CSS Houdini。
.el {
--color: cyan;
--multiplier: 0.24;
--pad: 30;
--slant: 20;
--background-canvas: (ctx, geom) => {
let multiplier = var(--multiplier);
let c = `var(--color)`;
let pad = var(--pad);
let slant = var(--slant);
ctx.moveTo(0, 0);
ctx.lineTo(pad + (geom.width - slant - pad) * multiplier, 0);
ctx.lineTo(pad + (geom.width - slant - pad) * multiplier + slant, geom.height);
ctx.lineTo(0, geom.height);
ctx.fillStyle = c;
ctx.fill();
};
background: paint(background-canvas);
transition: --multiplier .4s;
}
.el:hover {
--multiplier: 1;
}
在如今的Web开发中,JavaScript几乎占据了项目代码的大部分。我们可以在项目开发中使用ES 2020、ES2021、甚至提案中的新特性(如:Decorator),即使浏览器尚未支持,也可以编写Polyfill或使用Babel之类的工具进行转译,让我们可以将最新的特性应用到生产环境中(如下图所示)。
而CSS就不同了,除了制定CSS标准规范所需的时间外,各家浏览器的版本、实战进度差异更是旷日持久(如下图所示),最多利用PostCSS、Sass等工具來帮我们转译出浏览器能接受的CSS。开发者们能操作的就是通过JS去控制DOM与CSSOM来影响页面的变化,但是对于接下來的Layout、Paint与Composite就几乎没有控制权了。为了解决上述问题,为了让CSS的魔力不再受到浏览器的限制,Houdini就此诞生。
我们上文中提到JavaScript中进入提案中的特性我们可以编写Polyfill,只需要很短的时间就可以讲新特性投入到生产环境中。这时,脑海中闪现出的第一个想法就是CSS Polyfill,只要CSS的Polyfill 足够强大,CSS或许也能有JavaScript一样的发展速度,令人可悲的是编写CSS Polyfill异常的困难,并且大多数情况下无法在不破坏性能的情况下进行。这是因为JavaScript是一门动态脚本语言。它带来了极强的扩展性,正是因为这样,我们可以很轻松使用JavaScript做出JavaScript的Polyfill。但是CSS不是动态的,在某些场景下,我们可以在编译时将一种形式的CSS的转换成另一种(如PostCSS)。如果你的Polyfill依赖于DOM结构或者某一个元素的布局、定位等,那么我们的Polyfill就无法编译时执行,而需要在浏览器中运行了。不幸的是,在浏览器中实现这种方案非常不容易。
如上图所示,是从浏览器获取到HTML到渲染在屏幕上的全过程,我们可以看到只有带颜色(粉色、蓝色)的部分是JavaScript可以控制的环节。首先我们根本无法控制浏览器解析HTML与CSS并将其转化为DOM与CSSOM的过程,以及Cascade,Layout,Paint,Composite我们也无能为力。整个过程中我们唯一完全可控制的就是DOM,另外CSSOM部分可控。
CSS Houdini草案中提到,这种程度的暴露是不确定的、兼容性不稳定的以及缺乏对关键特性的支持的。比如,在浏览器中的 CSSOM 是不会告诉我们它是如何处理跨域的样式表,而且对于浏览器无法解析的 CSS 语句它的处理方式就是不解析了,也就是说——如果我们要用 CSS polyfill 让浏览器去支持它尚且不支持的属性,那就不能在 CSSOM 这个环节做,我们只能遍历一遍DOM,找到 <style> 或 <link rel="stylesheet"> 标签,获取其中的 CSS 样式、解析、重写,最后再加回 DOM 树中。令人尴尬的是,这样DOM树全部刷新了,会导致页面的重新渲染(如下如所示)。
即便如此,有的人可能会说:“除了这种方法,我们也别无选择,更何况对网站的性能也不会造成很大的影响”。那么对于部分网站是这样的。但如果我们的Polyfill是需要对可交互的页面呢?例如scroll,resize,mousemove,keyup等等,这些事件随时会被触发,那么意味着随时都会导致页面的重新渲染,交互不会像原本那样丝滑,甚至导致页面崩溃,对用户的体验也极其不好。
综上所述,如果我们想让浏览器解析它不认识的样式(低版本浏览器使用grid布局),然而渲染流程我们无法介入,我们也只能通过手动更新DOM的方式,这样会带来很多问题,Houdini的出现正是致力于解决他们。
Houdini是一组底层API,它公开了CSS引擎的各个部分,如下图所示展示了每个环节对应的新API(灰色部分各大浏览器还未实现),从而使开发人员能够通过加入浏览器渲染引擎的样式和布局过程来扩展CSS。Houdini是一群来自Mozilla,Apple,Opera,Microsoft,HP,Intel和Google的工程师组成的工作小组设计而成的。它们使开发者可以直接访问CSS对象模型(CSSOM),使开发人员可以编写浏览器可以解析为CSS的代码,从而创建新的CSS功能,而无需等待它们在浏览器中本地实现。
尽管当前已经有了CSS变量,可以让开发者控制属性值,但是无法约束类型或者更严格的定义,CSS Houdini新的API,我们可以扩展css的变量,我们可以定义CSS变量的类型,初始值,继承。它是css变量更强大灵活。
CSS变量现状:
.dom {
--my-color: green;
--my-color: url('not-a-color'); // 它并不知道当前的变量类型
color: var(--my-color);
}
Houdini提供了两种自定义属性的注册方式,分别是在js和css中。
CSS.registerProperty({
name: '--my-prop', // String 自定义属性名
syntax: '<color>', // String 如何去解析当前的属性,即属性类型,默认 *
inherits: false, // Boolean 如果是true,子节点将会继承
initialValue: '#c0ffee', // String 属性点初始值
});
我们还可以在css中注册,也可以达到上面的效果
@property --my-prop {
syntax: '<color>';
inherits: false;
initial-value: #c0ffee;
}
这个API中最令人振奋人心的功能是自定义属性上添加动画,像这样:transition: --multiplier 0.4s;,这个功能我们在前面介绍什么是js in css那个demo用使用过。我们还可以使用+使syntax属性支持一个或多个类型,也可以使用|来分割。更多syntax属性值:
属性值 | 描述 |
<length> | 长度值 |
<number> | 数字 |
<percentage> | 百分比 |
<length-percentage> | 长度或百分比,calc将长度和百分比组成的表达式 |
<color> | 颜色 |
<image> | 图像 |
<url> | 网址 |
<integer> | 整数 |
<angle> | 角度 |
<time> | 时间 |
<resolution> | 分辨率 |
<transform-list> | 转换函数 |
<custom-ident> | ident |
Worklets是渲染引擎的扩展,从概念上来讲它类似于Web Workers,但有几个重要的区别:
Worklet是一个JavaScript模块,通过调用worklet的addModule方法(它是个Promise)来添加。比如registerLayout, registerPaint, registerAnimator 我们都需要放在Worklet中
//加载单个
await demoWorklet.addModule('path/to/script.js');
// 一次性加载多个worklet
Promise.all([
demoWorklet1.addModule('script1.js'),
demoWorklet2.addModule('script2.js'),
]).then(results => {});
registerDemoWorklet('name', class {
// 每个Worklet可以定义要使用的不同函数
// 他们将由渲染引擎在需要时调用
process(arg) {
return !arg;
}
});
Worklets的生命周期
Typed OM是对现有的CSSOM的扩展,并实现 Parsing API 和 Properties & Values API相关的特性。它将css值转化为有意义类型的JavaScript的对象,而不是像现在的字符串。如果我们尝试将字符串类型的值转化为有意义的类型并返回可能会有很大的性能开销,因此这个API可以让我们更高效的使用CSS的值。
现在读取CSS值增加了新的基类CSSStyleValue,他有许多的子类可以更加精准的描述css值的类型:
子类 | 描述 |
CSSKeywordValue | CSS关键字和其他标识符(如inherit或grid) |
CSSPositionValue | 位置信息 (x,y) |
CSSImageValue | 表示图像的值属性的对象 |
CSSUnitValue | 表示为具有单个单位的单个值(例如50px),也可以表示为没有单位的单个值或百分比 |
CSSMathValue | 比较复杂的数值,比如有calc,min和max。这包括子类 CSSMathSum, CSSMathProduct, CSSMathMin, CSSMathMax, CSSMathNegate 和 CSSMathInvert |
CSSTransformValue | 由CSS transforms组成的CSSTransformComponent列表,其中包括CSSTranslate, CSSRotate, CSSScale, CSSSkew, CSSSkewX, CSSSkewY, CSSPerspective 和 CSSMatrixComponent |
使用Typed OM主要有两种方法:
使用attributeStyleMap设置并获取
myElement.attributeStyleMap.set('font-size', CSS.em(2));
myElement.attributeStyleMap.get('font-size'); // CSSUnitValue { value: 2, unit: 'em' }
myElement.attributeStyleMap.set('opacity', CSS.number(.5));
myElement.attributeStyleMap.get('opacity'); // CSSUnitValue { value: 0.5, unit: 'number' };
在线demo
使用computedStyleMap
.foo {
transform: translateX(1em) rotate(50deg) skewX(10deg);
vertical-align: baseline;
width: calc(100% - 3em);
}
const cs = document.querySelector('.foo').computedStyleMap();
cs.get('vertical-align');
// CSSKeywordValue {
// value: 'baseline',
// }
cs.get('width');
// CSSMathSum {
// operator: 'sum',
// length: 2,
// values: CSSNumericArray {
// 0: CSSUnitValue { value: -90, unit: 'px' },
// 1: CSSUnitValue { value: 100, unit: 'percent' },
// },
// }
cs.get('transform');
// CSSTransformValue {
// is2d: true,
// length: 3,
// 0: CSSTranslate {
// is2d: true,
// x: CSSUnitValue { value: 20, unit: 'px' },
// y: CSSUnitValue { value: 0, unit: 'px' },
// z: CSSUnitValue { value: 0, unit: 'px' },
// },
// 1: CSSRotate {...},
// 2: CSSSkewX {...},
// }
复制代码
在线demo
开发者可以通过这个API实现自己的布局算法,我们可以像原生css一样使用我们自定义的布局(像display:flex, display:table)。在 Masonry layout library 上我们可以看到开发者们是有多想实现各种各样的复杂布局,其中一些布局光靠 CSS 是不行的。虽然这些布局会让人耳目一新印象深刻,但是它们的页面性能往往都很差,在一些低端设备上性能问题犹为明显。
CSS Layout API 暴露了一个registerLayout方法给开发者,接收一个布局名(layout name)作为后面在 CSS中使用的属性值,还有一个包含有这个布局逻辑的JavaScript类。
my-div {
display: layout(my-layout);
}
// layout-worklet.js
registerLayout('my-layout', class {
static get inputProperties() { return ['--foo']; }
static get childrenInputProperties() { return ['--bar']; }
async intrinsicSizes(children, edges, styleMap) {}
async layout(children, edges, constraints, styleMap) {}
});
await CSS.layoutWorklet.addModule('layout-worklet.js');
在线demo 目前浏览器大部分还不支持
我们可以在CSS background-image中使用它,我们可以使用Canvas 2d上下文,根据元素的大小控制图像,还可以使用自定义属性。
await CSS.paintWorklet.addModule('paint-worklet.js');
registerPaint('sample-paint', class {
static get inputProperties() { return ['--foo']; }
static get inputArguments() { return ['<color>']; }
static get contextOptions() { return {alpha: true}; }
paint(ctx, size, props, args) { }
});
在线demo
这个API让我们可以控制基于用户输入的关键帧动画,并且以非阻塞的方式。还能更改一个 DOM 元素的属性,不过是不会引起渲染引擎重新计算布局或者样式的属性,比如 transform、opacity 或者滚动条位置(scroll offset)。Animation API的使用方式与 Paint API 和 Layout API略有不同我们还需要通过new一个WorkletAnimation来注册worklet。
// animation-worklet.js
registerAnimator('sample-animator', class {
constructor(options) {
}
animate(currentTime, effect) {
effect.localTime = currentTime;
}
});
await CSS.animationWorklet.addModule('animation-worklet.js');
// 需要添加动画的元素
const elem = document.querySelector('#my-elem');
const scrollSource = document.scrollingElement;
const timeRange = 1000;
const scrollTimeline = new ScrollTimeline({
scrollSource,
timeRange,
});
const effectKeyframes = new KeyframeEffect(
elem,
// 动画需要绑定的关键帧
[
{transform: 'scale(1)'},
{transform: 'scale(.25)'},
{transform: 'scale(1)'}
],
{
duration: timeRange,
},
);
new WorkletAnimation(
'sample-animator',
effectKeyframes,
scrollTimeline,
{},
).play();
关于此API的更多内容点击这里
允许开发者自由扩展 CSS 词法分析器。
解析规则:
const background = window.cssParse.rule("background: green");
console.log(background.styleMap.get("background").value) // "green"
const styles = window.cssParse.ruleSet(".foo { background: green; margin: 5px; }");
console.log(styles.length) // 5
console.log(styles[0].styleMap.get("margin-top").value) // 5
console.log(styles[0].styleMap.get("margin-top").type) // "px"
解析CSS:
const style = fetch("style.css")
.then(response => CSS.parseStylesheet(response.body));
style.then(console.log);
它将提供一些方法来测量在屏幕上呈现的文本元素的尺寸,将允许开发者控制文本元素在屏幕上呈现的方式。使用当前功能很难或无法测量这些值,因此该API将使开发者可以更轻松地创建与文本和字体相关的CSS特性。例如:
了解到这里,部分开发者可能会说:“我不需要这些花里胡哨的技术,并不能带收益。我只想简简单单地写几个页面,做做普通的Web App,并不想试图干预浏览器的渲染过程从而实现一些实验性或炫酷的功能。”如果这样想的话,我们不妨退一步再去思考。回忆下最近做过的项目,用于实现页面效果所使用到的技术,grid布局方式在考虑兼容老版本浏览器时也不得不放弃。我们想控制浏览器渲染页面的过程并不是仅仅为了炫技,更多的是为了帮助开发者们解决以下两个问题:
几年过后再回眸,当主流浏览器完全支持Houdini的时候。我们可以在浏览器上随心所欲的使用任何CSS属性,并且他们都能完美支持。像今天的grid布局在旧版本浏览器支持的并不友好的这类问题,那时我们只需要安装对应的Polyfill就能解决类似的问题。
如在文中发现有误之处,欢迎反馈、纠正。
*请认真填写需求信息,我们会在24小时内与您取得联系。